-WCF.ACP.Language={},WCF.ACP.Language.ItemList=Class.extend({_proxy:null,_dialog:null,_notification:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsLanguageItem").each($.proxy(function(a,t){var e=$(t),i=e.data("languageItemID"),n=this;e.click(function(){n._click(i)})},this))},_click:function(a){this._proxy.setOption("data",{actionName:"prepareEdit",className:"wcf\\data\\language\\item\\LanguageItemAction",objectIDs:[a]}),this._proxy.sendRequest()},_success:function(a,t,e){a.returnValues?(this._showDialog(a.returnValues.template,a.returnValues.languageItem),this._dialog.find(".jsSubmitLanguageItem").click($.proxy(this._submit,this))):(null===this._notification&&(this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success.edit"))),this._dialog.wcfDialog("close"),this._notification.show())},_showDialog:function(a,t){null===this._dialog&&(this._dialog=$("#languageItemEdit"),this._dialog.length||(this._dialog=$('<div id="languageItemEdit" />').hide().appendTo(document.body))),this._dialog.html(a).wcfDialog({title:t}).wcfDialog("render")},_submit:function(){var a=$("#overlayLanguageItemValue").val(),t=$("#overlayLanguageCustomItemValue").val(),e=$("#overlayLanguageUseCustomValue").is(":checked")?1:0,i=$("#overlayLanguageItemID").val();this._proxy.setOption("data",{actionName:"edit",className:"wcf\\data\\language\\item\\LanguageItemAction",objectIDs:[i],parameters:{languageItemValue:a,languageCustomItemValue:t,languageUseCustomValue:e}}),this._proxy.sendRequest()}});
\ No newline at end of file
+WCF.ACP.Language={},WCF.ACP.Language.ItemList=Class.extend({_proxy:null,_dialog:null,_notification:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsLanguageItem").each($.proxy(function(e,t){var a=$(t),i=a.data("languageItemID"),n=this;a.click(function(){n._click(i)})},this))},_click:function(e){this._proxy.setOption("data",{actionName:"prepareEdit",className:"wcf\\data\\language\\item\\LanguageItemAction",objectIDs:[e]}),this._proxy.sendRequest()},_delete:function(){WCF.System.Confirmation.show(WCF.Language.get("wcf.acp.language.item.delete.confirmMessage"),function(e){"cancel"!==e&&(this._proxy.setOption("data",{actionName:"deleteCustomLanguageItems",className:"wcf\\data\\language\\item\\LanguageItemAction",objectIDs:[$("#overlayLanguageItemID").val()]}),this._proxy.sendRequest())}.bind(this))},_success:function(e,t,a){e.returnValues?(this._showDialog(e.returnValues.template,e.returnValues.languageItem),this._dialog.find(".jsSubmitLanguageItem").click($.proxy(this._submit,this)),this._dialog.find(".jsDeleteLanguageItem").click($.proxy(this._delete,this))):(null===this._notification&&(this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success.edit"))),this._dialog.wcfDialog("close"),"deleteCustomLanguageItems"===e.actionName?this._notification.show(window.location.reload.bind(window.location)):this._notification.show())},_showDialog:function(e,t){null===this._dialog&&(this._dialog=$("#languageItemEdit"),this._dialog.length||(this._dialog=$('<div id="languageItemEdit" />').hide().appendTo(document.body))),this._dialog.html(e).wcfDialog({title:t}).wcfDialog("render")},_submit:function(){var e=$("#overlayLanguageItemValue").val(),t=$("#overlayLanguageCustomItemValue").val(),a=$("#overlayLanguageUseCustomValue").is(":checked")?1:0,i=$("#overlayLanguageItemID").val();this._proxy.setOption("data",{actionName:"edit",className:"wcf\\data\\language\\item\\LanguageItemAction",objectIDs:[i],parameters:{languageItemValue:e,languageCustomItemValue:t,languageUseCustomValue:a}}),this._proxy.sendRequest()}});
\ No newline at end of file
-WCF.ACP={},WCF.ACP.Application={},WCF.ACP.Cronjob={},WCF.ACP.Cronjob.ExecutionHandler=Class.extend({_notification:null,_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsCronjobRow .jsExecuteButton").click($.proxy(this._click,this)),this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success"),"success")},_click:function(e){this._proxy.setOption("data",{actionName:"execute",className:"wcf\\data\\cronjob\\CronjobAction",objectIDs:[$(e.target).data("objectID")]}),this._proxy.sendRequest()},_success:function(e,t,a){$(".jsCronjobRow").each($.proxy(function(t,a){var i=$(a).find(".jsExecuteButton"),n=i.data("objectID");if(WCF.inArray(n,e.objectIDs))return e.returnValues[n]&&($(a).find("td.columnNextExec").html(e.returnValues[n].formatted),$(a).wcfHighlight()),this._notification.show(),!1},this))}}),WCF.ACP.Cronjob.LogList=Class.extend({_dialog:null,init:function(){$(".jsCronjobLogDelete").click(function(){WCF.System.Confirmation.show(WCF.Language.get("wcf.acp.cronjob.log.clear.confirm"),function(e){"confirm"==e&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"clearAll",className:"wcf\\data\\cronjob\\log\\CronjobLogAction"},success:function(){window.location.reload()}})})}),$(".jsCronjobError").click($.proxy(this._showError,this))},_showError:function(e){var t=$(e.currentTarget);null===this._dialog?(this._dialog=$('<div style="overflow: auto"><pre>'+t.next().html()+"</pre></div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.cronjob.log.error.details")})):(this._dialog.html("<pre>"+t.next().html()+"</pre>"),this._dialog.wcfDialog("open"))}}),WCF.ACP.Package={},WCF.ACP.Package.Installation=Class.extend({_actionName:"InstallPackage",_allowRollback:!1,_dialog:null,_dialogTitle:"",_proxy:null,_queueID:0,_shouldRender:!1,init:function(e,t,a,i){switch(this._actionName=t||"InstallPackage",this._allowRollback=!0===a,this._queueID=e,this._actionName){case"InstallPackage":this._dialogTitle="wcf.acp.package."+(i?"update":"install")+".title";break;case"UninstallPackage":this._dialogTitle="wcf.acp.package.uninstallation.title"}this._initProxy(),this._init()},_initProxy:function(){for(var e="",t=this._actionName.split(/([A-Z][a-z0-9]+)/),a=0,i=t.length;a<i;a++){var n=t[a];n.length&&(e.length&&(e+="-"),e+=n.toLowerCase())}this._proxy=new WCF.Action.Proxy({failure:$.proxy(this._failure,this),showLoadingOverlay:!1,success:$.proxy(this._success,this),url:"index.php?"+e+"/&t="+SECURITY_TOKEN})},_init:function(){$("#submitButton").click($.proxy(this.prepareInstallation,this))},_failure:function(){null!==this._dialog&&($("#packageInstallationProgress").removeAttr("value"),this._setIcon("times")),this._allowRollback&&null!==this._dialog&&this._purgeTemplateContent($.proxy(function(){var e=$('<div class="formSubmit" />').appendTo($("#packageInstallationInnerContent"));$('<button class="buttonPrimary">'+WCF.Language.get("wcf.acp.package.installation.rollback")+"</button>").appendTo(e).click($.proxy(this._rollback,this)),$("#packageInstallationInnerContentContainer").show(),this._dialog.wcfDialog("render")},this))},_rollback:function(e){this._setIcon("spinner"),e&&$(e.currentTarget).disable(),this._executeStep("rollback")},prepareInstallation:function(){document.activeElement&&document.activeElement.blur(),this._proxy.setOption("data",this._getParameters()),this._proxy.sendRequest()},_getParameters:function(){return{queueID:this._queueID,step:"prepare"}},_success:function(e,t,a){if(this._shouldRender=!1,null===this._dialog&&(this._dialog=$('<div id="package'+("UninstallPackage"===this._actionName?"Uni":"I")+'nstallationDialog" />').hide().appendTo(document.body),this._dialog.wcfDialog({closable:!1,title:WCF.Language.get(this._dialogTitle)})),this._setIcon("spinner"),"rollback"==e.step)return this._dialog.wcfDialog("close"),this._dialog.remove(),void new WCF.PeriodicalExecuter(function(t){t.stop(),(new WCF.ACP.Package.Uninstallation).start(e.packageID)},200);if(e.queueID&&(this._queueID=e.queueID),e.template&&!e.ignoreTemplate&&(this._dialog.html(e.template),this._shouldRender=!0),e.progress&&($("#packageInstallationProgress").attr("value",e.progress).text(e.progress+"%"),$("#packageInstallationProgressLabel").text(e.progress+"%")),e.currentAction&&$("#packageInstallationAction").html(e.currentAction),"success"===e.step)return this._setIcon("check"),void this._purgeTemplateContent($.proxy(function(){var t=$('<div class="formSubmit" />').appendTo($("#packageInstallationInnerContent")),a=$('<button class="buttonPrimary">'+WCF.Language.get("wcf.global.button.next")+"</button>").appendTo(t).click(function(){$(this).disable(),window.location=e.redirectLocation});$("#packageInstallationInnerContentContainer").show(),$(document).keydown(function(e){e.which===$.ui.keyCode.ENTER&&a.trigger("click")}),this._dialog.wcfDialog("render")},this));if(e.innerTemplate){var i=this;if($("#packageInstallationInnerContent").html(e.innerTemplate).find("input").keyup(function(t){t.keyCode===$.ui.keyCode.ENTER&&i._submit(e)}),e.step&&e.node){$("#packageInstallationProgress").removeAttr("value"),this._setIcon("question");var n=$('<div class="formSubmit" />').appendTo($("#packageInstallationInnerContent"));$('<button class="buttonPrimary">'+WCF.Language.get("wcf.global.button.next")+"</button>").appendTo(n).click($.proxy(function(t){$(t.currentTarget).disable(),this._submit(e)},this))}return $("#packageInstallationInnerContentContainer").show(),void this._dialog.wcfDialog("render")}this._purgeTemplateContent($.proxy(function(){this._shouldRender&&this._dialog.wcfDialog("render"),e.step&&e.node&&this._executeStep(e.step,e.node)},this))},_submit:function(e){this._setIcon("spinner");var t={};$("#packageInstallationInnerContent input").each(function(e,a){var i=$(a),n=i.attr("type");if("checkbox"!=n&&"radio"!=n||i.prop("checked")){var s=i.attr("name");s.match(/(.*)\[([^[]*)\]$/)?(s=RegExp.$1,$key=RegExp.$2,void 0===t[s]&&($key?t[s]={}:t[s]=[]),$key?t[s][$key]=i.val():t[s].push(i.val())):t[s]=i.val()}}),this._executeStep(e.step,e.node,t)},_purgeTemplateContent:function(e){$("#packageInstallationInnerContent").children().length&&($("#packageInstallationInnerContentContainer").hide(),$("#packageInstallationInnerContent").empty(),this._shouldRender=!0),e()},_executeStep:function(e,t,a){a||(a={});var i=$.extend({node:t,queueID:this._queueID,step:e},a);this._proxy.setOption("data",i),this._proxy.sendRequest()},_setIcon:function(e){this._dialog.find(".jsPackageInstallationStatus").removeClass("fa-check fa-question fa-times fa-spinner").addClass("fa-"+e)}}),WCF.ACP.Package.Installation.Cancel=Class.extend({init:function(e){$("#backButton").click(function(){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"cancelInstallation",className:"wcf\\data\\package\\installation\\queue\\PackageInstallationQueueAction",objectIDs:[e]},success:function(e){window.location=e.returnValues.url}})})}}),WCF.ACP.Package.Uninstallation=WCF.ACP.Package.Installation.extend({_elements:null,_packageID:0,_wcfPackageListURL:"",init:function(e,t){this._elements=e,this._packageID=0,this._wcfPackageListURL=t,void 0!==this._elements&&this._elements.length&&this._super(0,"UninstallPackage")},start:function(e){this._actionName="UninstallPackage",this._packageID=e,this._queueID=0,this._dialogTitle="wcf.acp.package.uninstallation.title",this._initProxy(),this.prepareInstallation()},_init:function(){this._elements.click($.proxy(this._showConfirmationDialog,this))},_showConfirmationDialog:function(e){var t=$(e.currentTarget);if(t.data("isApplication")&&this._wcfPackageListURL)return void(window.location=WCF.String.unescapeHTML(this._wcfPackageListURL.replace(/{packageID}/,t.data("objectID"))));var a=this;WCF.System.Confirmation.show(t.data("confirmMessage"),function(e){"confirm"===e&&(a._packageID=t.data("objectID"),a.prepareInstallation())},void 0,void 0,!0)},_getParameters:function(){return{packageID:this._packageID,step:"prepare"}}}),WCF.ACP.Package.Search=Class.extend({_button:null,_cache:{},_container:null,_dialog:null,_package:null,_packageDescription:null,_packageName:null,_packageSearchResultContainer:null,_packageSearchResultList:null,_pageCount:0,_pageNo:1,_proxy:null,_searchID:0,_selectedPackage:"",_selectedPackageVersion:"",init:function(){this._button=null,this._cache={},this._container=$("#packageSearch"),this._dialog=null,this._package=null,this._packageName=null,this._packageSearchResultContainer=$("#packageSearchResultContainer"),this._packageSearchResultList=$("#packageSearchResultList"),this._pageCount=0,this._pageNo=1,this._searchDescription=null,this._searchID=0,this._selectedPackage="",this._selectedPackageVersion="",this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initElements()},_initElements:function(){this._button=this._container.find(".formSubmit > button.jsButtonPackageSearch").disable().click($.proxy(this._search,this)),this._package=$("#package").keyup($.proxy(this._keyUp,this)),this._packageDescription=$("#packageDescription").keyup($.proxy(this._keyUp,this)),this._packageName=$("#packageName").keyup($.proxy(this._keyUp,this))},_keyUp:function(e){""===this._package.val()&&""===this._packageDescription.val()&&""===this._packageName.val()?this._button.disable():(this._button.enable(),13===e.which&&this._button.trigger("click"))},_search:function(){var e=this._getSearchValues();if(!this._validate(e))return!1;e.pageNo=this._pageNo,this._proxy.setOption("data",{actionName:"search",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:e}),this._proxy.sendRequest()},_getSearchValues:function(){return{package:$.trim(this._package.val()),packageDescription:$.trim(this._packageDescription.val()),packageName:$.trim(this._packageName.val())}},_validate:function(e){return""!==e.package||""!==e.packageDescription||""!==e.packageName},_success:function(e,t,a){switch(e.actionName){case"getResultList":this._insertTemplate(e.returnValues.template);break;case"prepareInstallation":if(e.returnValues.queueID){null!==this._dialog&&this._dialog.wcfDialog("close");new WCF.ACP.Package.Installation(e.returnValues.queueID,void 0,!1).prepareInstallation()}else e.returnValues.template&&(null===this._dialog?(this._dialog=$("<div>"+e.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.update.unauthorized")})):this._dialog.html(e.returnValues.template).wcfDialog("open"),this._dialog.find(".formSubmit > button").click($.proxy(this._submitAuthentication,this)));break;case"search":this._pageCount=e.returnValues.pageCount,this._searchID=e.returnValues.searchID,this._insertTemplate(e.returnValues.template,void 0===e.returnValues.count?void 0:e.returnValues.count),this._setupPagination()}},_submitAuthentication:function(e){var t=$("#packageUpdateServerUsername"),a=$("#packageUpdateServerPassword");t.next("small.innerError").remove(),a.next("small.innerError").remove();var i=!0;""===$.trim(t.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(t),i=!1),""===$.trim(a.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(a),i=!1),i&&this._prepareInstallation($(e.currentTarget).data("packageUpdateServerID"))},_insertTemplate:function(e,t){this._packageSearchResultContainer.show(),this._packageSearchResultList.html(e),void 0===t&&(this._content[this._pageNo]=e),void 0!==t&&(this._content={1:e},this._packageSearchResultContainer.find("h2.sectionTitle > .badge").html(t)),this._packageSearchResultList.find(".jsInstallPackage").click($.proxy(this._click,this))},_click:function(e){var t=$(e.currentTarget);WCF.System.Confirmation.show(t.data("confirmMessage"),$.proxy(function(e){"confirm"===e&&(this._selectedPackage=t.data("package"),this._selectedPackageVersion=t.data("packageVersion"),this._prepareInstallation())},this),void 0,void 0,!0)},_prepareInstallation:function(e){var t={packages:{}};t.packages[this._selectedPackage]=this._selectedPackageVersion,e&&(t.authData={packageUpdateServerID:e,password:$.trim($("#packageUpdateServerPassword").val()),saveCredentials:!!$("#packageUpdateServerSaveCredentials:checked").length,username:$.trim($("#packageUpdateServerUsername").val())}),this._proxy.setOption("data",{actionName:"prepareInstallation",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:t}),this._proxy.sendRequest()},_setupPagination:function(){if(this._content={1:this._packageSearchResultList.html()},this._packageSearchResultContainer.find(".pagination").wcfPages("destroy").remove(),this._pageCount>1){$('<footer class="contentFooter"><div class="paginationBottom"><nav /></div></footer>').insertAfter(this._packageSearchResultList).find("nav").wcfPages({activePage:this._pageNo,maxPage:this._pageCount}).on("wcfpagesswitched",$.proxy(this._showPage,this))}},_showPage:function(e,t){if(t&&t.activePage&&(this._pageNo=t.activePage),this._pageNo<1||this._pageNo>this._pageCount)return void console.debug("[WCF.ACP.Package.Search] Cannot access page "+this._pageNo+" of "+this._pageCount);void 0===this._content[this._pageNo]?(this._proxy.setOption("data",{actionName:"getResultList",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:{pageNo:this._pageNo,searchID:this._searchID}}),this._proxy.sendRequest()):(this._packageSearchResultList.html(this._content[this._pageNo]),WCF.DOMNodeInsertedHandler.execute())}}),WCF.ACP.Package.Server={},WCF.ACP.Package.Server.Installation=Class.extend({_proxy:null,_selectedPackage:"",init:function(){this._dialog=null,this._selectedPackage=null,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)})},bind:function(){$(".jsButtonPackageInstall").removeClass("jsButtonPackageInstall").click($.proxy(this._click,this))},_click:function(e){var t=$(e.currentTarget);WCF.System.Confirmation.show(t.data("confirmMessage"),$.proxy(function(e){"confirm"===e&&(this._selectedPackage=t.data("package"),this._selectedPackageVersion=t.data("packageVersion"),this._prepareInstallation())},this),void 0,void 0,!0)},_success:function(e){if(e.returnValues.queueID){null!==this._dialog&&this._dialog.wcfDialog("close");new WCF.ACP.Package.Installation(e.returnValues.queueID,void 0,!1).prepareInstallation()}else e.returnValues.template&&(null===this._dialog?(this._dialog=$("<div>"+e.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.update.unauthorized")})):this._dialog.html(e.returnValues.template).wcfDialog("open"),this._dialog.find(".formSubmit > button").click($.proxy(this._submitAuthentication,this)))},_submitAuthentication:function(e){var t=$("#packageUpdateServerUsername"),a=$("#packageUpdateServerPassword");t.next("small.innerError").remove(),a.next("small.innerError").remove();var i=!0;""===$.trim(t.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(t),i=!1),""===$.trim(a.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(a),i=!1),i&&this._prepareInstallation($(e.currentTarget).data("packageUpdateServerID"))},_prepareInstallation:function(e){var t={packages:{}};t.packages[this._selectedPackage]=this._selectedPackageVersion,e&&(t.authData={packageUpdateServerID:e,password:$.trim($("#packageUpdateServerPassword").val()),saveCredentials:!!$("#packageUpdateServerSaveCredentials:checked").length,username:$.trim($("#packageUpdateServerUsername").val())}),this._proxy.setOption("data",{actionName:"prepareInstallation",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:t}),this._proxy.sendRequest()}}),WCF.ACP.Package.Update={},WCF.ACP.Package.Update.Manager=Class.extend({_dialog:null,_proxy:null,_submitButton:null,init:function(){this._dialog=null,this._submitButton=$(".formSubmit > button").click($.proxy(this._click,this)),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsPackageUpdate").each($.proxy(function(e,t){var a=$(t);a.find("input[type=checkbox]").data("packageUpdate",a).change($.proxy(this._change,this))},this))},_change:function(e){var t=$(e.currentTarget);t.is(":checked")?(t.data("packageUpdate").find("select").enable(),t.data("packageUpdate").find("dl").removeClass("disabled"),this._submitButton.enable()):(t.data("packageUpdate").find("select").disable(),t.data("packageUpdate").find("dl").addClass("disabled"),$("input[type=checkbox]:checked").length?this._submitButton.enable():this._submitButton.disable())},_click:function(e,t){var a={};if($(".jsPackageUpdate").each($.proxy(function(e,t){var i=$(t);i.find("input[type=checkbox]:checked").length&&(a[i.data("package")]=i.find("select").val())},this)),$.getLength(a)){this._submitButton.disable();var i={packages:a};t&&(i.authData={packageUpdateServerID:t,password:$.trim($("#packageUpdateServerPassword").val()),saveCredentials:!!$("#packageUpdateServerSaveCredentials:checked").length,username:$.trim($("#packageUpdateServerUsername").val())}),this._proxy.setOption("data",{actionName:"prepareUpdate",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:i}),this._proxy.sendRequest()}},_success:function(e,t,a){if(e.returnValues.queueID){null!==this._dialog&&this._dialog.wcfDialog("close");new WCF.ACP.Package.Installation(e.returnValues.queueID,void 0,!1,!0).prepareInstallation()}else e.returnValues.excludedPackages?(null===this._dialog?(this._dialog=$("<div>"+e.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.update.excludedPackages")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.acp.package.update.excludedPackages")),this._dialog.html(e.returnValues.template).wcfDialog("open")),this._submitButton.enable()):e.returnValues.template&&(null===this._dialog?(this._dialog=$("<div>"+e.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.update.unauthorized")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.acp.package.update.unauthorized")),this._dialog.html(e.returnValues.template).wcfDialog("open")),this._dialog.find(".formSubmit > button").click($.proxy(this._submitAuthentication,this)))},_submitAuthentication:function(e){var t=$("#packageUpdateServerUsername"),a=$("#packageUpdateServerPassword");t.next("small.innerError").remove(),a.next("small.innerError").remove();var i=!0;""===$.trim(t.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(t),i=!1),""===$.trim(a.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(a),i=!1),i&&this._click(void 0,$(e.currentTarget).data("packageUpdateServerID"))}}),WCF.ACP.Package.Update.Search=Class.extend({_dialog:null,init:function(e){if(this._dialog=null,!0===e)$(".jsButtonPackageUpdate").click($.proxy(this._click,this));else{$('<li><a class="button"><span class="icon icon16 fa-refresh"></span> <span>'+WCF.Language.get("wcf.acp.package.searchForUpdates")+"</span></a></li>").click(this._click.bind(this)).prependTo($(".contentHeaderNavigation > ul"))}},_click:function(){null===this._dialog?new WCF.Action.Proxy({autoSend:!0,data:{actionName:"searchForUpdates",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:{ignoreCache:1}},success:$.proxy(this._success,this)}):this._dialog.wcfDialog("open")},_success:function(e,t,a){e.returnValues.url?window.location=e.returnValues.url:(this._dialog=$("<div>"+WCF.Language.get("wcf.acp.package.searchForUpdates.noResults")+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.searchForUpdates")}))}}),WCF.ACP.PluginStore={},WCF.ACP.PluginStore.PurchasedItems={},WCF.ACP.PluginStore.PurchasedItems.Search=Class.extend({_dialog:null,_proxy:null,init:function(){this._dialog=null,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$('<li><a class="button"><span class="icon icon16 fa-shopping-cart" /> <span>'+WCF.Language.get("wcf.acp.pluginStore.purchasedItems.button.search")+"</span></a></li>").prependTo($(".contentHeaderNavigation > ul")).click($.proxy(this._click,this))},_click:function(){this._proxy.setOption("data",{actionName:"searchForPurchasedItems",className:"wcf\\data\\package\\PackageAction"}),this._proxy.sendRequest()},_success:function(e,t,a){if(e.returnValues.template){null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.html(e.returnValues.template).wcfDialog({title:WCF.Language.get("wcf.acp.pluginStore.authorization")})):(this._dialog.html(e.returnValues.template),this._dialog.wcfDialog("open"));var i=this._dialog.find("button").click($.proxy(this._submit,this));this._dialog.find("input").keyup(function(e){if(e.which==$.ui.keyCode.ENTER)return i.trigger("click"),!1})}else e.returnValues.noResults?null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.html(e.returnValues.noResults).wcfDialog({title:WCF.Language.get("wcf.acp.pluginStore.purchasedItems")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.acp.pluginStore.purchasedItems")),this._dialog.html(e.returnValues.noResults),this._dialog.wcfDialog("open")):e.returnValues.noSSL?null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.html(e.returnValues.noSSL).wcfDialog({title:WCF.Language.get("wcf.global.error.title")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.global.error.title")),this._dialog.html(e.returnValues.noSSL),this._dialog.wcfDialog("open")):e.returnValues.redirectURL&&(window.location=e.returnValues.redirectURL)},_submit:function(){this._dialog.wcfDialog("close"),this._proxy.setOption("data",{actionName:"searchForPurchasedItems",className:"wcf\\data\\package\\PackageAction",parameters:{password:$("#pluginStorePassword").val(),username:$("#pluginStoreUsername").val()}}),this._proxy.sendRequest()}}),WCF.ACP.Worker=Class.extend({init:function(e,t,a,i,n){if("function"==typeof n)throw new Error("The callback parameter is no longer supported, please migrate to 'WoltLabSuite/Core/Acp/Ui/Worker'.");require(["WoltLabSuite/Core/Acp/Ui/Worker"],function(n){new n({dialogId:e,dialogTitle:a,className:t,parameters:i})})}}),WCF.ACP.Category={},WCF.ACP.Category.Collapsible=WCF.Collapsible.SimpleRemote.extend({init:function(e){var t=$('.formSubmit > button[data-type="submit"]');t&&t.click($.proxy(this._sort,this)),this._super(e)},_getButtonContainer:function(e){return $("#"+e+" > .buttons")},_getContainers:function(){return $(".jsCategory").has("ol").has("li")},_getTarget:function(e){return $("#"+e+" > ol")},_sort:function(){$(".collapsibleButton").remove(),this._containers={},this._containerData={};var e=this._getContainers();0==e.length&&console.debug("[WCF.ACP.Category.Collapsible] Empty container set given, aborting."),e.each($.proxy(function(e,t){var a=$(t),i=a.wcfIdentify();this._containers[i]=a,this._initContainer(i)},this))}}),WCF.ACP.Search=WCF.Search.Base.extend({_providerName:"",init:function(){this._className="wcf\\data\\acp\\search\\provider\\ACPSearchProviderAction",this._super("#pageHeaderSearch input[name=q]"),$("#pageHeaderSearch > form").on("submit",function(e){e.preventDefault()}),WCF.Dropdown.getDropdownMenu("pageHeaderSearchType").find("a[data-provider-name]").on("click",$.proxy(function(e){e.preventDefault();var t=$(e.target);$(".pageHeaderSearchType > .button").text(t.text());var a=this._providerName;if(this._providerName="everywhere"!=t.data("providerName")?t.data("providerName"):"",a!=this._providerName){var i=$.trim(this._searchInput.val());if(i){var n={data:{excludedSearchValues:this._excludedSearchValues,searchString:i}};this._queryServer(n)}}},this))},_createListItem:function(e){this._list.children("li").length>0&&$('<li class="dropdownDivider" />').appendTo(this._list),$('<li class="dropdownText">'+e.title+"</li>").appendTo(this._list);for(var t in e.items){var a=e.items[t];$('<li><a href="'+a.link+'"><span>'+WCF.String.escapeHTML(a.title)+"</span>"+(a.subtitle?"<small>"+WCF.String.escapeHTML(a.subtitle)+"</small>":"")+"</a></li>").appendTo(this._list),this._itemCount++}},_openDropdown:function(){this._list.find("small").each(function(e,t){for(;t.scrollWidth>t.clientWidth;)t.innerText="… "+t.innerText.substr(3)})},_handleEmptyResult:function(){return $('<li class="dropdownText">'+WCF.Language.get("wcf.acp.search.noResults")+"</li>").appendTo(this._list),!0},_highlightSelectedElement:function(){this._list.find("li").removeClass("dropdownNavigationItem"),this._list.find("li:not(.dropdownDivider):not(.dropdownText)").eq(this._itemIndex).addClass("dropdownNavigationItem")},_selectElement:function(e){if(-1===this._itemIndex)return!1;window.location=this._list.find("li.dropdownNavigationItem > a").attr("href")},_success:function(e){this._super(e),this._list.addClass("acpSearchDropdown")},_getParameters:function(e){return e.data.providerName=this._providerName,e}}),WCF.ACP.User={},WCF.ACP.User.BanHandler={_callback:null,_dialog:null,_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsBanButton").click($.proxy(function(e){var t=$(e.currentTarget);t.data("banned")?this.unban([t.data("objectID")]):this.ban([t.data("objectID")])},this)),require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.user",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.user.ban"===e.data.actionName&&this.ban(e.data.parameters.objectIDs)},unban:function(e){this._proxy.setOption("data",{actionName:"unban",className:"wcf\\data\\user\\UserAction",objectIDs:e}),this._proxy.sendRequest()},ban:function(e){null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.append($('<div class="section"><dl><dt><label for="userBanReason">'+WCF.Language.get("wcf.acp.user.banReason")+'</label></dt><dd><textarea id="userBanReason" cols="40" rows="3" /><small>'+WCF.Language.get("wcf.acp.user.banReason.description")+'</small></dd></dl><dl><dt></dt><dd><label for="userBanNeverExpires"><input type="checkbox" name="userBanNeverExpires" id="userBanNeverExpires" checked> '+WCF.Language.get("wcf.acp.user.ban.neverExpires")+'</label></dd></dl><dl id="userBanExpiresSettings" style="display: none;"><dt><label for="userBanExpires">'+WCF.Language.get("wcf.acp.user.ban.expires")+'</label></dt><dd><input type="date" name="userBanExpires" id="userBanExpires" class="medium" min="'+new Date(1e3*TIME_NOW).toISOString()+'" data-ignore-timezone="true" /><small>'+WCF.Language.get("wcf.acp.user.ban.expires.description")+"</small></dd></dl></div>")),this._dialog.append($('<div class="formSubmit"><button class="buttonPrimary" accesskey="s">'+WCF.Language.get("wcf.global.button.submit")+"</button></div>")),this._dialog.find("#userBanNeverExpires").change(function(){$("#userBanExpiresSettings").toggle()}),this._dialog.find("button").click($.proxy(this._submit,this))):($("#userBanReason").val(""),$("#userBanNeverExpires").prop("checked",!0),$("#userBanExpiresSettings").hide(),$("#userBanExpiresDatePicker, #userBanExpires").val("")),this._dialog.data("userIDs",e),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.user.ban.sure")})},_submit:function(){this._dialog.find(".innerError").remove();var e="";if(!$("#userBanNeverExpires").is(":checked")){var e=$("#userBanExpiresDatePicker").val();if(!e)return void this._dialog.find("#userBanExpiresSettings > dd > small").prepend($('<small class="innerError" />').text(WCF.Language.get("wcf.global.form.error.empty")))}this._proxy.setOption("data",{actionName:"ban",className:"wcf\\data\\user\\UserAction",objectIDs:this._dialog.data("userIDs"),parameters:{banReason:$("#userBanReason").val(),banExpires:e}}),this._proxy.sendRequest()},_success:function(e,t,a){$(".jsBanButton").each(function(t,a){var i=$(a);WCF.inArray(i.data("objectID"),e.objectIDs)&&("unban"==e.actionName?i.data("banned",!1).attr("data-tooltip",i.data("banMessage")).removeClass("fa-lock").addClass("fa-unlock"):i.data("banned",!0).attr("data-tooltip",i.data("unbanMessage")).removeClass("fa-unlock").addClass("fa-lock"))}),(new WCF.System.Notification).show(),WCF.Clipboard.reload(),"ban"==e.actionName&&this._dialog.wcfDialog("close")}},WCF.ACP.User.Group={},WCF.ACP.User.Group.Copy=Class.extend({_groupID:0,init:function(e){this._groupID=e,$(".jsButtonUserGroupCopy").click($.proxy(this._click,this))},_click:function(){var e=$('<div class="section" />');e.append($('<dl class="wide"><dt /><dd><label><input type="checkbox" id="copyMembers" value="1" /> '+WCF.Language.get("wcf.acp.group.copy.copyMembers")+"</label><small>"+WCF.Language.get("wcf.acp.group.copy.copyMembers.description")+"</small></dd></dl>")),e.append($('<dl class="wide"><dt /><dd><label><input type="checkbox" id="copyUserGroupOptions" value="1" /> '+WCF.Language.get("wcf.acp.group.copy.copyUserGroupOptions")+"</label><small>"+WCF.Language.get("wcf.acp.group.copy.copyUserGroupOptions.description")+"</small></dd></dl>")),e.append($('<dl class="wide"><dt /><dd><label><input type="checkbox" id="copyACLOptions" value="1" /> '+WCF.Language.get("wcf.acp.group.copy.copyACLOptions")+"</label><small>"+WCF.Language.get("wcf.acp.group.copy.copyACLOptions.description")+"</small></dd></dl>")),WCF.System.Confirmation.show(WCF.Language.get("wcf.acp.group.copy.confirmMessage"),$.proxy(function(e){"confirm"===e&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"copy",className:"wcf\\data\\user\\group\\UserGroupAction",objectIDs:[this._groupID],parameters:{copyACLOptions:$("#copyACLOptions").is(":checked"),copyMembers:$("#copyMembers").is(":checked"),copyUserGroupOptions:$("#copyUserGroupOptions").is(":checked")}},success:function(e){window.location=e.returnValues.redirectURL}})},this),"",e,!0)}}),WCF.ACP.User.EnableHandler={_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsEnableButton").click($.proxy(function(e){var t=$(e.currentTarget);t.data("enabled")?this.disable([t.data("objectID")]):this.enable([t.data("objectID")])},this)),require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.user",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.user.enable"===e.data.actionName&&this.enable(e.data.parameters.objectIDs)},disable:function(e){this._proxy.setOption("data",{actionName:"disable",className:"wcf\\data\\user\\UserAction",objectIDs:e}),this._proxy.sendRequest()},enable:function(e){this._proxy.setOption("data",{actionName:"enable",className:"wcf\\data\\user\\UserAction",objectIDs:e}),this._proxy.sendRequest()},_success:function(e,t,a){$(".jsEnableButton").each(function(t,a){var i=$(a);WCF.inArray(i.data("objectID"),e.objectIDs)&&("disable"==e.actionName?i.data("enabled",!1).attr("data-tooltip",i.data("enableMessage")).removeClass("fa-check-square-o").addClass("fa-square-o"):i.data("enabled",!0).attr("data-tooltip",i.data("disableMessage")).removeClass("fa-square-o").addClass("fa-check-square-o"))}),(new WCF.System.Notification).show(function(){window.location.reload()})}},WCF.ACP.User.SendNewPasswordHandler={init:function(){require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.user",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.user.sendNewPassword"===e.data.actionName&&WCF.System.Confirmation.show(e.data.parameters.confirmMessage,function(t){"confirm"===t&&new WCF.ACP.Worker("sendingNewPasswords","wcf\\system\\worker\\SendNewPasswordWorker",WCF.Language.get("wcf.acp.user.sendNewPassword.workerTitle"),{userIDs:e.data.parameters.objectIDs})})}},WCF.ACP.Import={},
-WCF.ACP.Import.Manager=Class.extend({_currentAction:"",_dialog:null,_index:-1,_objectTypes:[],_proxy:null,_redirectURL:"",init:function(e,t){this._currentAction="",this._index=-1,this._objectTypes=e,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this),url:"index.php?worker-proxy/&t="+SECURITY_TOKEN}),this._redirectURL=t,this._invoke()},_invoke:function(){if(++this._index>=this._objectTypes.length){this._dialog.find(".fa-spinner").removeClass("fa-spinner").addClass("fa-check"),this._dialog.find("h1").text(WCF.Language.get("wcf.acp.dataImport.completed"));var e=$('<div class="formSubmit" />').appendTo(this._dialog.find("#workerContainer"));$("<button>"+WCF.Language.get("wcf.global.button.next")+"</button>").click($.proxy(function(){new WCF.Action.Proxy({autoSend:!0,data:{noRedirect:1},dataType:"html",success:$.proxy(function(){window.location=this._redirectURL},this),url:"index.php?cache-clear/&t="+SECURITY_TOKEN})},this)).appendTo(e),this._dialog.wcfDialog("render")}else this._run(WCF.Language.get("wcf.acp.dataImport.data."+this._objectTypes[this._index]),this._objectTypes[this._index])},_run:function(e,t){this._currentAction=e,this._proxy.setOption("data",{className:"wcf\\system\\worker\\ImportWorker",parameters:{objectType:t}}),this._proxy.sendRequest()},_success:function(e){null===this._dialog&&(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.wcfDialog({closable:!1,title:WCF.Language.get("wcf.acp.dataImport")})),e.template&&this._dialog.html(e.template),this._currentAction&&this._dialog.find("h1").text(this._currentAction),this._dialog.find("progress").attr("value",e.progress).text(e.progress+"%").next("span").text(e.progress+"%"),e.progress<100?(this._proxy.setOption("data",{className:e.className,loopCount:e.loopCount,parameters:e.parameters}),this._proxy.sendRequest()):this._invoke()}}),WCF.ACP.Stat={},WCF.ACP.Stat.Chart=Class.extend({init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$("#statRefreshButton").click($.proxy(this._refresh,this)),this._refresh()},_refresh:function(){var e=[];$("input[name=objectTypeID]:checked").each(function(){e.push($(this).val())}),e.length&&(this._proxy.setOption("data",{className:"wcf\\data\\stat\\daily\\StatDailyAction",actionName:"getData",parameters:{startDate:$("#startDateDatePicker").val(),endDate:$("#endDateDatePicker").val(),value:$("input[name=value]:checked").val(),dateGrouping:$("input[name=dateGrouping]:checked").val(),objectTypeIDs:e}}),this._proxy.sendRequest())},_success:function(e){switch($("input[name=dateGrouping]:checked").val()){case"yearly":var t=[1,"year"],a=WCF.Language.get("wcf.acp.stat.timeFormat.yearly");break;case"monthly":var t=[1,"month"],a=WCF.Language.get("wcf.acp.stat.timeFormat.monthly");break;case"weekly":var t=[7,"day"],a=WCF.Language.get("wcf.acp.stat.timeFormat.weekly");break;default:var t=[1,"day"],a=WCF.Language.get("wcf.acp.stat.timeFormat.daily")}var i={series:{lines:{show:!0},points:{show:!0}},grid:{hoverable:!0},xaxis:{mode:"time",minTickSize:t,timeformat:a,monthNames:WCF.Language.get("__monthsShort")},yaxis:{min:0,tickDecimals:0,tickFormatter:function(e){return WCF.String.addThousandsSeparator(e)}}},n=[];for(var s in e.returnValues){for(var o=e.returnValues[s],r=0;r<o.data.length;r++)o.data[r][0]*=1e3;n.push(o)}$.plot("#chart",n,i),require(["Ui/Alignment"],function(e){var t=elCreate("span");t.style.setProperty("position","absolute",""),document.body.appendChild(t),$("#chart").on("plothover",function(a,i,n){n?(t.style.setProperty("top",n.pageY+"px",""),t.style.setProperty("left",n.pageX+"px",""),$("#chartTooltip").html(n.series.xaxis.tickFormatter(n.datapoint[0],n.series.xaxis)+", "+WCF.String.formatNumeric(n.datapoint[1])+" "+n.series.label).show(),e.set($("#chartTooltip")[0],t,{verticalOffset:5,horizontal:"center"})):$("#chartTooltip").hide()})}),n.length||$("#chart").append('<p style="position: absolute; font-size: 1.2rem; text-align: center; top: 50%; margin-top: -20px; width: 100%">'+WCF.Language.get("wcf.acp.stat.noData")+"</p>"),elBySel(".contentHeader > .contentTitle").scrollIntoView({behavior:"smooth"})}}),WCF.ACP.Ad={},WCF.ACP.Ad.LocationHandler=Class.extend({_pageConditions:null,_pageInputs:[],init:function(){this._pageConditions=$("#pageConditions"),this._pageInputs=$('input[name="pageIDs[]"]');var e=$(this._pageInputs[0]).parents("dl:eq(0)");e.hide();var t=e.parent("section");t.children("dl:visible").length||t.hide();var a=t.next("section");if(a){var i=a.css("margin-top");a.css("margin-top",0),require(["EventHandler"],function(e){e.add("com.woltlab.wcf.pageConditionDependence","checkVisivility",function(){t.is(":visible")?a.css("margin-top",i):a.css("margin-top",0)})})}e.next("dl").css("margin-top",0),$("#objectTypeID").on("change",$.proxy(this._setPageController,this)),this._setPageController(),$("#adForm").submit($.proxy(this._submit,this))},_setPageController:function(){var e=$("#objectTypeID").find("option:checked");require(["Core"],function(t){for(var a,i,n=0,s=this._pageInputs.length;n<s;n++)a=this._pageInputs[n],i=!1,e.data("page")&&elData(a,"identifier")===e.data("page")?(a.checked||(i=!0),a.checked=!0):(a.checked&&(i=!0),a.checked=!1),i&&t.triggerEvent(this._pageInputs[n],"change")}.bind(this))},_submit:function(){if(this._pageConditions.is(":hidden"))this._pageConditions.find("select, input").remove();else for(var e=0,t=this._pageInputs.length;e<t;e++)this._pageInputs[e].checked=!1}}),WCF.ACP.Tag={},WCF.ACP.Tag.SetAsSynonymsHandler=Class.extend({_dialog:null,_objectIDs:[],init:function(){require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.tag",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.tag.setAsSynonyms"===e.data.actionName&&(this._objectIDs=e.data.parameters.objectIDs,null===this._dialog&&(this._dialog=$('<div id="setAsSynonymsDialog" />').hide().appendTo(document.body),this._dialog.wcfDialog({closable:!1,title:WCF.Language.get("wcf.acp.tag.setAsSynonyms")})),this._dialog.html(e.data.parameters.template),$button=this._dialog.find('button[data-type="submit"]').disable().click($.proxy(this._submit,this)),this._dialog.find("input[type=radio]").change(function(){$button.enable()}))},_submit:function(){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"setAsSynonyms",className:"wcf\\data\\tag\\TagAction",objectIDs:this._objectIDs,parameters:{tagID:this._dialog.find('input[name="tagID"]:checked').val()}},success:$.proxy(function(){this._dialog.wcfDialog("close"),(new WCF.System.Notification).show(function(){window.location.reload()})},this)})}});
\ No newline at end of file
+WCF.ACP={},WCF.ACP.Application={},WCF.ACP.Cronjob={},WCF.ACP.Cronjob.ExecutionHandler=Class.extend({_notification:null,_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsCronjobRow .jsExecuteButton").click($.proxy(this._click,this)),this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success"),"success")},_click:function(e){this._proxy.setOption("data",{actionName:"execute",className:"wcf\\data\\cronjob\\CronjobAction",objectIDs:[$(e.target).data("objectID")]}),this._proxy.sendRequest()},_success:function(e,t,a){$(".jsCronjobRow").each($.proxy(function(t,a){var i=$(a).find(".jsExecuteButton"),n=i.data("objectID");if(WCF.inArray(n,e.objectIDs))return e.returnValues[n]&&($(a).find("td.columnNextExec").html(e.returnValues[n].formatted),$(a).wcfHighlight()),this._notification.show(),!1},this))}}),WCF.ACP.Cronjob.LogList=Class.extend({_dialog:null,init:function(){$(".jsCronjobLogDelete").click(function(){WCF.System.Confirmation.show(WCF.Language.get("wcf.acp.cronjob.log.clear.confirm"),function(e){"confirm"==e&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"clearAll",className:"wcf\\data\\cronjob\\log\\CronjobLogAction"},success:function(){window.location.reload()}})})}),$(".jsCronjobError").click($.proxy(this._showError,this))},_showError:function(e){var t=$(e.currentTarget);null===this._dialog?(this._dialog=$('<div style="overflow: auto"><pre>'+t.next().html()+"</pre></div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.cronjob.log.error.details")})):(this._dialog.html("<pre>"+t.next().html()+"</pre>"),this._dialog.wcfDialog("open"))}}),WCF.ACP.Package={},WCF.ACP.Package.Installation=Class.extend({_actionName:"InstallPackage",_additionalRequestParameters:{},_allowRollback:!1,_dialog:null,_dialogTitle:"",_proxy:null,_queueID:0,_shouldRender:!1,init:function(e,t,a,i,n){this._actionName=t||"InstallPackage",this._allowRollback=!0===a,this._queueID=e,this._additionalRequestParameters=n||{},this._dialogTitle="wcf.acp.package."+(i?"update":"install")+".title","UninstallPackage"===this._actionName&&(this._dialogTitle="wcf.acp.package.uninstallation.title"),this._initProxy(),this._init()},_initProxy:function(){for(var e="",t=this._actionName.split(/([A-Z][a-z0-9]+)/),a=0,i=t.length;a<i;a++){var n=t[a];n.length&&(e.length&&(e+="-"),e+=n.toLowerCase())}this._proxy=new WCF.Action.Proxy({failure:$.proxy(this._failure,this),showLoadingOverlay:!1,success:$.proxy(this._success,this),url:"index.php?"+e+"/&t="+SECURITY_TOKEN})},_init:function(){$("#submitButton").click($.proxy(this.prepareInstallation,this))},_failure:function(){null!==this._dialog&&($("#packageInstallationProgress").removeAttr("value"),this._setIcon("times")),this._allowRollback&&null!==this._dialog&&this._purgeTemplateContent($.proxy(function(){var e=$('<div class="formSubmit" />').appendTo($("#packageInstallationInnerContent"));$('<button class="buttonPrimary">'+WCF.Language.get("wcf.acp.package.installation.rollback")+"</button>").appendTo(e).click($.proxy(this._rollback,this)),$("#packageInstallationInnerContentContainer").show(),this._dialog.wcfDialog("render")},this))},_rollback:function(e){this._setIcon("spinner"),e&&$(e.currentTarget).disable(),this._executeStep("rollback")},prepareInstallation:function(){document.activeElement&&document.activeElement.blur(),this._proxy.setOption("data",this._getParameters()),this._proxy.sendRequest()},_getParameters:function(){return $.extend({},this._additionalRequestParameters,{queueID:this._queueID,step:"prepare"})},_success:function(e,t,a){if(this._shouldRender=!1,null===this._dialog&&(this._dialog=$('<div id="package'+("UninstallPackage"===this._actionName?"Uni":"I")+'nstallationDialog" />').hide().appendTo(document.body),this._dialog.wcfDialog({closable:!1,title:WCF.Language.get(this._dialogTitle)})),this._setIcon("spinner"),"rollback"==e.step)return this._dialog.wcfDialog("close"),this._dialog.remove(),void new WCF.PeriodicalExecuter(function(t){t.stop(),(new WCF.ACP.Package.Uninstallation).start(e.packageID)},200);if(e.queueID&&(this._queueID=e.queueID),e.template&&!e.ignoreTemplate&&(this._dialog.html(e.template),this._shouldRender=!0),e.progress&&($("#packageInstallationProgress").attr("value",e.progress).text(e.progress+"%"),$("#packageInstallationProgressLabel").text(e.progress+"%")),e.currentAction&&$("#packageInstallationAction").html(e.currentAction),"success"===e.step)return this._setIcon("check"),void this._purgeTemplateContent($.proxy(function(){var t=$('<div class="formSubmit" />').appendTo($("#packageInstallationInnerContent")),a=$('<button class="buttonPrimary">'+WCF.Language.get("wcf.global.button.next")+"</button>").appendTo(t).click(function(){$(this).disable(),window.location=e.redirectLocation});$("#packageInstallationInnerContentContainer").show(),$(document).keydown(function(e){e.which===$.ui.keyCode.ENTER&&a.trigger("click")}),this._dialog.wcfDialog("render")},this));if(e.innerTemplate){var i=this;if($("#packageInstallationInnerContent").html(e.innerTemplate).find("input").keyup(function(t){t.keyCode===$.ui.keyCode.ENTER&&i._submit(e)}),e.step&&e.node){$("#packageInstallationProgress").removeAttr("value"),this._setIcon("question");var n=$('<div class="formSubmit" />').appendTo($("#packageInstallationInnerContent"));$('<button class="buttonPrimary">'+WCF.Language.get("wcf.global.button.next")+"</button>").appendTo(n).click($.proxy(function(t){$(t.currentTarget).disable(),this._submit(e)},this))}return $("#packageInstallationInnerContentContainer").show(),void this._dialog.wcfDialog("render")}this._purgeTemplateContent($.proxy(function(){this._shouldRender&&this._dialog.wcfDialog("render"),e.step&&e.node&&this._executeStep(e.step,e.node)},this))},_submit:function(e){this._setIcon("spinner");var t={};$("#packageInstallationInnerContent input").each(function(e,a){var i=$(a),n=i.attr("type");if("checkbox"!=n&&"radio"!=n||i.prop("checked")){var s=i.attr("name");s.match(/(.*)\[([^[]*)\]$/)?(s=RegExp.$1,$key=RegExp.$2,void 0===t[s]&&($key?t[s]={}:t[s]=[]),$key?t[s][$key]=i.val():t[s].push(i.val())):t[s]=i.val()}}),this._executeStep(e.step,e.node,t)},_purgeTemplateContent:function(e){$("#packageInstallationInnerContent").children().length&&($("#packageInstallationInnerContentContainer").hide(),$("#packageInstallationInnerContent").empty(),this._shouldRender=!0),e()},_executeStep:function(e,t,a){a||(a={});var i=$.extend({},this._additionalRequestParameters,{node:t,queueID:this._queueID,step:e},a);this._proxy.setOption("data",i),this._proxy.sendRequest()},_setIcon:function(e){this._dialog.find(".jsPackageInstallationStatus").removeClass("fa-check fa-question fa-times fa-spinner").addClass("fa-"+e)}}),WCF.ACP.Package.Installation.Cancel=Class.extend({init:function(e){$("#backButton").click(function(){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"cancelInstallation",className:"wcf\\data\\package\\installation\\queue\\PackageInstallationQueueAction",objectIDs:[e]},success:function(e){window.location=e.returnValues.url}})})}}),WCF.ACP.Package.Uninstallation=WCF.ACP.Package.Installation.extend({_elements:null,_packageID:0,_wcfPackageListURL:"",init:function(e,t){this._elements=e,this._packageID=0,this._wcfPackageListURL=t,void 0!==this._elements&&this._elements.length&&this._super(0,"UninstallPackage")},start:function(e){this._actionName="UninstallPackage",this._packageID=e,this._queueID=0,this._dialogTitle="wcf.acp.package.uninstallation.title",this._initProxy(),this.prepareInstallation()},_init:function(){this._elements.click($.proxy(this._showConfirmationDialog,this))},_showConfirmationDialog:function(e){var t=$(e.currentTarget);if(t.data("isApplication")&&this._wcfPackageListURL)return void(window.location=WCF.String.unescapeHTML(this._wcfPackageListURL.replace(/{packageID}/,t.data("objectID"))));var a=this;WCF.System.Confirmation.show(t.data("confirmMessage"),function(e){"confirm"===e&&(a._packageID=t.data("objectID"),a.prepareInstallation())},void 0,void 0,!0)},_getParameters:function(){return{packageID:this._packageID,step:"prepare"}}}),WCF.ACP.Package.Server={},WCF.ACP.Package.Server.Installation=Class.extend({_proxy:null,_selectedPackage:"",init:function(){this._dialog=null,this._selectedPackage=null,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)})},bind:function(){$(".jsButtonPackageInstall").removeClass("jsButtonPackageInstall").click($.proxy(this._click,this))},_click:function(e){var t=$(e.currentTarget);WCF.System.Confirmation.show(t.data("confirmMessage"),$.proxy(function(e){"confirm"===e&&(this._selectedPackage=t.data("package"),this._selectedPackageVersion=t.data("packageVersion"),this._prepareInstallation())},this),void 0,void 0,!0)},_success:function(e){if(e.returnValues.queueID){null!==this._dialog&&this._dialog.wcfDialog("close");new WCF.ACP.Package.Installation(e.returnValues.queueID,void 0,!1).prepareInstallation()}else e.returnValues.template&&(null===this._dialog?(this._dialog=$("<div>"+e.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.update.unauthorized")})):this._dialog.html(e.returnValues.template).wcfDialog("open"),this._dialog.find(".formSubmit > button").click($.proxy(this._submitAuthentication,this)))},_submitAuthentication:function(e){var t=$("#packageUpdateServerUsername"),a=$("#packageUpdateServerPassword");t.next("small.innerError").remove(),a.next("small.innerError").remove();var i=!0;""===$.trim(t.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(t),i=!1),""===$.trim(a.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(a),i=!1),i&&this._prepareInstallation($(e.currentTarget).data("packageUpdateServerID"))},_prepareInstallation:function(e){var t={packages:{}};t.packages[this._selectedPackage]=this._selectedPackageVersion,e&&(t.authData={packageUpdateServerID:e,password:$.trim($("#packageUpdateServerPassword").val()),saveCredentials:!!$("#packageUpdateServerSaveCredentials:checked").length,username:$.trim($("#packageUpdateServerUsername").val())}),this._proxy.setOption("data",{actionName:"prepareInstallation",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:t}),this._proxy.sendRequest()}}),WCF.ACP.Package.Update={},WCF.ACP.Package.Update.Manager=Class.extend({_dialog:null,_proxy:null,_submitButton:null,init:function(){this._dialog=null,this._submitButton=$(".formSubmit > button").click($.proxy(this._click,this)),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsPackageUpdate").each($.proxy(function(e,t){var a=$(t);a.find("input[type=checkbox]").data("packageUpdate",a).change($.proxy(this._change,this))},this))},_change:function(e){var t=$(e.currentTarget);t.is(":checked")?(t.data("packageUpdate").find("select").enable(),t.data("packageUpdate").find("dl").removeClass("disabled"),this._submitButton.enable()):(t.data("packageUpdate").find("select").disable(),t.data("packageUpdate").find("dl").addClass("disabled"),$("input[type=checkbox]:checked").length?this._submitButton.enable():this._submitButton.disable())},_click:function(e,t){var a={};if($(".jsPackageUpdate").each($.proxy(function(e,t){var i=$(t);i.find("input[type=checkbox]:checked").length&&(a[i.data("package")]=i.find("select").val())},this)),$.getLength(a)){this._submitButton.disable();var i={packages:a};t&&(i.authData={packageUpdateServerID:t,password:$.trim($("#packageUpdateServerPassword").val()),saveCredentials:!!$("#packageUpdateServerSaveCredentials:checked").length,username:$.trim($("#packageUpdateServerUsername").val())}),this._proxy.setOption("data",{actionName:"prepareUpdate",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:i}),this._proxy.sendRequest()}},_success:function(e,t,a){if(e.returnValues.queueID){null!==this._dialog&&this._dialog.wcfDialog("close");new WCF.ACP.Package.Installation(e.returnValues.queueID,void 0,!1,!0).prepareInstallation()}else e.returnValues.excludedPackages?(null===this._dialog?(this._dialog=$("<div>"+e.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.update.excludedPackages")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.acp.package.update.excludedPackages")),this._dialog.html(e.returnValues.template).wcfDialog("open")),this._submitButton.enable()):e.returnValues.template&&(null===this._dialog?(this._dialog=$("<div>"+e.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.update.unauthorized")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.acp.package.update.unauthorized")),this._dialog.html(e.returnValues.template).wcfDialog("open")),this._dialog.find(".formSubmit > button").click($.proxy(this._submitAuthentication,this)))},_submitAuthentication:function(e){var t=$("#packageUpdateServerUsername"),a=$("#packageUpdateServerPassword");t.next("small.innerError").remove(),a.next("small.innerError").remove();var i=!0;""===$.trim(t.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(t),i=!1),""===$.trim(a.val())&&($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(a),i=!1),i&&this._click(void 0,$(e.currentTarget).data("packageUpdateServerID"))}}),WCF.ACP.Package.Update.Search=Class.extend({_dialog:null,init:function(e){if(this._dialog=null,!0===e)$(".jsButtonPackageUpdate").click($.proxy(this._click,this));else{$('<li><a class="button"><span class="icon icon16 fa-refresh"></span> <span>'+WCF.Language.get("wcf.acp.package.searchForUpdates")+"</span></a></li>").click(this._click.bind(this)).prependTo($(".contentHeaderNavigation > ul"))}},_click:function(){null===this._dialog?new WCF.Action.Proxy({autoSend:!0,data:{actionName:"searchForUpdates",className:"wcf\\data\\package\\update\\PackageUpdateAction",parameters:{ignoreCache:1}},success:$.proxy(this._success,this)}):this._dialog.wcfDialog("open")},_success:function(e,t,a){e.returnValues.url?window.location=e.returnValues.url:(this._dialog=$("<div>"+WCF.Language.get("wcf.acp.package.searchForUpdates.noResults")+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.package.searchForUpdates")}))}}),WCF.ACP.PluginStore={},WCF.ACP.PluginStore.PurchasedItems={},WCF.ACP.PluginStore.PurchasedItems.Search=Class.extend({_dialog:null,_proxy:null,init:function(){this._dialog=null,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$('<li><a class="button"><span class="icon icon16 fa-shopping-cart" /> <span>'+WCF.Language.get("wcf.acp.pluginStore.purchasedItems.button.search")+"</span></a></li>").prependTo($(".contentHeaderNavigation > ul")).click($.proxy(this._click,this))},_click:function(){this._proxy.setOption("data",{actionName:"searchForPurchasedItems",className:"wcf\\data\\package\\PackageAction"}),this._proxy.sendRequest()},_success:function(e,t,a){if(e.returnValues.template){null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.html(e.returnValues.template).wcfDialog({title:WCF.Language.get("wcf.acp.pluginStore.authorization")})):(this._dialog.html(e.returnValues.template),this._dialog.wcfDialog("open"));var i=this._dialog.find("button").click($.proxy(this._submit,this));this._dialog.find("input").keyup(function(e){if(e.which==$.ui.keyCode.ENTER)return i.trigger("click"),!1})}else e.returnValues.noResults?null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.html(e.returnValues.noResults).wcfDialog({title:WCF.Language.get("wcf.acp.pluginStore.purchasedItems")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.acp.pluginStore.purchasedItems")),this._dialog.html(e.returnValues.noResults),this._dialog.wcfDialog("open")):e.returnValues.noSSL?null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.html(e.returnValues.noSSL).wcfDialog({title:WCF.Language.get("wcf.global.error.title")})):(this._dialog.wcfDialog("option","title",WCF.Language.get("wcf.global.error.title")),this._dialog.html(e.returnValues.noSSL),this._dialog.wcfDialog("open")):e.returnValues.redirectURL&&(window.location=e.returnValues.redirectURL)},_submit:function(){this._dialog.wcfDialog("close"),this._proxy.setOption("data",{actionName:"searchForPurchasedItems",className:"wcf\\data\\package\\PackageAction",parameters:{password:$("#pluginStorePassword").val(),username:$("#pluginStoreUsername").val()}}),this._proxy.sendRequest()}}),WCF.ACP.Worker=Class.extend({init:function(e,t,a,i,n){if("function"==typeof n)throw new Error("The callback parameter is no longer supported, please migrate to 'WoltLabSuite/Core/Acp/Ui/Worker'.");require(["WoltLabSuite/Core/Acp/Ui/Worker"],function(n){new n({dialogId:e,dialogTitle:a,className:t,parameters:i})})}}),WCF.ACP.Category={},WCF.ACP.Category.Collapsible=WCF.Collapsible.SimpleRemote.extend({init:function(e){var t=$('.formSubmit > button[data-type="submit"]');t&&t.click($.proxy(this._sort,this)),this._super(e)},_getButtonContainer:function(e){return $("#"+e+" > .buttons")},_getContainers:function(){return $(".jsCategory").has("ol").has("li")},_getTarget:function(e){return $("#"+e+" > ol")},_sort:function(){$(".collapsibleButton").remove(),this._containers={},this._containerData={};var e=this._getContainers();0==e.length&&console.debug("[WCF.ACP.Category.Collapsible] Empty container set given, aborting."),e.each($.proxy(function(e,t){var a=$(t),i=a.wcfIdentify();this._containers[i]=a,this._initContainer(i)},this))}}),WCF.ACP.Search=WCF.Search.Base.extend({_delay:250,_providerName:"",init:function(){this._className="wcf\\data\\acp\\search\\provider\\ACPSearchProviderAction",this._super("#pageHeaderSearch input[name=q]"),$("#pageHeaderSearch > form").on("submit",function(e){e.preventDefault()}),WCF.Dropdown.getDropdownMenu("pageHeaderSearchType").find("a[data-provider-name]").on("click",$.proxy(function(e){e.preventDefault();var t=$(e.target);$(".pageHeaderSearchType > .button > .pageHeaderSearchTypeLabel").text(t.text());var a=this._providerName;if(this._providerName="everywhere"!=t.data("providerName")?t.data("providerName"):"",a!=this._providerName){var i=$.trim(this._searchInput.val());if(i){var n={data:{excludedSearchValues:this._excludedSearchValues,searchString:i}};this._queryServer(n)}}},this))},_createListItem:function(e){this._list.children("li").length>0&&$('<li class="dropdownDivider" />').appendTo(this._list),$('<li class="dropdownText">'+e.title+"</li>").appendTo(this._list);for(var t in e.items){var a=e.items[t];$('<li><a href="'+a.link+'"><span>'+WCF.String.escapeHTML(a.title)+"</span>"+(a.subtitle?"<small>"+WCF.String.escapeHTML(a.subtitle)+"</small>":"")+"</a></li>").appendTo(this._list),this._itemCount++}},_openDropdown:function(){this._list.find("small").each(function(e,t){for(;t.scrollWidth>t.clientWidth;)t.innerText="… "+t.innerText.substr(3)})},_handleEmptyResult:function(){return $('<li class="dropdownText">'+WCF.Language.get("wcf.acp.search.noResults")+"</li>").appendTo(this._list),!0},_highlightSelectedElement:function(){this._list.find("li").removeClass("dropdownNavigationItem"),this._list.find("li:not(.dropdownDivider):not(.dropdownText)").eq(this._itemIndex).addClass("dropdownNavigationItem")},_selectElement:function(e){if(-1===this._itemIndex)return!1;window.location=this._list.find("li.dropdownNavigationItem > a").attr("href")},_success:function(e){this._super(e);var t=elById("pageHeaderSearch");this._list[0].style.setProperty("top",t.offsetTop+t.clientHeight+"px","important"),this._list.addClass("acpSearchDropdown")},_getParameters:function(e){return e.data.providerName=this._providerName,e}}),WCF.ACP.User={},WCF.ACP.User.BanHandler={_callback:null,_dialog:null,_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsBanButton").click($.proxy(function(e){var t=$(e.currentTarget);t.data("banned")?this.unban([t.data("objectID")]):this.ban([t.data("objectID")])},this)),require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.user",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.user.ban"===e.data.actionName&&this.ban(e.data.parameters.objectIDs)},unban:function(e){this._proxy.setOption("data",{actionName:"unban",className:"wcf\\data\\user\\UserAction",objectIDs:e}),this._proxy.sendRequest()},ban:function(e){null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.append($('<div class="section"><dl><dt><label for="userBanReason">'+WCF.Language.get("wcf.acp.user.banReason")+'</label></dt><dd><textarea id="userBanReason" cols="40" rows="3" /><small>'+WCF.Language.get("wcf.acp.user.banReason.description")+'</small></dd></dl><dl><dt></dt><dd><label for="userBanNeverExpires"><input type="checkbox" name="userBanNeverExpires" id="userBanNeverExpires" checked> '+WCF.Language.get("wcf.acp.user.ban.neverExpires")+'</label></dd></dl><dl id="userBanExpiresSettings" style="display: none;"><dt><label for="userBanExpires">'+WCF.Language.get("wcf.acp.user.ban.expires")+'</label></dt><dd><input type="date" name="userBanExpires" id="userBanExpires" class="medium" min="'+new Date(1e3*TIME_NOW).toISOString()+'" data-ignore-timezone="true" /><small>'+WCF.Language.get("wcf.acp.user.ban.expires.description")+"</small></dd></dl></div>")),this._dialog.append($('<div class="formSubmit"><button class="buttonPrimary" accesskey="s">'+WCF.Language.get("wcf.global.button.submit")+"</button></div>")),this._dialog.find("#userBanNeverExpires").change(function(){$("#userBanExpiresSettings").toggle()}),this._dialog.find("button").click($.proxy(this._submit,this))):($("#userBanReason").val(""),$("#userBanNeverExpires").prop("checked",!0),$("#userBanExpiresSettings").hide(),$("#userBanExpiresDatePicker, #userBanExpires").val("")),this._dialog.data("userIDs",e),this._dialog.wcfDialog({title:WCF.Language.get("wcf.acp.user.ban.sure")})},_submit:function(){this._dialog.find(".innerError").remove();var e="";if(!$("#userBanNeverExpires").is(":checked")){var e=$("#userBanExpiresDatePicker").val();if(!e)return void this._dialog.find("#userBanExpiresSettings > dd > small").prepend($('<small class="innerError" />').text(WCF.Language.get("wcf.global.form.error.empty")))}this._proxy.setOption("data",{actionName:"ban",className:"wcf\\data\\user\\UserAction",objectIDs:this._dialog.data("userIDs"),parameters:{banReason:$("#userBanReason").val(),banExpires:e}}),this._proxy.sendRequest()},_success:function(e,t,a){elBySelAll(".jsUserRow",void 0,function(t){var a=parseInt(elData(t,"object-id"),10);-1!==e.objectIDs.indexOf(a)&&elData(t,"banned","ban"===e.actionName)}),$(".jsBanButton").each(function(t,a){var i=$(a);WCF.inArray(i.data("objectID"),e.objectIDs)&&("unban"==e.actionName?i.data("banned",!1).attr("data-tooltip",i.data("banMessage")).removeClass("fa-lock").addClass("fa-unlock"):i.data("banned",!0).attr("data-tooltip",i.data("unbanMessage")).removeClass("fa-unlock").addClass("fa-lock"))}),(new WCF.System.Notification).show(),WCF.Clipboard.reload(),"ban"==e.actionName&&this._dialog.wcfDialog("close"),WCF.System.Event.fireEvent("com.woltlab.wcf.acp.user","refresh",{userIds:e.objectIDs})}},WCF.ACP.User.Group={},WCF.ACP.User.Group.Copy=Class.extend({_groupID:0,init:function(e){this._groupID=e,$(".jsButtonUserGroupCopy").click($.proxy(this._click,this))},_click:function(){var e=$('<div class="section" />');e.append($('<dl class="wide"><dt /><dd><label><input type="checkbox" id="copyMembers" value="1" /> '+WCF.Language.get("wcf.acp.group.copy.copyMembers")+"</label><small>"+WCF.Language.get("wcf.acp.group.copy.copyMembers.description")+"</small></dd></dl>")),e.append($('<dl class="wide"><dt /><dd><label><input type="checkbox" id="copyUserGroupOptions" value="1" /> '+WCF.Language.get("wcf.acp.group.copy.copyUserGroupOptions")+"</label><small>"+WCF.Language.get("wcf.acp.group.copy.copyUserGroupOptions.description")+"</small></dd></dl>")),e.append($('<dl class="wide"><dt /><dd><label><input type="checkbox" id="copyACLOptions" value="1" /> '+WCF.Language.get("wcf.acp.group.copy.copyACLOptions")+"</label><small>"+WCF.Language.get("wcf.acp.group.copy.copyACLOptions.description")+"</small></dd></dl>")),WCF.System.Confirmation.show(WCF.Language.get("wcf.acp.group.copy.confirmMessage"),$.proxy(function(e){"confirm"===e&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"copy",className:"wcf\\data\\user\\group\\UserGroupAction",objectIDs:[this._groupID],parameters:{copyACLOptions:$("#copyACLOptions").is(":checked"),copyMembers:$("#copyMembers").is(":checked"),copyUserGroupOptions:$("#copyUserGroupOptions").is(":checked")}},success:function(e){window.location=e.returnValues.redirectURL}})},this),"",e,!0)}}),WCF.ACP.User.EnableHandler={_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".jsEnableButton").click($.proxy(function(e){var t=$(e.currentTarget);t.data("enabled")?this.disable([t.data("objectID")]):this.enable([t.data("objectID")])},this)),require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.user",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.user.enable"===e.data.actionName&&this.enable(e.data.parameters.objectIDs)},disable:function(e){this._proxy.setOption("data",{actionName:"disable",className:"wcf\\data\\user\\UserAction",objectIDs:e}),this._proxy.sendRequest()},enable:function(e){this._proxy.setOption("data",{actionName:"enable",className:"wcf\\data\\user\\UserAction",objectIDs:e}),this._proxy.sendRequest()},_success:function(e,t,a){elBySelAll(".jsUserRow",void 0,function(t){var a=parseInt(elData(t,"object-id"),10);-1!==e.objectIDs.indexOf(a)&&elData(t,"enabled","enable"===e.actionName)}),$(".jsEnableButton").each(function(t,a){var i=$(a);WCF.inArray(i.data("objectID"),e.objectIDs)&&("disable"==e.actionName?i.data("enabled",!1).attr("data-tooltip",i.data("enableMessage")).removeClass("fa-check-square-o").addClass("fa-square-o"):i.data("enabled",!0).attr("data-tooltip",i.data("disableMessage")).removeClass("fa-square-o").addClass("fa-check-square-o"))}),(new WCF.System.Notification).show(function(){window.location.reload()}),WCF.System.Event.fireEvent("com.woltlab.wcf.acp.user","refresh",{userIds:e.objectIDs})}},WCF.ACP.User.SendNewPasswordHandler={init:function(){require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.user",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.user.sendNewPassword"===e.data.actionName&&WCF.System.Confirmation.show(e.data.parameters.confirmMessage,function(t){"confirm"===t&&new WCF.ACP.Worker("sendingNewPasswords","wcf\\system\\worker\\SendNewPasswordWorker",WCF.Language.get("wcf.acp.user.sendNewPassword.workerTitle"),{userIDs:e.data.parameters.objectIDs})})}},WCF.ACP.Import={},WCF.ACP.Import.Manager=Class.extend({_currentAction:"",_dialog:null,_index:-1,_objectTypes:[],_proxy:null,_redirectURL:"",init:function(e,t){this._currentAction="",this._index=-1,this._objectTypes=e,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this),url:"index.php?worker-proxy/&t="+SECURITY_TOKEN}),this._redirectURL=t,this._invoke()},_invoke:function(){if(++this._index>=this._objectTypes.length){this._dialog.find(".fa-spinner").removeClass("fa-spinner").addClass("fa-check"),this._dialog.find("h1").text(WCF.Language.get("wcf.acp.dataImport.completed"));var e=$('<div class="formSubmit" />').appendTo(this._dialog.find("#workerContainer"));$("<button>"+WCF.Language.get("wcf.global.button.next")+"</button>").click($.proxy(function(){new WCF.Action.Proxy({autoSend:!0,data:{noRedirect:1},dataType:"html",success:$.proxy(function(){window.location=this._redirectURL},this),url:"index.php?cache-clear/&t="+SECURITY_TOKEN})},this)).appendTo(e),this._dialog.wcfDialog("render")}else this._run(WCF.Language.get("wcf.acp.dataImport.data."+this._objectTypes[this._index]),this._objectTypes[this._index])},_run:function(e,t){this._currentAction=e,this._proxy.setOption("data",{className:"wcf\\system\\worker\\ImportWorker",parameters:{objectType:t}}),this._proxy.sendRequest()},_success:function(e){null===this._dialog&&(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.wcfDialog({closable:!1,title:WCF.Language.get("wcf.acp.dataImport")})),e.template&&this._dialog.html(e.template),this._currentAction&&this._dialog.find("h1").text(this._currentAction),this._dialog.find("progress").attr("value",e.progress).text(e.progress+"%").next("span").text(e.progress+"%"),e.progress<100?(this._proxy.setOption("data",{className:e.className,loopCount:e.loopCount,parameters:e.parameters}),this._proxy.sendRequest()):this._invoke()}}),WCF.ACP.Stat={},WCF.ACP.Stat.Chart=Class.extend({init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$("#statRefreshButton").click($.proxy(this._refresh,this)),this._refresh()},_refresh:function(){var e=[];$("input[name=objectTypeID]:checked").each(function(){e.push($(this).val())}),e.length&&(this._proxy.setOption("data",{className:"wcf\\data\\stat\\daily\\StatDailyAction",actionName:"getData",parameters:{startDate:$("#startDateDatePicker").val(),endDate:$("#endDateDatePicker").val(),value:$("input[name=value]:checked").val(),dateGrouping:$("input[name=dateGrouping]:checked").val(),objectTypeIDs:e}}),this._proxy.sendRequest())},_success:function(e){switch($("input[name=dateGrouping]:checked").val()){case"yearly":var t=[1,"year"],a=WCF.Language.get("wcf.acp.stat.timeFormat.yearly");break;case"monthly":var t=[1,"month"],a=WCF.Language.get("wcf.acp.stat.timeFormat.monthly");break;case"weekly":var t=[7,"day"],a=WCF.Language.get("wcf.acp.stat.timeFormat.weekly");break;default:var t=[1,"day"],a=WCF.Language.get("wcf.acp.stat.timeFormat.daily")}var i={series:{lines:{show:!0},points:{show:!0}},grid:{hoverable:!0},xaxis:{mode:"time",minTickSize:t,timeformat:a,monthNames:WCF.Language.get("__monthsShort")},yaxis:{min:0,tickDecimals:0,tickFormatter:function(e){return WCF.String.addThousandsSeparator(e)}}},n=[];for(var s in e.returnValues){for(var o=e.returnValues[s],r=0;r<o.data.length;r++)o.data[r][0]*=1e3;n.push(o)}$.plot("#chart",n,i),require(["Ui/Alignment"],function(e){var t=elCreate("span");t.style.setProperty("position","absolute",""),document.body.appendChild(t),$("#chart").on("plothover",function(a,i,n){n?(t.style.setProperty("top",n.pageY+"px",""),t.style.setProperty("left",n.pageX+"px",""),$("#chartTooltip").html(n.series.xaxis.tickFormatter(n.datapoint[0],n.series.xaxis)+", "+WCF.String.formatNumeric(n.datapoint[1])+" "+n.series.label).show(),e.set($("#chartTooltip")[0],t,{verticalOffset:5,horizontal:"center"})):$("#chartTooltip").hide()})}),n.length||$("#chart").append('<p style="position: absolute; font-size: 1.2rem; text-align: center; top: 50%; margin-top: -20px; width: 100%">'+WCF.Language.get("wcf.acp.stat.noData")+"</p>"),elBySel(".contentHeader > .contentTitle").scrollIntoView({behavior:"smooth"})}}),WCF.ACP.Ad={},WCF.ACP.Ad.LocationHandler=Class.extend({_pageConditions:null,_pageInputs:[],_pageSelectionContainer:null,init:function(e){this._variablesDescriptions=e,this._pageConditions=$("#pageConditions"),this._pageInputs=$('input[name="pageIDs[]"]'),this._variablesDescriptionsList=$("#ad").next("small").children("ul"),this._pageSelectionContainer=$(this._pageInputs[0]).parents("dl:eq(0)"),this._hidePageSelection(!0),$("#objectTypeID").on("change",$.proxy(this._setPageController,this)),this._setPageController(),$("#adForm").submit($.proxy(this._submit,this))},
+_hidePageSelection:function(e){this._pageSelectionContainer.prev("dl").hide(),this._pageSelectionContainer.hide(),this._pageSelectionContainer.next("dl").css("margin-top",0);var t=this._pageSelectionContainer.parent("section");if(!t.children("dl:visible").length){t.hide();var a=t.next("section");a&&(a.css("margin-top",0),e&&require(["EventHandler"],function(e){e.add("com.woltlab.wcf.pageConditionDependence","checkVisivility",function(){t.is(":visible")?a.css("margin-top","40px"):a.css("margin-top",0)})}))}},_showPageSelection:function(){this._pageSelectionContainer.prev("dl").show(),this._pageSelectionContainer.show(),this._pageSelectionContainer.next("dl").css("margin-top","40px");var e=this._pageSelectionContainer.parent("section");e.show();var t=e.next("section");t&&t.css("margin-top","40px")},_setPageController:function(){var e=$("#objectTypeID").find("option:checked"),t=e.parent();t.is("optgroup")&&"com.woltlab.wcf.global"===t.data("categoryName")?this._showPageSelection():(this._hidePageSelection(),require(["Core"],function(t){for(var a,i,n=0,s=this._pageInputs.length;n<s;n++)a=this._pageInputs[n],i=!1,e.data("page")&&elData(a,"identifier")===e.data("page")?(a.checked||(i=!0),a.checked=!0):(a.checked&&(i=!0),a.checked=!1),i&&t.triggerEvent(this._pageInputs[n],"change")}.bind(this))),this._variablesDescriptionsList.children(":not(.jsDefaultItem)").remove();var a=$("#objectTypeID").val();a in this._variablesDescriptions&&(this._variablesDescriptionsList[0].innerHTML+=this._variablesDescriptions[a])},_submit:function(){if(this._pageConditions.is(":hidden"))this._pageConditions.find("select, input").remove();else if(this._pageSelectionContainer.is(":hidden"))for(var e=0,t=this._pageInputs.length;e<t;e++)this._pageInputs[e].checked=!1}}),WCF.ACP.Tag={},WCF.ACP.Tag.SetAsSynonymsHandler=Class.extend({_dialog:null,_objectIDs:[],init:function(){require(["EventHandler"],function(e){e.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.tag",this._clipboardAction.bind(this))}.bind(this))},_clipboardAction:function(e){"com.woltlab.wcf.tag.setAsSynonyms"===e.data.actionName&&(this._objectIDs=e.data.parameters.objectIDs,null===this._dialog&&(this._dialog=$('<div id="setAsSynonymsDialog" />').hide().appendTo(document.body),this._dialog.wcfDialog({closable:!1,title:WCF.Language.get("wcf.acp.tag.setAsSynonyms")})),this._dialog.html(e.data.parameters.template),$button=this._dialog.find('button[data-type="submit"]').disable().click($.proxy(this._submit,this)),this._dialog.find("input[type=radio]").change(function(){$button.enable()}))},_submit:function(){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"setAsSynonyms",className:"wcf\\data\\tag\\TagAction",objectIDs:this._objectIDs,parameters:{tagID:this._dialog.find('input[name="tagID"]:checked').val()}},success:$.proxy(function(){this._dialog.wcfDialog("close"),(new WCF.System.Notification).show(function(){window.location.reload()})},this)})}});
\ No newline at end of file
// redactor.combined.min.js -- DO NOT EDIT
// redactor.js
-(function (window, undefined) { !function(t){"use strict";function e(t,i){return new e.prototype.init(t,i)}Function.prototype.bind||(Function.prototype.bind=function(t){var e=this;return function(){return e.apply(t)}});var i=0;t.fn.redactor=function(i){var r=[],o=Array.prototype.slice.call(arguments,1);return"string"==typeof i?this.each(function(){var e,s=t.data(this,"redactor");if("-1"!==i.search(/\./)?(e=i.split("."),void 0!==s[e[0]]&&(e=s[e[0]][e[1]])):e=s[i],void 0!==s&&t.isFunction(e)){var n=e.apply(s,o);void 0!==n&&n!==s&&r.push(n)}else t.error('No such method "'+i+'" for Redactor')}):this.each(function(){t.data(this,"redactor",{}),t.data(this,"redactor",e(this,i))}),0===r.length?this:1===r.length?r[0]:r},t.Redactor=e,t.Redactor.VERSION="2.12",t.Redactor.modules=["air","autosave","block","buffer","build","button","caret","clean","code","core","detect","dropdown","events","file","focus","image","indent","inline","insert","keydown","keyup","lang","line","link","linkify","list","marker","modal","observe","offset","paragraphize","paste","placeholder","progress","selection","shortcuts","storage","toolbar","upload","uploads3","utils","browser"],t.Redactor.settings={},t.Redactor.opts={animation:!1,lang:"en",direction:"ltr",spellcheck:!0,overrideStyles:!0,stylesClass:!1,scrollTarget:document,focus:!1,focusEnd:!1,clickToEdit:!1,structure:!1,tabindex:!1,minHeight:!1,maxHeight:!1,maxWidth:!1,plugins:!1,callbacks:{},placeholder:!1,linkify:!0,enterKey:!0,pastePlainText:!1,pasteImages:!0,pasteLinks:!0,pasteBlockTags:["pre","h1","h2","h3","h4","h5","h6","table","tbody","thead","tfoot","th","tr","td","ul","ol","li","blockquote","p","figure","figcaption"],pasteInlineTags:["br","strong","ins","code","del","span","samp","kbd","sup","sub","mark","var","cite","small","b","u","em","i"],preClass:!1,preSpaces:4,tabAsSpaces:!1,tabKey:!0,autosave:!1,autosaveName:!1,autosaveFields:!1,imageUpload:null,imageUploadParam:"file",imageUploadFields:!1,imageUploadForms:!1,imageTag:"figure",imageEditable:!0,imageCaption:!0,imagePosition:!1,imageResizable:!1,imageFloatMargin:"10px",dragImageUpload:!0,multipleImageUpload:!0,clipboardImageUpload:!0,fileUpload:null,fileUploadParam:"file",fileUploadFields:!1,fileUploadForms:!1,dragFileUpload:!0,s3:!1,linkNewTab:!1,linkTooltip:!0,linkNofollow:!1,linkSize:30,linkValidation:!0,pasteLinkTarget:!1,videoContainerClass:"video-container",toolbar:!0,toolbarFixed:!0,toolbarFixedTarget:document,toolbarFixedTopOffset:0,toolbarExternal:!1,toolbarOverflow:!1,air:!1,airWidth:!1,formatting:["p","blockquote","pre","h1","h2","h3","h4","h5","h6"],formattingAdd:!1,buttons:["format","bold","italic","deleted","lists","image","file","link","horizontalrule"],buttonsTextLabeled:!1,buttonsHide:[],buttonsHideOnMobile:[],script:!0,removeNewlines:!1,removeComments:!0,replaceTags:{b:"strong",i:"em",strike:"del"},keepStyleAttr:[],keepInlineOnEnter:!1,shortcuts:{"ctrl+shift+m, meta+shift+m":{func:"inline.removeFormat"},"ctrl+b, meta+b":{func:"inline.format",params:["bold"]},"ctrl+i, meta+i":{func:"inline.format",params:["italic"]},"ctrl+h, meta+h":{func:"inline.format",params:["superscript"]},"ctrl+l, meta+l":{func:"inline.format",params:["subscript"]},"ctrl+k, meta+k":{func:"link.show"},"ctrl+shift+7":{func:"list.toggle",params:["orderedlist"]},"ctrl+shift+8":{func:"list.toggle",params:["unorderedlist"]}},shortcutsAdd:!1,activeButtons:["deleted","italic","bold"],activeButtonsStates:{b:"bold",strong:"bold",i:"italic",em:"italic",del:"deleted",strike:"deleted"},langs:{en:{format:"Format",image:"Image",file:"File",link:"Link",bold:"Bold",italic:"Italic",deleted:"Strikethrough",underline:"Underline","bold-abbr":"B","italic-abbr":"I","deleted-abbr":"S","underline-abbr":"U",lists:"Lists","link-insert":"Insert link","link-edit":"Edit link","link-in-new-tab":"Open link in new tab",unlink:"Unlink",cancel:"Cancel",close:"Close",insert:"Insert",save:"Save",delete:"Delete",text:"Text",edit:"Edit",title:"Title",paragraph:"Normal text",quote:"Quote",code:"Code",heading1:"Heading 1",heading2:"Heading 2",heading3:"Heading 3",heading4:"Heading 4",heading5:"Heading 5",heading6:"Heading 6",filename:"Name",optional:"optional",unorderedlist:"Unordered List",orderedlist:"Ordered List",outdent:"Outdent",indent:"Indent",horizontalrule:"Line","upload-label":"Drop file here or ",caption:"Caption",bulletslist:"Bullets",numberslist:"Numbers","image-position":"Position",none:"None",left:"Left",right:"Right",center:"Center","accessibility-help-label":"Rich text editor"}},type:"textarea",inline:!1,inlineTags:["a","span","strong","strike","b","u","em","i","code","del","ins","samp","kbd","sup","sub","mark","var","cite","small"],blockTags:["pre","ul","ol","li","p","h1","h2","h3","h4","h5","h6","dl","dt","dd","div","td","blockquote","output","figcaption","figure","address","section","header","footer","aside","article","iframe"],paragraphize:!0,paragraphizeBlocks:["table","div","pre","form","ul","ol","h1","h2","h3","h4","h5","h6","dl","blockquote","figcaption","address","section","header","footer","aside","article","object","style","script","iframe","select","input","textarea","button","option","map","area","math","hr","fieldset","legend","hgroup","nav","figure","details","menu","summary","p"],emptyHtml:"<p>​</p>",invisibleSpace:"​",emptyHtmlRendered:t("").html("").html(),imageTypes:["image/png","image/jpeg","image/gif"],userAgent:navigator.userAgent.toLowerCase(),observe:{dropdowns:[]},regexps:{linkyoutube:/https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/gi,linkvimeo:/https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/,linkimage:/((https?|www)[^\s]+\.)(jpe?g|png|gif)(\?[^\s-]+)?/gi,url:/(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/gi}},e.fn=t.Redactor.prototype={keyCode:{BACKSPACE:8,DELETE:46,UP:38,DOWN:40,ENTER:13,SPACE:32,ESC:27,TAB:9,CTRL:17,META:91,SHIFT:16,ALT:18,RIGHT:39,LEFT:37,LEFT_WIN:91},init:function(e,r){if(this.$element=t(e),this.uuid=i++,this.sBuffer=[],this.sRebuffer=[],this.loadOptions(r),this.loadModules(),this.opts.clickToEdit&&!this.$element.hasClass("redactor-click-to-edit"))return this.loadToEdit(r);this.$element.hasClass("redactor-click-to-edit")&&this.$element.removeClass("redactor-click-to-edit"),this.reIsBlock=new RegExp("^("+this.opts.blockTags.join("|").toUpperCase()+")$","i"),this.reIsInline=new RegExp("^("+this.opts.inlineTags.join("|").toUpperCase()+")$","i"),this.opts.dragImageUpload=null!==this.opts.imageUpload&&this.opts.dragImageUpload,this.opts.dragFileUpload=null!==this.opts.fileUpload&&this.opts.dragFileUpload,this.formatting={},this.lang.load(),t.extend(this.opts.shortcuts,this.opts.shortcutsAdd),this.$editor=this.$element,this.detectType(),this.core.callback("start"),this.core.callback("startToEdit"),this.start=!0,this.build.start()},detectType:function(){this.build.isInline()||this.opts.inline?this.opts.type="inline":this.build.isTag("DIV")?this.opts.type="div":this.build.isTag("PRE")&&(this.opts.type="pre")},loadToEdit:function(e){this.$element.on("click.redactor-click-to-edit",t.proxy(function(){this.initToEdit(e)},this)),this.$element.addClass("redactor-click-to-edit")},initToEdit:function(e){t.extend(e.callbacks,{startToEdit:function(){this.insert.node(this.marker.get(),!1)},initToEdit:function(){this.selection.restore(),this.clickToCancelStorage=this.code.get(),t(this.opts.clickToCancel).off(".redactor-click-to-edit"),t(this.opts.clickToCancel).show().on("click.redactor-click-to-edit",t.proxy(function(i){i.preventDefault(),this.core.destroy(),this.events.syncFire=!1,this.$element.html(this.clickToCancelStorage),this.core.callback("cancel",this.clickToCancelStorage),this.events.syncFire=!0,this.clickToCancelStorage="",t(this.opts.clickToCancel).hide(),t(this.opts.clickToSave).hide(),this.$element.on("click.redactor-click-to-edit",t.proxy(function(){this.initToEdit(e)},this)),this.$element.addClass("redactor-click-to-edit")},this)),t(this.opts.clickToSave).off(".redactor-click-to-edit"),t(this.opts.clickToSave).show().on("click.redactor-click-to-edit",t.proxy(function(i){i.preventDefault(),this.core.destroy(),this.core.callback("save",this.code.get()),t(this.opts.clickToCancel).hide(),t(this.opts.clickToSave).hide(),this.$element.on("click.redactor-click-to-edit",t.proxy(function(){this.initToEdit(e)},this)),this.$element.addClass("redactor-click-to-edit")},this))}}),this.$element.redactor(e),this.$element.off(".redactor-click-to-edit")},loadOptions:function(e){var i={};void 0!==t.Redactor.settings.namespace?this.$element.hasClass(t.Redactor.settings.namespace)&&(i=t.Redactor.settings):i=t.Redactor.settings,this.opts=t.extend({},t.Redactor.opts,this.$element.data(),e),this.opts=t.extend({},this.opts,i)},getModuleMethods:function(t){return Object.getOwnPropertyNames(t).filter(function(e){return"function"==typeof t[e]})},loadModules:function(){for(var e=t.Redactor.modules.length,i=0;i<e;i++)this.bindModuleMethods(t.Redactor.modules[i])},bindModuleMethods:function(t){if(void 0!==this[t]){this[t]=this[t]();for(var e=this.getModuleMethods(this[t]),i=e.length,r=0;r<i;r++)this[t][e[r]]=this[t][e[r]].bind(this)}},air:function(){return{enabled:!1,collapsed:function(){this.opts.air&&this.selection.get().collapseToStart()},collapsedEnd:function(){this.opts.air&&this.selection.get().collapseToEnd()},build:function(){this.detect.isMobile()||(this.button.hideButtons(),this.button.hideButtonsOnMobile(),0!==this.opts.buttons.length&&(this.$air=this.air.createContainer(),!1!==this.opts.airWidth&&this.$air.css("width",this.opts.airWidth),this.air.append(),this.button.$toolbar=this.$air,this.button.setFormatting(),this.button.load(this.$air),this.core.editor().on("mouseup.redactor",this,t.proxy(function(t){""!==this.selection.text()&&this.air.show(t)},this))))},append:function(){this.$air.appendTo("body")},createContainer:function(){return t("<ul>").addClass("redactor-air").attr({id:"redactor-air-"+this.uuid,role:"toolbar"}).hide()},show:function(e){this.selection.saveInstant(),t(".redactor-air").hide();var i=0,r=this.$air.innerWidth();t(window).width()<e.clientX+r&&(i=200),this.$air.css({left:e.clientX-i+"px",top:e.clientY+10+t(document).scrollTop()+"px"}).show(),this.air.enabled=!0,this.air.bindHide()},bindHide:function(){t(document).on("mousedown.redactor-air."+this.uuid,t.proxy(function(e){var i=t(e.target).closest(".redactor-dropdown").length;if(0===t(e.target).closest(this.$air).length&&0===i){!1!==this.air.hide(e)&&this.marker.remove()}},this)).on("keydown.redactor-air."+this.uuid,t.proxy(function(e){var i=e.which;if((this.utils.isRedactorParent(e.target)||t(e.target).hasClass("redactor-in"))&&0===t(e.target).closest("#redactor-modal").length){if(i===this.keyCode.ESC)this.selection.get().collapseToStart();else if(i===this.keyCode.BACKSPACE||i===this.keyCode.DELETE){var r=this.selection.get(),o=this.selection.range(r);o.deleteContents()}else i===this.keyCode.ENTER&&this.selection.get().collapseToEnd();this.air.enabled?this.air.hide(e):this.selection.get().collapseToStart()}},this))},hide:function(e){if(e.ctrlKey||e.metaKey||e.shiftKey&&e.altKey)return!1;this.button.setInactiveAll(),this.$air.fadeOut(100),this.air.enabled=!1,t(document).off("mousedown.redactor-air."+this.uuid),t(document).off("keydown.redactor-air."+this.uuid)}}},autosave:function(){return{enabled:!1,html:!1,init:function(){this.opts.autosave&&(this.autosave.enabled=!0,this.autosave.name=this.opts.autosaveName?this.opts.autosaveName:this.$textarea.attr("name"))},is:function(){return this.autosave.enabled},send:function(){if(this.opts.autosave&&(this.autosave.source=this.code.get(),this.autosave.html!==this.autosave.source)){var e={};e.name=this.autosave.name,e[this.autosave.name]=this.autosave.source,e=this.autosave.getHiddenFields(e);t.ajax({url:this.opts.autosave,type:"post",data:e}).done(this.autosave.success)}},getHiddenFields:function(e){return!1===this.opts.autosaveFields||"object"!=typeof this.opts.autosaveFields?e:(t.each(this.opts.autosaveFields,t.proxy(function(i,r){null!==r&&0===r.toString().indexOf("#")&&(r=t(r).val()),e[i]=r},this)),e)},success:function(t){var e;try{e=JSON.parse(t)}catch(i){e=t}var i=void 0===e.error?"autosave":"autosaveError";this.core.callback(i,this.autosave.name,e),this.autosave.html=this.autosave.source},disable:function(){this.autosave.enabled=!1,clearInterval(this.autosaveTimeout)}}},block:function(){return{format:function(e,i,r,o){if(e="quote"===e?"blockquote":e,this.block.tags=["p","blockquote","pre","h1","h2","h3","h4","h5","h6","div","figure"],-1!==t.inArray(e,this.block.tags))return"p"===e&&void 0===i&&(i="class"),this.placeholder.hide(),this.buffer.set(),this.utils.isCollapsed()?this.block.formatCollapsed(e,i,r,o):this.block.formatUncollapsed(e,i,r,o)},formatCollapsed:function(e,i,r,o){this.selection.save();var s=this.selection.block(),n=s.tagName.toLowerCase();if(-1===t.inArray(n,this.block.tags))return void this.selection.restore();var a=!1;n===e&&void 0===i&&(e="p",a=!0),a&&(this.block.removeAllClass(),this.block.removeAllAttr());var l;if("blockquote"===n&&this.utils.isEndOfElement(s)){this.marker.remove(),l=document.createElement("p"),l.innerHTML=this.opts.invisibleSpace,t(s).after(l),this.caret.start(l);var c=t(s).children().last();0!==c.length&&"BR"===c[0].tagName&&c.remove()}else l=this.utils.replaceToTag(s,e);if("object"==typeof i){o=r;for(var d in i)l=this.block.setAttr(l,d,i[d],o)}else l=this.block.setAttr(l,i,r,o);return"pre"===e&&1===l.length&&t(l).html(t.trim(t(l).html())),this.selection.restore(),this.block.removeInlineTags(l),l},formatUncollapsed:function(e,i,r,o){this.selection.save();var s=[],n=this.selection.blocks();n[0]&&(t(n[0]).hasClass("redactor-in")||t(n[0]).hasClass("redactor-box"))&&(n=this.core.editor().find(this.opts.blockTags.join(", ")));for(var a=n.length,l=0;l<a;l++){var c=n[l].tagName.toLowerCase();if(-1!==t.inArray(c,this.block.tags)&&"figure"!==c){var d=this.utils.replaceToTag(n[l],e);if("object"==typeof i){o=r;for(var h in i)d=this.block.setAttr(d,h,i[h],o)}else d=this.block.setAttr(d,i,r,o);s.push(d),this.block.removeInlineTags(d)}}if(this.selection.restore(),"pre"===e&&0!==s.length){var u=s[0];t.each(s,function(e,i){0!==e&&(t(u).append("\n"+t.trim(i.html())),t(i).remove())}),s=[],s.push(u)}return s},removeInlineTags:function(e){e=e[0]||e;var i=this.opts.inlineTags,r=["PRE","H1","H2","H3","H4","H5","H6"];if(-1!==t.inArray(e.tagName,r)){if("PRE"!==e.tagName){var o=i.indexOf("a");i.splice(o,1)}t(e).find(i.join(",")).not(".redactor-selection-marker").contents().unwrap()}},setAttr:function(t,e,i,r){if(void 0===e)return t;var o=void 0===r?"replace":r;return t="class"===e?this.block[o+"Class"](i,t):"remove"===o?this.block[o+"Attr"](e,t):"removeAll"===o?this.block[o+"Attr"](e,t):this.block[o+"Attr"](e,i,t)},getBlocks:function(e){if(e=void 0===e?this.selection.blocks():e,t(e).hasClass("redactor-box")){var i=[],r=this.core.editor().children();return t.each(r,t.proxy(function(t,e){this.utils.isBlock(e)&&i.push(e)},this)),i}return e},replaceClass:function(e,i){return t(this.block.getBlocks(i)).removeAttr("class").addClass(e)[0]},toggleClass:function(e,i){return t(this.block.getBlocks(i)).toggleClass(e)[0]},addClass:function(e,i){return t(this.block.getBlocks(i)).addClass(e)[0]},removeClass:function(e,i){return t(this.block.getBlocks(i)).removeClass(e)[0]},removeAllClass:function(e){return t(this.block.getBlocks(e)).removeAttr("class")[0]},replaceAttr:function(e,i,r){return r=this.block.removeAttr(e,r),t(r).attr(e,i)[0]},toggleAttr:function(e,i,r){r=this.block.getBlocks(r);var o=this,s=[];return t.each(r,function(r,n){t(n).attr(e)?s.push(o.block.removeAttr(e,n)):s.push(o.block.addAttr(e,i,n))}),s},addAttr:function(e,i,r){return t(this.block.getBlocks(r)).attr(e,i)[0]},removeAttr:function(e,i){return t(this.block.getBlocks(i)).removeAttr(e)[0]},removeAllAttr:function(e){e=this.block.getBlocks(e);var i=[];return t.each(e,function(t,e){if(void 0!==e.attributes)for(;e.attributes.length;)e.removeAttribute(e.attributes[0].name);i.push(e)}),i}}},buffer:function(){return{set:function(t){void 0===t&&this.buffer.clear(),void 0===t||"undo"===t?this.buffer.setUndo():this.buffer.setRedo()},setUndo:function(){var t=this.selection.saveInstant(),e=this.sBuffer[this.sBuffer.length-1],i=this.core.editor().html();(void 0===e||e[0]!==i)&&this.sBuffer.push([i,t])},setRedo:function(){var t=this.selection.saveInstant();this.sRebuffer.push([this.core.editor().html(),t])},add:function(){this.sBuffer.push([this.core.editor().html(),0])},undo:function(){if(0!==this.sBuffer.length){var t=this.sBuffer.pop();this.buffer.set("redo"),this.core.editor().html(t[0]),this.selection.restoreInstant(t[1]),this.selection.restore(),this.observe.load()}},redo:function(){if(0!==this.sRebuffer.length){var t=this.sRebuffer.pop();this.buffer.set("undo"),this.core.editor().html(t[0]),this.selection.restoreInstant(t[1]),this.selection.restore(),this.observe.load()}},clear:function(){this.sRebuffer=[]}}},build:function(){return{start:function(){if("inline"===this.opts.type)this.opts.type="inline";else if("div"===this.opts.type){var e=t.trim(this.$editor.html());""===e&&this.$editor.html(this.opts.emptyHtml),this.build.buildTextarea()}else"textarea"===this.opts.type&&this.build.startTextarea();this.build.setIn(),this.build.setId(),this.build.enableEditor(),this.build.setOptions(),this.build.callEditor()},createContainerBox:function(){this.$box=t('<div class="redactor-box" role="application" />')},setIn:function(){this.core.editor().addClass("redactor-in")},setId:function(){var t="textarea"===this.opts.type?"redactor-uuid-"+this.uuid:this.$element.attr("id");this.core.editor().attr("id",void 0===t?"redactor-uuid-"+this.uuid:t)},getName:function(){var t=this.$element.attr("name");return void 0===t?"content-"+this.uuid:t},buildTextarea:function(){this.$textarea=t("<textarea>"),this.$textarea.attr("name",this.build.getName()),this.$textarea.hide(),this.$element.after(this.$textarea),this.build.setStartAttrs()},loadFromTextarea:function(){this.$editor=t("<div />"),this.$textarea=this.$element,this.$element.attr("name",this.build.getName()),this.$box.insertAfter(this.$element).append(this.$editor).append(this.$element),this.build.setStartAttrs(),this.$editor.addClass("redactor-layer"),this.opts.overrideStyles&&this.$editor.addClass("redactor-styles"),this.$element.hide(),this.$box.prepend('<span class="redactor-voice-label" id="redactor-voice-'+this.uuid+'" aria-hidden="false">'+this.lang.get("accessibility-help-label")+"</span>")},setStartAttrs:function(){this.$editor.attr({"aria-labelledby":"redactor-voice-"+this.uuid,role:"presentation"})},startTextarea:function(){this.build.createContainerBox(),this.build.loadFromTextarea(),this.code.start(this.core.textarea().val()),this.core.textarea().val(this.clean.onSync(this.$editor.html()))},isTag:function(t){return this.$element[0].tagName===t},isInline:function(){return!this.build.isTag("TEXTAREA")&&!this.build.isTag("DIV")&&!this.build.isTag("PRE")},enableEditor:function(){this.core.editor().attr({contenteditable:!0})},setOptions:function(){"inline"===this.opts.type&&(this.opts.enterKey=!1),"inline"!==this.opts.type&&"pre"!==this.opts.type||(this.opts.toolbarMobile=!1,this.opts.toolbar=!1,this.opts.air=!1,this.opts.linkify=!1),this.core.editor().attr("spellcheck",this.opts.spellcheck),this.opts.structure&&this.core.editor().addClass("redactor-structure"),this.opts.stylesClass&&this.core.editor().addClass(this.opts.stylesClass),"textarea"===this.opts.type&&(this.core.box().attr("dir",this.opts.direction),this.core.editor().attr("dir",this.opts.direction),this.opts.tabindex&&this.core.editor().attr("tabindex",this.opts.tabindex),this.opts.minHeight?this.core.editor().css("min-height",this.opts.minHeight):this.core.editor().css("min-height","40px"),this.opts.maxHeight&&this.core.editor().css("max-height",this.opts.maxHeight),this.opts.maxWidth&&this.core.editor().css({"max-width":this.opts.maxWidth,margin:"auto"}))},callEditor:function(){this.build.disableBrowsersEditing(),this.events.init(),this.build.setHelpers(),(this.opts.toolbar||this.opts.air)&&(this.toolbarsButtons=this.button.init()),this.opts.air?this.air.build():this.opts.toolbar&&this.toolbar.build(),this.detect.isMobile()&&this.opts.toolbarMobile&&this.opts.air&&(this.opts.toolbar=!0,this.toolbar.build()),(this.opts.air||this.opts.toolbar)&&(this.core.editor().on("mouseup.redactor-observe."+this.uuid+" keyup.redactor-observe."+this.uuid+" focus.redactor-observe."+this.uuid+" touchstart.redactor-observe."+this.uuid,t.proxy(this.observe.toolbar,this)),this.core.element().on("blur.callback.redactor",t.proxy(function(){this.button.setInactiveAll()},this))),this.modal.templates(),this.build.plugins(),this.autosave.init(),this.code.html=this.code.cleaned(this.core.editor().html()),this.core.callback("init"),this.core.callback("initToEdit"),this.storage.observe(),this.start=!1},setHelpers:function(){this.opts.linkify&&this.linkify.format(),this.placeholder.init(),this.opts.focus?setTimeout(this.focus.start,100):this.opts.focusEnd&&setTimeout(this.focus.end,100)},disableBrowsersEditing:function(){try{document.execCommand("enableObjectResizing",!1,!1),document.execCommand("enableInlineTableEditing",!1,!1),document.execCommand("AutoUrlDetect",!1,!1)}catch(t){}},plugins:function(){this.opts.plugins&&t.each(this.opts.plugins,t.proxy(function(i,r){var o="undefined"!=typeof RedactorPlugins&&void 0!==RedactorPlugins[r]?RedactorPlugins:e.fn;if(t.isFunction(o[r])){this[r]=o[r]();for(var s=this.getModuleMethods(this[r]),n=s.length,a=0;a<n;a++)this[r][s[a]]=this[r][s[a]].bind(this);if(void 0!==this[r].langs){var l={};void 0!==this[r].langs[this.opts.lang]?l=this[r].langs[this.opts.lang]:void 0===this[r].langs[this.opts.lang]&&void 0!==this[r].langs.en&&(l=this[r].langs.en);var c=this;t.each(l,function(t,e){void 0===c.opts.curLang[t]&&(c.opts.curLang[t]=e)})}t.isFunction(this[r].init)&&this[r].init()}},this))}}},button:function(){return{toolbar:function(){return void 0!==this.button.$toolbar&&this.button.$toolbar?this.button.$toolbar:this.$toolbar},init:function(){return{format:{title:this.lang.get("format"),icon:!0,dropdown:{p:{title:this.lang.get("paragraph"),func:"block.format"},blockquote:{title:this.lang.get("quote"),func:"block.format"},pre:{title:this.lang.get("code"),func:"block.format"},h1:{title:this.lang.get("heading1"),func:"block.format"},h2:{title:this.lang.get("heading2"),func:"block.format"},h3:{title:this.lang.get("heading3"),func:"block.format"},h4:{title:this.lang.get("heading4"),func:"block.format"},h5:{title:this.lang.get("heading5"),func:"block.format"},h6:{title:this.lang.get("heading6"),func:"block.format"}}},bold:{title:this.lang.get("bold-abbr"),icon:!0,label:this.lang.get("bold"),func:"inline.format"},italic:{title:this.lang.get("italic-abbr"),icon:!0,label:this.lang.get("italic"),func:"inline.format"},deleted:{title:this.lang.get("deleted-abbr"),icon:!0,label:this.lang.get("deleted"),func:"inline.format"},underline:{title:this.lang.get("underline-abbr"),icon:!0,label:this.lang.get("underline"),func:"inline.format"},lists:{title:this.lang.get("lists"),icon:!0,dropdown:{unorderedlist:{title:"• "+this.lang.get("unorderedlist"),func:"list.toggle"},orderedlist:{title:"1. "+this.lang.get("orderedlist"),func:"list.toggle"},outdent:{title:"< "+this.lang.get("outdent"),func:"indent.decrease",observe:{element:"li",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},indent:{title:"> "+this.lang.get("indent"),func:"indent.increase",observe:{element:"li",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}}}},ul:{title:"• "+this.lang.get("bulletslist"),icon:!0,func:"list.toggle"},ol:{title:"1. "+this.lang.get("numberslist"),icon:!0,func:"list.toggle"},outdent:{title:this.lang.get("outdent"),icon:!0,func:"indent.decrease"},indent:{title:this.lang.get("indent"),icon:!0,func:"indent.increase"},image:{title:this.lang.get("image"),icon:!0,func:"image.show"},file:{title:this.lang.get("file"),icon:!0,func:"file.show"},link:{title:this.lang.get("link"),icon:!0,dropdown:{link:{title:this.lang.get("link-insert"),func:"link.show",observe:{element:"a",in:{title:this.lang.get("link-edit")},out:{title:this.lang.get("link-insert")}}},unlink:{title:this.lang.get("unlink"),func:"link.unlink",observe:{element:"a",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}}}},horizontalrule:{title:this.lang.get("horizontalrule"),icon:!0,func:"line.insert"}}},setFormatting:function(){t.each(this.toolbarsButtons.format.dropdown,t.proxy(function(e,i){-1===t.inArray(e,this.opts.formatting)&&delete this.toolbarsButtons.format.dropdown[e]},this))},hideButtons:function(){0!==this.opts.buttonsHide.length&&this.button.hideButtonsSlicer(this.opts.buttonsHide)},hideButtonsOnMobile:function(){this.detect.isMobile()&&0!==this.opts.buttonsHideOnMobile.length&&this.button.hideButtonsSlicer(this.opts.buttonsHideOnMobile)},hideButtonsSlicer:function(e){t.each(e,t.proxy(function(t,e){var i=this.opts.buttons.indexOf(e);-1!==i&&this.opts.buttons.splice(i,1)},this))},load:function(e){this.button.buttons=[],t.each(this.opts.buttons,t.proxy(function(i,r){!this.toolbarsButtons[r]||"file"===r&&!this.file.is()||"image"===r&&!this.image.is()||e.append(t("<li>").append(this.button.build(r,this.toolbarsButtons[r])))},this))},buildButtonTooltip:function(e,i){if(void 0!==this.button.toolbar()&&!this.opts.air&&!this.detect.isMobile()){var r=t("<span>");r.addClass("re-button-tooltip"),r.html(i);var o=this,s=this.button.toolbar(),n=s.closest(".redactor-toolbar-box");n=0===n.length?s:n,n.prepend(r),e.on("mouseover",function(){if(!t(this).hasClass("redactor-button-disabled")){var i=s.hasClass("toolbar-fixed-box")?e.offset():e.position();i=o.opts.toolbarFixedTarget!==document?e.position():i;var n=s.hasClass("toolbar-fixed-box")?e.position().top:i.top,a=e.innerHeight(),l=e.innerWidth(),c=s.hasClass("toolbar-fixed-box")?"fixed":"absolute";c=o.opts.toolbarFixedTarget!==document?"absolute":c;var d=o.opts.toolbarFixedTarget!==document?s.position().top:0;r.show(),r.css({top:n+a+d+"px",left:i.left+l/2-r.innerWidth()/2+"px",position:c})}}).on("mouseout",function(){r.hide()})}},build:function(e,i){if(!1!==this.opts.toolbar){var r=void 0!==i.label?i.label:i.title,o=t('<a href="javascript:void(null);" alt="'+r+'" rel="'+e+'" />');if(o.addClass("re-button re-"+e),o.attr({role:"button","aria-label":r,tabindex:"-1"}),void 0===i.icon||this.opts.buttonsTextLabeled)o.html(i.title);else{var s=t("<i>");s.addClass("re-icon-"+e),o.append(s),o.addClass("re-button-icon"),this.button.buildButtonTooltip(o,r)}if((i.func||i.command||i.dropdown)&&this.button.setEvent(o,e,i),i.dropdown){o.addClass("redactor-toolbar-link-dropdown").attr("aria-haspopup",!0);var n=t('<ul class="redactor-dropdown redactor-dropdown-'+this.uuid+" redactor-dropdown-box-"+e+'" style="display: none;">');o.data("dropdown",n),this.dropdown.build(e,n,i.dropdown)}return this.button.buttons.push(o),o}},getButtons:function(){return this.button.toolbar().find("a.re-button")},getButtonsKeys:function(){return this.button.buttons},setEvent:function(e,i,r){e.on("mousedown",t.proxy(function(t){if(t.preventDefault(),e.hasClass("redactor-button-disabled"))return!1;var o="func",s=r.func;return r.command?(o="command",s=r.command):r.dropdown&&(o="dropdown",s=!1),this.button.toggle(t,i,o,s),!1},this))},toggle:function(t,e,i,r,o){!this.detect.isIe()&&this.detect.isDesktop()||(this.utils.freezeScroll(),t.returnValue=!1),"command"===i?this.inline.format(r):"dropdown"===i?this.dropdown.show(t,e):this.button.clickCallback(t,r,e,o),"dropdown"!==i&&this.dropdown.hideAll(!1),this.opts.air&&"dropdown"!==i&&this.air.hide(t),!this.detect.isIe()&&this.detect.isDesktop()||this.utils.unfreezeScroll()},clickCallback:function(e,i,r,o){var s;if(o=void 0===o?r:o,t.isFunction(i))i.call(this,r);else if("-1"!==i.search(/\./)){if(s=i.split("."),void 0===this[s[0]])return;"object"==typeof o?this[s[0]][s[1]].apply(this,o):this[s[0]][s[1]].call(this,o)}else"object"==typeof o?this[i].apply(this,o):this[i].call(this,o);this.observe.buttons(e,r)},all:function(){return this.button.buttons},get:function(t){if(!1!==this.opts.toolbar)return this.button.toolbar().find("a.re-"+t)},set:function(t,e){if(!1!==this.opts.toolbar){var i=this.button.toolbar().find("a.re-"+t);return i.html(e).attr("aria-label",e),i}},add:function(e,i){if(!0!==this.button.isAdded(e))return t();var r=this.button.build(e,{title:i});return this.button.toolbar().append(t("<li>").append(r)),r},addFirst:function(e,i){if(!0!==this.button.isAdded(e))return t();var r=this.button.build(e,{title:i});return this.button.toolbar().prepend(t("<li>").append(r)),r},addAfter:function(e,i,r){if(!0!==this.button.isAdded(i))return t();var o=this.button.build(i,{title:r}),s=this.button.get(e);return 0!==s.length?s.parent().after(t("<li>").append(o)):this.button.toolbar().append(t("<li>").append(o)),o},addBefore:function(e,i,r){if(!0!==this.button.isAdded(i))return t();var o=this.button.build(i,{title:r}),s=this.button.get(e);return 0!==s.length?s.parent().before(t("<li>").append(o)):this.button.toolbar().append(t("<li>").append(o)),o},isAdded:function(t){var e=this.opts.buttonsHideOnMobile.indexOf(t);return!(!1===this.opts.toolbar||-1!==e&&this.detect.isMobile())},setIcon:function(t,e){this.opts.buttonsTextLabeled||(t.html(e).addClass("re-button-icon"),this.button.buildButtonTooltip(t,t.attr("alt")))},changeIcon:function(t,e){var i=this.button.get(t);0!==i.length&&i.find("i").removeAttr("class").addClass("re-icon-"+e)},addCallback:function(e,i){if(void 0!==e&&!1!==this.opts.toolbar){var r="dropdown"===i?"dropdown":"func",o=e.attr("rel");e.on("mousedown",t.proxy(function(t){if(e.hasClass("redactor-button-disabled"))return!1;this.button.toggle(t,o,r,i)},this))}},addDropdown:function(e,i){if(!1!==this.opts.toolbar){e.addClass("redactor-toolbar-link-dropdown").attr("aria-haspopup",!0);var r=e.attr("rel");this.button.addCallback(e,"dropdown");var o=t('<div class="redactor-dropdown redactor-dropdown-'+this.uuid+" redactor-dropdown-box-"+r+'" style="display: none;">');return e.data("dropdown",o),i&&this.dropdown.build(r,o,i),o}},setActive:function(t){this.button.get(t).addClass("redactor-act")},setInactive:function(t){this.button.get(t).removeClass("redactor-act")},setInactiveAll:function(t){var e=this.button.toolbar().find("a.re-button");void 0!==t&&(e=e.not(".re-"+t)),e.removeClass("redactor-act")},disable:function(t){this.button.get(t).addClass("redactor-button-disabled")},enable:function(t){this.button.get(t).removeClass("redactor-button-disabled")},disableAll:function(t){var e=this.button.toolbar().find("a.re-button");void 0!==t&&(e=e.not(".re-"+t)),e.addClass("redactor-button-disabled")},enableAll:function(){this.button.toolbar().find("a.re-button").removeClass("redactor-button-disabled")},remove:function(t){this.button.get(t).remove()}}},caret:function(){return{set:function(t,e,i){var r=this.core.editor().scrollTop();this.core.editor().focus(),this.core.editor().scrollTop(r),i=void 0===i?0:1,t=t[0]||t,e=e[0]||e;var o=this.selection.get(),s=this.selection.range(o);try{s.setStart(t,0),s.setEnd(e,i)}catch(t){}this.selection.update(o,s)},prepare:function(t){return this.detect.isFirefox()&&void 0!==this.start&&this.core.editor().focus(),t[0]||t},start:function(e){var i,r;if(e=this.caret.prepare(e)){if("BR"===e.tagName)return this.caret.before(e);var o=t(e).children().first(),s=this.utils.isInlineTag(e.tagName);""===e.innerHTML||s?this.caret.setStartEmptyOrInline(e,s):o&&0!==o.length&&this.utils.isInlineTag(o[0].tagName)&&""===o.text()?this.caret.setStartEmptyOrInline(o[0],!0):(i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.selectNodeContents(e),r.collapse(!0),i.addRange(r))}},setStartEmptyOrInline:function(e,i){var r=window.getSelection(),o=document.createRange(),s=document.createTextNode("");o.setStart(e,0),o.insertNode(s),o.setStartAfter(s),
-o.collapse(!0),r.removeAllRanges(),r.addRange(o),i||this.core.editor().on("keydown.redactor-remove-textnode",function(){t(s).remove(),t(this).off("keydown.redactor-remove-textnode")})},end:function(e){var i,r;if(e=this.caret.prepare(e)){if("BR"!==e.tagName&&""===e.innerHTML)return this.caret.start(e);if("BR"===e.tagName){var o=document.createElement("span");return o.className="redactor-invisible-space",o.innerHTML="​",t(e).after(o),i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.setStartBefore(o),r.setEndBefore(o),i.addRange(r),void t(o).replaceWith(function(){return t(this).contents()})}if(e.lastChild&&1===e.lastChild.nodeType)return this.caret.after(e.lastChild);var i=window.getSelection();if(i.getRangeAt||i.rangeCount)try{var r=i.getRangeAt(0);r.selectNodeContents(e),r.collapse(!1),i.removeAllRanges(),i.addRange(r)}catch(t){}}},after:function(e){var i,r;if(e=this.caret.prepare(e)){if("BR"===e.tagName)return this.caret.end(e);if(this.utils.isBlockTag(e.tagName)){var o=this.caret.next(e);return void(void 0===o?this.caret.end(e):("TABLE"===o.tagName?o=t(o).find("th, td").first()[0]:"UL"!==o.tagName&&"OL"!==o.tagName||(o=t(o).find("li").first()[0]),this.caret.start(o)))}var s=document.createTextNode("");i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.setStartAfter(e),r.insertNode(s),r.setStartAfter(s),r.collapse(!0),i.addRange(r)}},before:function(e){var i,r;if(e=this.caret.prepare(e)){if(this.utils.isBlockTag(e.tagName)){var o=this.caret.prev(e);return void(void 0===o?this.caret.start(e):("TABLE"===o.tagName?o=t(o).find("th, td").last()[0]:"UL"!==o.tagName&&"OL"!==o.tagName||(o=t(o).find("li").last()[0]),this.caret.end(o)))}i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.setStartBefore(e),r.collapse(!0),i.addRange(r)}},next:function(e){var i=t(e).next();return i.hasClass("redactor-script-tag, redactor-selection-marker")?i.next()[0]:i[0]},prev:function(e){var i=t(e).prev();return i.hasClass("redactor-script-tag, redactor-selection-marker")?i.prev()[0]:i[0]},offset:function(t){return this.offset.get(t)}}},clean:function(){return{onSet:function(e){e=this.clean.savePreCode(e),e=this.clean.saveFormTags(e),this.opts.script&&(e=e.replace(/<script(.*?[^>]?)>([\w\W]*?)<\/script>/gi,'<pre class="redactor-script-tag" $1>$2</pre>')),e=e.replace(/\$/g,"$"),e=e.replace(/&/g,"&"),e=e.replace(/<a href="(.*?[^>]?)®(.*?[^>]?)">/gi,'<a href="$1®$2">'),e=e.replace(/<span id="selection-marker-1"(.*?[^>]?)><\/span>/gi,"###marker1###"),e=e.replace(/<span id="selection-marker-2"(.*?[^>]?)><\/span>/gi,"###marker2###");var i=this,r=t("<div/>").html(t.parseHTML(e,document,!0)),o=this.opts.replaceTags;if(o){var s=Object.keys(this.opts.replaceTags);r.find(s.join(",")).each(function(t,e){i.utils.replaceToTag(e,o[e.tagName.toLowerCase()])})}r.find("span, a").attr("data-redactor-span",!0),r.find(this.opts.inlineTags.join(",")).each(function(){var e=t(this);e.attr("style")&&e.attr("data-redactor-style-cache",e.attr("style"))}),e=r.html();var n=["font","html","head","link","body","meta","applet"];return this.opts.script||n.push("script"),e=this.clean.stripTags(e,n),this.opts.removeComments&&(e=e.replace(/<!--[\s\S]*?-->/gi,"")),e=this.paragraphize.load(e),e=e.replace("###marker1###",'<span id="selection-marker-1" class="redactor-selection-marker"></span>'),e=e.replace("###marker2###",'<span id="selection-marker-2" class="redactor-selection-marker"></span>'),-1!==e.search(/^(||\s||<br\s?\/?>|| )$/i)?this.opts.emptyHtml:e},onGet:function(t){return this.clean.onSync(t)},onSync:function(e){if(e=e.replace(/\u200B/g,""),e=e.replace(/​/gi,""),-1!==e.search(/^<p>(||\s||<br\s?\/?>|| )<\/p>$/i))return"";e=e.replace(/<span(.*?)id="redactor-image-box"(.*?[^>])>([\w\W]*?)<img(.*?)><\/span>/gi,"$3<img$4>"),e=e.replace(/<span(.*?)id="redactor-image-resizer"(.*?[^>])>(.*?)<\/span>/gi,""),e=e.replace(/<span(.*?)id="redactor-image-editter"(.*?[^>])>(.*?)<\/span>/gi,""),e=e.replace(/<img(.*?)style="(.*?)opacity: 0\.5;(.*?)"(.*?)>/gi,'<img$1style="$2$3"$4>');var i=t("<div/>").html(t.parseHTML(e,document,!0));i.find('*[style=""]').removeAttr("style"),i.find('*[class=""]').removeAttr("class"),i.find('*[rel=""]').removeAttr("rel"),i.find('*[data-image=""]').removeAttr("data-image"),i.find('*[alt=""]').removeAttr("alt"),i.find('*[title=""]').removeAttr("title"),i.find("*[data-redactor-style-cache]").removeAttr("data-redactor-style-cache"),i.find(".redactor-invisible-space, .redactor-unlink").each(function(){t(this).contents().unwrap()}),i.find("span, a").removeAttr("data-redactor-span data-redactor-style-cache").each(function(){0===this.attributes.length&&t(this).contents().unwrap()}),i.find("img").removeAttr("rel"),i.find(".redactor-selection-marker, #redactor-insert-marker").remove(),e=i.html(),this.opts.script&&(e=e.replace(/<pre class="redactor-script-tag"(.*?[^>]?)>([\w\W]*?)<\/pre>/gi,"<script$1>$2<\/script>")),e=this.clean.restoreFormTags(e),e=e.replace(new RegExp("<br\\s?/?></h","gi"),"</h"),e=e.replace(new RegExp("<br\\s?/?></li>","gi"),"</li>"),e=e.replace(new RegExp("</li><br\\s?/?>","gi"),"</li>"),e=e.replace(/<pre>/gi,"<pre>\n"),this.opts.preClass&&(e=e.replace(/<pre>/gi,'<pre class="'+this.opts.preClass+'">')),this.opts.linkNofollow&&(e=e.replace(/<a(.*?)rel="nofollow"(.*?[^>])>/gi,"<a$1$2>"),e=e.replace(/<a(.*?[^>])>/gi,'<a$1 rel="nofollow">'));var r={"™":"™","©":"©","…":"…","—":"—","‐":"‐"};return t.each(r,function(t,i){e=e.replace(new RegExp(t,"g"),i)}),e=e.replace(/&/g,"&"),e=e.replace(/\n{2,}/g,"\n"),this.opts.removeNewlines&&(e=e.replace(/\r?\n/g,"")),e},onPaste:function(e,i,r){if(!0!==r){e=e.replace(/<b\sid="internal-source-marker(.*?)">([\w\W]*?)<\/b>/gi,"$2"),e=e.replace(/<b(.*?)id="docs-internal-guid(.*?)">([\w\W]*?)<\/b>/gi,"$3"),e=e.replace(/<span[^>]*(font-style: italic; font-weight: bold|font-weight: bold; font-style: italic)[^>]*>([\w\W]*?)<\/span>/gi,"<b><i>$2</i></b>"),e=e.replace(/<span[^>]*(font-style: italic; font-weight: 700|font-weight: 700; font-style: italic)[^>]*>([\w\W]*?)<\/span>/gi,"<b><i>$2</i></b>"),e=e.replace(/<span[^>]*font-style: italic[^>]*>([\w\W]*?)<\/span>/gi,"<i>$1</i>"),e=e.replace(/<span[^>]*font-weight: bold[^>]*>([\w\W]*?)<\/span>/gi,"<b>$1</b>"),e=e.replace(/<span[^>]*font-weight: 700[^>]*>([\w\W]*?)<\/span>/gi,"<b>$1</b>"),e=e.replace(/<o:p[^>]*>/gi,""),e=e.replace(/<\/o:p>/gi,"");this.clean.isHtmlMsWord(e)&&(e=this.clean.cleanMsWord(e))}return e=t.trim(e),i.pre?this.opts.preSpaces&&(e=e.replace(/\t/g,new Array(this.opts.preSpaces+1).join(" "))):(e=this.clean.replaceBrToNl(e),e=this.clean.removeTagsInsidePre(e)),!0!==r&&(e=this.clean.removeEmptyInlineTags(e),!1===i.encode&&(e=e.replace(/&/g,"&"),e=this.clean.convertTags(e,i),e=this.clean.getPlainText(e),e=this.clean.reconvertTags(e,i))),i.text&&(e=this.clean.replaceNbspToSpaces(e),e=this.clean.getPlainText(e)),i.lists&&(e=e.replace("\n","<br>")),i.encode&&(e=this.clean.encodeHtml(e)),i.paragraphize&&(e=e.replace(/ \n/g," "),e=e.replace(/\n /g," "),e=this.paragraphize.load(e),e=e.replace(/<p><\/p>/g,"")),e=e.replace(/<li><p>/g,"<li>"),e=e.replace(/<\/p><\/li>/g,"</li>")},getCurrentType:function(t,e){var i=this.selection.blocks(),r={text:!1,encode:!1,paragraphize:!0,line:this.clean.isHtmlLine(t),blocks:this.clean.isHtmlBlocked(t),pre:!1,lists:!1,block:!0,inline:!0,links:!0,images:!0};return 1===i.length&&this.utils.isCurrentOrParent(["h1","h2","h3","h4","h5","h6","a","figcaption"])?(r.text=!0,r.paragraphize=!1,r.inline=!1,r.images=!1,r.links=!1,r.line=!0):"inline"===this.opts.type||!1===this.opts.enterKey?(r.paragraphize=!1,r.block=!1,r.line=!0):1===i.length&&this.utils.isCurrentOrParent(["li"])?(r.lists=!0,r.block=!1,r.paragraphize=!1,r.images=!1):1===i.length&&this.utils.isCurrentOrParent(["th","td","blockquote"])?(r.block=!1,r.paragraphize=!1):("pre"===this.opts.type||1===i.length&&this.utils.isCurrentOrParent("pre"))&&(r.inline=!1,r.block=!1,r.encode=!0,r.pre=!0,r.paragraphize=!1,r.images=!1,r.links=!1),!0===r.line&&(r.paragraphize=!1),!0===e&&(r.text=!1),r},isHtmlBlocked:function(t){var e=t.match(new RegExp("</("+this.opts.blockTags.join("|").toUpperCase()+")>","gi")),i=t.match(new RegExp("<hr(.*?[^>])>","gi"));return null!==e||null!==i},isHtmlLine:function(t){if(this.clean.isHtmlBlocked(t))return!1;var e=t.match(/<br\s?\/?>/gi),i=t.match(/\n/gi);return!e&&!i},isHtmlMsWord:function(t){return t.match(/class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i)},removeEmptyInlineTags:function(e){var i=this.opts.inlineTags,r=t("<div/>").html(t.parseHTML(e,document,!0)),o=this,s=r.find("span"),n=r.find(i.join(","));return n.removeAttr("style"),n.each(function(){var e=t(this).html();0===this.attributes.length&&o.utils.isEmpty(e)&&t(this).replaceWith(function(){return t(this).contents()})}),s.each(function(){t(this).html();0===this.attributes.length&&t(this).replaceWith(function(){return t(this).contents()})}),e=r.html(),e=e.replace("\x3c!--?php","<?php"),e=e.replace("\x3c!--?","<?"),e=e.replace("?--\x3e","?>"),r.remove(),e},cleanMsWord:function(e){e=e.replace(/<!--[\s\S]*?-->/g,""),e=e.replace(/<o:p>[\s\S]*?<\/o:p>/gi,""),e=e.replace(/\n/g," "),e=e.replace(/<br\s?\/?>|<\/p>|<\/div>|<\/li>|<\/td>/gi,"\n\n");var i=t("<div/>").html(e),r=!1,o=1,s=[];return i.find("p[style]").each(function(){var e=t(this).attr("style").match(/mso\-list\:l([0-9]+)\slevel([0-9]+)/);if(e){var n=parseInt(e[1]),a=parseInt(e[2]),l=t(this).html().match(/^[\w]+\./)?"ol":"ul",c=t("<li/>").html(t(this).html());if(c.html(c.html().replace(/^([\w\.]+)</,"<")),c.find("span:first").remove(),1==a&&-1==t.inArray(n,s)){var d=t("<"+l+"/>").attr({"data-level":a,"data-list":n}).html(c);t(this).replaceWith(d),r=n,s.push(n)}else{if(a>o){for(var h=i.find('[data-level="'+o+'"][data-list="'+r+'"]'),u=h,p=o;p<a;p++)d=t("<"+l+"/>"),d.appendTo(u.find("li").last()),u=d;u.attr({"data-level":a,"data-list":n}).html(c)}else{var h=i.find('[data-level="'+a+'"][data-list="'+n+'"]').last();h.append(c)}o=a,r=n,t(this).remove()}}}),i.find("[data-level][data-list]").removeAttr("data-level data-list"),e=i.html()},replaceNbspToSpaces:function(t){return t.replace(" "," ")},replaceBrToNl:function(t){return t.replace(/<br\s?\/?>/gi,"\n")},replaceNlToBr:function(t){return t.replace(/\n/g,"<br />")},convertTags:function(e,i){var r=t("<div>").html(e);r.find("iframe").remove();var o=r.find("a");if(o.removeAttr("style"),!1!==this.opts.pasteLinkTarget&&o.attr("target",this.opts.pasteLinkTarget),i.links&&this.opts.pasteLinks&&r.find("a").each(function(t,e){if(e.href){for(var i,r='#####[a href="'+e.href+'"',o=0,s=e.attributes.length;o<s;o++)i=e.attributes.item(o),"href"!==i.name&&(r+=" "+i.name+'="'+i.value+'"');e.outerHTML=r+"]#####"+e.innerHTML+"#####[/a]#####"}}),e=r.html(),i.images&&this.opts.pasteImages&&(e=e.replace(/<img(.*?)src="(.*?)"(.*?[^>])>/gi,'#####[img$1src="$2"$3]#####')),this.opts.pastePlainText)return e;var s,n=i.lists?["ul","ol","li"]:this.opts.pasteBlockTags;s=i.block||i.lists?i.inline?n.concat(this.opts.pasteInlineTags):n:i.inline?this.opts.pasteInlineTags:[];for(var a=s.length,l=0;l<a;l++)e=e.replace(new RegExp("</"+s[l]+">","gi"),"###/"+s[l]+"###"),"td"===s[l]||"th"===s[l]?e=e.replace(new RegExp("<"+s[l]+'(.*?[^>])((colspan|rowspan)="(.*?[^>])")?(.*?[^>])>',"gi"),"###"+s[l]+" $2###"):this.utils.isInlineTag(s[l])?(e=e.replace(new RegExp("<"+s[l]+'([^>]*)class="([^>]*)"[^>]*>',"gi"),"###"+s[l]+' class="$2"###'),e=e.replace(new RegExp("<"+s[l]+'([^>]*)data-redactor-style-cache="([^>]*)"[^>]*>',"gi"),"###"+s[l]+' cache="$2"###'),e=e.replace(new RegExp("<"+s[l]+"[^>]*>","gi"),"###"+s[l]+"###")):e=e.replace(new RegExp("<"+s[l]+"[^>]*>","gi"),"###"+s[l]+"###");return e},reconvertTags:function(t,e){if((e.links&&this.opts.pasteLinks||e.images&&this.opts.pasteImages)&&(t=t.replace(new RegExp("#####\\[","gi"),"<"),t=t.replace(new RegExp("\\]#####","gi"),">")),this.opts.pastePlainText)return t;var i,r=e.lists?["ul","ol","li"]:this.opts.pasteBlockTags;i=e.block||e.lists?e.inline?r.concat(this.opts.pasteInlineTags):r:e.inline?this.opts.pasteInlineTags:[];for(var o=i.length,s=0;s<o;s++)t=t.replace(new RegExp("###/"+i[s]+"###","gi"),"</"+i[s]+">");for(var s=0;s<o;s++)t=t.replace(new RegExp("###"+i[s]+"###","gi"),"<"+i[s]+">");for(var s=0;s<o;s++)if("td"===i[s]||"th"===i[s])t=t.replace(new RegExp("###"+i[s]+"s?(.*?[^#])###","gi"),"<"+i[s]+"$1>");else if(this.utils.isInlineTag(i[s])){var n="span"===i[s]?' data-redactor-span="true"':"";t=t.replace(new RegExp("###"+i[s]+' cache="(.*?[^#])"###',"gi"),"<"+i[s]+' style="$1"'+n+' data-redactor-style-cache="$1">'),t=t.replace(new RegExp("###"+i[s]+"s?(.*?[^#])###","gi"),"<"+i[s]+"$1>")}return t},cleanPre:function(e){e=void 0===e?t(this.selection.block()).closest("pre",this.core.editor()[0]):e,t(e).find("br").replaceWith(function(){return document.createTextNode("\n")}),t(e).find("p").replaceWith(function(){return t(this).contents()})},removeTagsInsidePre:function(e){var i=t("<div />").append(e);return i.find("pre").replaceWith(function(){var e=t(this).html();return e=e.replace(/<br\s?\/?>|<\/p>|<\/div>|<\/li>|<\/td>/gi,"\n"),e=e.replace(/(<([^>]+)>)/gi,""),t("<pre />").append(e)}),e=i.html(),i.remove(),e},getPlainText:function(e){e=e.replace(/<!--[\s\S]*?-->/gi,""),e=e.replace(/<style[\s\S]*?style>/gi,""),e=e.replace(/<p><\/p>/g,""),e=e.replace(/<\/div>|<\/li>|<\/td>/gi,"\n"),e=e.replace(/<\/p>/gi,"\n\n"),e=e.replace(/<\/H[1-6]>/gi,"\n\n");var i=document.createElement("div");return i.innerHTML=e,e=i.textContent||i.innerText,t.trim(e)},savePreCode:function(t){return t=this.clean.savePreFormatting(t),t=this.clean.saveCodeFormatting(t),t=this.clean.restoreSelectionMarkers(t)},savePreFormatting:function(e){var i=e.match(/<pre(.*?)>([\w\W]*?)<\/pre>/gi);return null===i?e:(t.each(i,t.proxy(function(t,i){var r,o,s,n=[],a=!1;i.match(/<pre(.*?)>(([\n\r\s]+)?)<code(.*?)>/i)?(n=i.match(/<pre(.*?)>(([\n\r\s]+)?)<code(.*?)>([\w\W]*?)<\/code>(([\n\r\s]+)?)<\/pre>/i),a=!0,r=n[5],o=n[1],s=n[4]):(n=i.match(/<pre(.*?)>([\w\W]*?)<\/pre>/i),r=n[2],o=n[1]),r=r.replace(/<br\s?\/?>/g,"\n"),r=r.replace(/ /g," "),this.opts.preSpaces&&(r=r.replace(/\t/g,new Array(this.opts.preSpaces+1).join(" "))),r=this.clean.encodeEntities(r),r=r.replace(/\$/g,"$"),e=a?e.replace(i,"<pre"+o+"><code"+s+">"+r+"</code></pre>"):e.replace(i,"<pre"+o+">"+r+"</pre>")},this)),e)},saveCodeFormatting:function(e){var i=e.match(/<code(.*?)>([\w\W]*?)<\/code>/gi);return null===i?e:(t.each(i,t.proxy(function(t,i){var r=i.match(/<code(.*?)>([\w\W]*?)<\/code>/i);r[2]=r[2].replace(/ /g," "),r[2]=this.clean.encodeEntities(r[2]),r[2]=r[2].replace(/\$/g,"$"),e=e.replace(i,"<code"+r[1]+">"+r[2]+"</code>")},this)),e)},restoreSelectionMarkers:function(t){return t=t.replace(/<span id="selection-marker-([0-9])" class="redactor-selection-marker"><\/span>/g,'<span id="selection-marker-$1" class="redactor-selection-marker"></span>')},saveFormTags:function(t){return t.replace(/<form(.*?)>([\w\W]*?)<\/form>/gi,'<section$1 rel="redactor-form-tag">$2</section>')},restoreFormTags:function(t){return t.replace(/<section(.*?) rel="redactor-form-tag"(.*?)>([\w\W]*?)<\/section>/gi,"<form$1$2>$3</form>")},encodeHtml:function(t){return t=t.replace(/”/g,'"'),t=t.replace(/“/g,'"'),t=t.replace(/‘/g,"'"),t=t.replace(/’/g,"'"),t=this.clean.encodeEntities(t)},encodeEntities:function(t){return t=String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"'),t=t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},stripTags:function(t,e){if(void 0===e)return t.replace(/(<([^>]+)>)/gi,"");var i=/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi;return t.replace(i,function(t,i){return-1===e.indexOf(i.toLowerCase())?t:""})},removeMarkers:function(t){return t.replace(/<span(.*?[^>]?)class="redactor-selection-marker"(.*?[^>]?)>([\w\W]*?)<\/span>/gi,"")},removeSpaces:function(e){return e=t.trim(e),e=e.replace(/\n/g,""),e=e.replace(/[\t]*/g,""),e=e.replace(/\n\s*\n/g,"\n"),e=e.replace(/^[\s\n]*/g," "),e=e.replace(/[\s\n]*$/g," "),e=e.replace(/>\s{2,}</g,"> <"),e=e.replace(/\n\n/g,"\n"),e=e.replace(/\u200B/g,"")},removeSpacesHard:function(e){return e=t.trim(e),e=e.replace(/\n/g,""),e=e.replace(/[\t]*/g,""),e=e.replace(/\n\s*\n/g,"\n"),e=e.replace(/^[\s\n]*/g,""),e=e.replace(/[\s\n]*$/g,""),e=e.replace(/>\s{2,}</g,"><"),e=e.replace(/\n\n/g,"\n"),e=e.replace(/\u200B/g,"")},normalizeCurrentHeading:function(){var t=this.selection.block();this.utils.isCurrentOrParentHeader()&&t&&t.normalize()}}},code:function(){return{syncFire:!0,html:!1,start:function(e){e=t.trim(e),e=e.replace(/^(<span id="selection-marker-1" class="redactor-selection-marker"><\/span>)/,""),"textarea"===this.opts.type?e=this.clean.onSet(e):"div"===this.opts.type&&""===e&&(e=this.opts.emptyHtml),e=e.replace(/<p><span id="selection-marker-1" class="redactor-selection-marker"><\/span><\/p>/,""),this.events.stopDetectChanges(),this.core.editor().html(e),this.observe.load(),this.events.startDetectChanges()},set:function(e,i){e=t.trim(e),i=i||{},i.start&&(this.start=i.start),"textarea"===this.opts.type?e=this.clean.onSet(e):"div"===this.opts.type&&""===e&&(e=this.opts.emptyHtml),this.core.editor().html(e),"textarea"===this.opts.type&&this.code.sync(),this.placeholder.enable()},get:function(){if("textarea"===this.opts.type)return this.core.textarea().val();var t=this.core.editor().html();return t=this.clean.onGet(t)},sync:function(){if(this.code.syncFire){var e=this.core.editor().html(),i=this.code.cleaned(e);if(!this.code.isSync(i)){if(this.code.html=i,"textarea"!==this.opts.type)return this.core.callback("sync",e),void this.core.callback("change",e);"textarea"===this.opts.type&&setTimeout(t.proxy(function(){this.code.startSync(e)},this),10)}}},startSync:function(t){t=this.core.callback("syncBefore",t),t=this.clean.onSync(t),this.core.textarea().val(t),this.core.callback("sync",t),!1===this.start&&this.core.callback("change",t),this.start=!1},isSync:function(t){var e=!1!==this.code.html&&this.code.html;return!1!==e&&e===t},cleaned:function(t){return t=t.replace(/\u200B/g,""),this.clean.removeMarkers(t)}}},core:function(){return{id:function(){return this.$editor.attr("id")},element:function(){return this.$element},editor:function(){return void 0===this.$editor?t():this.$editor},textarea:function(){return this.$textarea},box:function(){return"textarea"===this.opts.type?this.$box:this.$element},toolbar:function(){return!!this.$toolbar&&this.$toolbar},air:function(){return!!this.$air&&this.$air},object:function(){return t.extend({},this)},structure:function(){this.core.editor().toggleClass("redactor-structure")},addEvent:function(t){this.core.event=t},getEvent:function(){return this.core.event},callback:function(e,i,r){var o=!1,s=t._data(this.core.element()[0],"events");if(void 0!==s&&void 0!==s[e])for(var n=s[e].length,a=0;a<n;a++){var l=s[e][a].namespace;if("callback.redactor"===l){var c=s[e][a].handler,d=void 0===r?[i]:[i,r];o=void 0===d?c.call(this,i):c.call(this,i,d)}}if(o)return o;if(void 0===this.opts.callbacks[e])return void 0===r?i:r;var h=this.opts.callbacks[e];return t.isFunction(h)?void 0===r?h.call(this,i):h.call(this,i,r):void 0===r?i:r},destroy:function(){this.opts.destroyed=!0,this.core.callback("destroy"),this.placeholder.destroy(),this.progress.destroy(),t("#redactor-voice-"+this.uuid).remove(),this.core.editor().removeClass("redactor-in redactor-styles redactor-structure redactor-layer-img-edit"),this.core.editor().off("keydown.redactor-remove-textnode"),this.core.editor().off(".redactor-observe."+this.uuid),this.$element.off(".redactor").removeData("redactor"),this.core.editor().off(".redactor"),t(document).off(".redactor-dropdown"),t(document).off(".redactor-air."+this.uuid),t(document).off("mousedown.redactor-blur."+this.uuid),t(document).off("mousedown.redactor."+this.uuid),t(document).off("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid),t(window).off(".redactor-toolbar."+this.uuid),t(window).off("touchmove.redactor."+this.uuid),t("body").off("scroll.redactor."+this.uuid),t(this.opts.toolbarFixedTarget).off("scroll.redactor."+this.uuid);var e=this;!1!==this.opts.plugins&&t.each(this.opts.plugins,function(i,r){t(window).off(".redactor-plugin-"+r),t(document).off(".redactor-plugin-"+r),t("body").off(".redactor-plugin-"+r),e.core.editor().off(".redactor-plugin-"+r)}),this.$element.off("click.redactor-click-to-edit"),this.$element.removeClass("redactor-click-to-edit"),this.core.editor().removeClass("redactor-layer"),this.core.editor().removeAttr("contenteditable");var i=this.code.get();this.opts.toolbar&&this.$toolbar&&this.$toolbar.find("a").each(function(){var e=t(this);e.data("dropdown")&&(e.data("dropdown").remove(),e.data("dropdown",{}))}),"textarea"===this.opts.type&&(this.$box.after(this.$element),this.$box.remove(),this.$element.val(i).show()),this.opts.air&&this.$air.remove(),this.opts.toolbar&&this.$toolbar&&this.$toolbar.remove(),this.$modalBox&&this.$modalBox.remove(),this.$modalOverlay&&this.$modalOverlay.remove(),t(".redactor-link-tooltip").remove(),clearInterval(this.autosaveTimeout)}}},detect:function(){return{isWebkit:function(){return/webkit/.test(this.opts.userAgent)},isFirefox:function(){return this.opts.userAgent.indexOf("firefox")>-1},isIe:function(t){if(document.documentMode||/Edge/.test(navigator.userAgent))return"edge";var e;return e=RegExp("msie"+(isNaN(t)?"":"\\s"+t),"i").test(navigator.userAgent),e||(e=!!navigator.userAgent.match(/Trident.*rv[ :]*11\./)),e},isMobile:function(){return/(iPhone|iPod|BlackBerry|Android)/.test(navigator.userAgent)},isDesktop:function(){return!/(iPhone|iPod|iPad|BlackBerry|Android)/.test(navigator.userAgent)},isIpad:function(){return/iPad/.test(navigator.userAgent)}}},dropdown:function(){return{active:!1,button:!1,key:!1,position:[],getDropdown:function(){return this.dropdown.active},build:function(e,i,r){r=this.dropdown.buildFormatting(e,r),t.each(r,t.proxy(function(t,r){var o=this.dropdown.buildItem(t,r);this.observe.addDropdown(o,t,r),i.attr("rel",e).append(o)},this))},buildFormatting:function(e,i){return"format"!==e||!1===this.opts.formattingAdd?i:(t.each(this.opts.formattingAdd,t.proxy(function(t,e){var r=this.utils.isBlockTag(e.args[0])?"block":"inline";i[t]={func:"block"===r?"block.format":"inline.format",args:e.args,title:e.title}},this)),i)},buildItem:function(e,i){var r=t("<li />");if(void 0!==i.classname&&r.addClass(i.classname),-1!==e.search(/^divider/i))return r.addClass("redactor-dropdown-divider"),r;var o=t('<a href="#" class="redactor-dropdown-'+e+'" role="button" />'),s=t("<span />").html(i.title);return o.append(s),o.on("mousedown",t.proxy(function(t){t.preventDefault(),this.dropdown.buildClick(t,e,i)},this)),r.append(o),r},buildClick:function(e,i,r){if(!t(e.target).hasClass("redactor-dropdown-link-inactive")){var o=this.dropdown.buildCommand(r);void 0!==r.args?this.button.toggle(e,i,o.type,o.callback,r.args):this.button.toggle(e,i,o.type,o.callback)}},buildCommand:function(t){var e={};return e.type="func",e.callback=t.func,t.command?(e.type="command",e.callback=t.command):t.dropdown&&(e.type="dropdown",e.callback=t.dropdown),e},show:function(e,i){if(this.detect.isDesktop()&&this.core.editor().focus(),this.dropdown.hideAll(!1,i),this.dropdown.key=i,this.dropdown.button=this.button.get(this.dropdown.key),this.dropdown.button.hasClass("dropact"))return void this.dropdown.hide();this.dropdown.active=this.dropdown.button.data("dropdown").appendTo(document.body),this.core.callback("dropdownShow",{dropdown:this.dropdown.active,key:this.dropdown.key,button:this.dropdown.button}),this.button.setActive(this.dropdown.key),this.dropdown.button.addClass("dropact"),this.dropdown.getButtonPosition(),this.button.toolbar().hasClass("toolbar-fixed-box")&&this.detect.isDesktop()?this.dropdown.showIsFixedToolbar():this.dropdown.showIsUnFixedToolbar(),this.detect.isDesktop()&&!this.detect.isFirefox()&&(this.dropdown.active.on("mouseover.redactor-dropdown",t.proxy(this.utils.disableBodyScroll,this)),this.dropdown.active.on("mouseout.redactor-dropdown mousedown.redactor-dropdown",t.proxy(this.utils.enableBodyScroll,this))),e.stopPropagation()},showIsFixedToolbar:function(){var e=this.dropdown.button.position().top+this.dropdown.button.innerHeight()+this.opts.toolbarFixedTopOffset,i="fixed";this.opts.toolbarFixedTarget!==document&&(e=this.dropdown.button.innerHeight()+this.$toolbar.offset().top+this.opts.toolbarFixedTopOffset,i="absolute"),this.dropdown.active.css({position:i,left:this.dropdown.position.left+"px",top:e+"px"}).show(),this.dropdown.active.redactorAnimation("slideDown",{duration:.2},t.proxy(function(){this.dropdown.enableCallback(),this.dropdown.enableEvents()},this))},showIsUnFixedToolbar:function(){this.dropdown.active.css({position:"absolute",left:this.dropdown.position.left+"px",top:this.dropdown.button.innerHeight()+this.dropdown.position.top+"px"}).show(),this.dropdown.active.redactorAnimation(this.opts.animation?"slideDown":"show",{duration:.2},t.proxy(function(){this.dropdown.enableCallback(),this.dropdown.enableEvents()},this))},enableEvents:function(){t(document).on("mousedown.redactor-dropdown",t.proxy(this.dropdown.hideAll,this)),this.core.editor().on("touchstart.redactor-dropdown",t.proxy(this.dropdown.hideAll,this)),t(document).on("keyup.redactor-dropdown",t.proxy(this.dropdown.closeHandler,this))},enableCallback:function(){this.core.callback("dropdownShown",{dropdown:this.dropdown.active,key:this.dropdown.key,button:this.dropdown.button})},getButtonPosition:function(){this.dropdown.position=this.dropdown.button.offset();var e=this.dropdown.active.width();this.dropdown.position.left+e>t(document).width()&&(this.dropdown.position.left=Math.max(0,this.dropdown.position.left-e+parseInt(this.dropdown.button.innerWidth())))},closeHandler:function(t){t.which===this.keyCode.ESC&&(this.dropdown.hideAll(t),this.core.editor().focus())},hideAll:function(e,i){if(this.detect.isDesktop()&&this.utils.enableBodyScroll(),!1===e||0===t(e.target).closest(".redactor-dropdown").length){var r=void 0===i?this.button.toolbar().find("a.dropact"):this.button.toolbar().find("a.dropact").not(".re-"+i),o=void 0===i?t(".redactor-dropdown-"+this.uuid):t(".redactor-dropdown-"+this.uuid).not(".redactor-dropdown-box-"+i);0!==o.length&&(t(document).off(".redactor-dropdown"),this.core.editor().off(".redactor-dropdown"),t.each(o,t.proxy(function(e,i){var r=t(i);this.core.callback("dropdownHide",r),r.hide(),r.off("mouseover mouseout").off(".redactor-dropdown")},this)),r.removeClass("redactor-act dropact"))}},hide:function(){!1!==this.dropdown.active&&(this.detect.isDesktop()&&this.utils.enableBodyScroll(),this.dropdown.active.redactorAnimation(this.opts.animation?"slideUp":"hide",{duration:.2},t.proxy(function(){t(document).off(".redactor-dropdown"),this.core.editor().off(".redactor-dropdown"),this.dropdown.hideOut()},this)))},hideOut:function(){this.core.callback("dropdownHide",this.dropdown.active),this.dropdown.button.removeClass("redactor-act dropact"),this.dropdown.active.off("mouseover mouseout").off(".redactor-dropdown"),this.dropdown.button=!1,this.dropdown.key=!1,this.dropdown.active=!1}}},events:function(){return{focused:!1,blured:!0,dropImage:!1,stopChanges:!1,stopDetectChanges:function(){this.events.stopChanges=!0},startDetectChanges:function(){var t=this;setTimeout(function(){t.events.stopChanges=!1},1)},dragover:function(e){e.preventDefault(),e.stopPropagation(),"IMG"===e.target.tagName&&t(e.target).addClass("redactor-image-dragover")},dragleave:function(t){this.core.editor().find("img").removeClass("redactor-image-dragover")},drop:function(t){return t=t.originalEvent||t,this.core.editor().find("img").removeClass("redactor-image-dragover"),"inline"===this.opts.type||"pre"===this.opts.type?(t.preventDefault(),!1):void 0===window.FormData||!t.dataTransfer||(0===t.dataTransfer.files.length?this.events.onDrop(t):(this.events.onDropUpload(t),void this.core.callback("drop",t)))},click:function(t){var e=this.core.getEvent(),i="click"!==e&&"arrow"!==e&&"click";this.core.addEvent(i),this.utils.disableSelectAll(),this.core.callback("click",t)},focus:function(t){if(!this.rtePaste&&(this.events.isCallback("focus")&&this.core.callback("focus",t),this.events.focused=!0,this.events.blured=!1,!1===this.selection.current())){var e=this.selection.get(),i=this.selection.range(e);i.setStart(this.core.editor()[0],0),i.setEnd(this.core.editor()[0],0),this.selection.update(e,i)}},blur:function(e){this.start||this.rtePaste||0===t(e.target).closest("#"+this.core.id()+", .redactor-toolbar, .redactor-dropdown, #redactor-modal-box").length&&(!this.events.blured&&this.events.isCallback("blur")&&this.core.callback("blur",e),this.events.focused=!1,this.events.blured=!0)},touchImageEditing:function(){var e=-1;this.events.imageEditing=!1,t(window).on("touchmove.redactor."+this.uuid,t.proxy(function(){this.events.imageEditing=!0,-1!==e&&clearTimeout(e),e=setTimeout(t.proxy(function(){this.events.imageEditing=!1},this),500)},this))},init:function(){this.core.editor().on("dragover.redactor dragenter.redactor",t.proxy(this.events.dragover,this)),this.core.editor().on("dragleave.redactor",t.proxy(this.events.dragleave,this)),this.core.editor().on("drop.redactor",t.proxy(this.events.drop,this)),this.core.editor().on("click.redactor",t.proxy(this.events.click,this)),this.core.editor().on("paste.redactor",t.proxy(this.paste.init,this)),this.core.editor().on("keydown.redactor",t.proxy(this.keydown.init,this)),this.core.editor().on("keyup.redactor",t.proxy(this.keyup.init,this)),this.core.editor().on("focus.redactor",t.proxy(this.events.focus,this)),t(document).on("mousedown.redactor-blur."+this.uuid,t.proxy(this.events.blur,this)),this.events.touchImageEditing(),this.events.createObserver(),this.events.setupObserver()},createObserver:function(){var e=this;this.events.observer=new MutationObserver(function(i){i.forEach(t.proxy(e.events.iterateObserver,e))})},iterateObserver:function(t){var e=!1;(("textarea"===this.opts.type||"div"===this.opts.type)&&!this.detect.isFirefox()&&t.target===this.core.editor()[0]||"class"===t.attributeName&&t.target===this.core.editor()[0]||"data-vivaldi-spatnav-clickable"==t.attributeName)&&(e=!0),e||(this.observe.load(),this.events.changeHandler())},setupObserver:function(){this.events.observer.observe(this.core.editor()[0],{attributes:!0,subtree:!0,childList:!0,characterData:!0,characterDataOldValue:!0})},changeHandler:function(){this.events.stopChanges||(this.code.sync(),this.autosave.is()&&(clearTimeout(this.autosaveTimeout),this.autosaveTimeout=setTimeout(t.proxy(this.autosave.send,this),300)))},onDropUpload:function(t){if(t.preventDefault(),t.stopPropagation(),(this.opts.dragImageUpload||this.opts.dragFileUpload)&&(null!==this.opts.imageUpload||null!==this.opts.fileUpload)){"IMG"===t.target.tagName&&(this.events.dropImage=t.target);for(var e=t.dataTransfer.files,i=e.length,r=0;r<i;r++)this.upload.directUpload(e[r],t)}},onDrop:function(t){this.core.callback("drop",t)},isCallback:function(e){return void 0!==this.opts.callbacks[e]&&t.isFunction(this.opts.callbacks[e])},stopDetect:function(){this.events.stopDetectChanges()},startDetect:function(){this.events.startDetectChanges()}}},file:function(){return{is:function(){return!(!this.opts.fileUpload||!this.opts.fileUpload&&!this.opts.s3)},show:function(){this.modal.load("file",this.lang.get("file"),700),this.upload.init("#redactor-modal-file-upload",this.opts.fileUpload,this.file.insert),t("#redactor-filename").val(this.selection.get().toString()),this.modal.show()},insert:function(e,i,r){if(void 0!==e.error)return this.modal.close(),void this.core.callback("fileUploadError",e);this.file.release(r,i),this.buffer.set(),this.air.collapsed();var o=this.file.text(e),s=t("<a />").attr("href",e.url).text(o),n=void 0===e.id?"":e.id,a=void 0===e.s3?"file":"s3";s.attr("data-"+a,n),s=t(this.insert.node(s)),
-this.caret.after(s),this.storage.add({type:a,node:s[0],url:e.url,id:n}),null!==i&&this.core.callback("fileUpload",s,e)},release:function(t,e){e?(this.marker.remove(),this.insert.nodeToPoint(t,this.marker.get()),this.selection.restore()):this.modal.close()},text:function(e){var i=t("#redactor-filename").val();return void 0===i||""===i?e.name:i}}},focus:function(){return{start:function(){if(this.core.editor().focus(),"inline"!==this.opts.type){var t=this.focus.first();!1!==t&&this.caret.start(t)}},end:function(){this.core.editor().focus();var t=this.opts.inline?this.core.editor():this.focus.last();if(0!==t.length){var e=this.focus.lastChild(t);if(this.detect.isWebkit()||!1===e){var i=this.selection.get(),r=this.selection.range(i);null!==r?(r.selectNodeContents(t[0]),r.collapse(!1),this.selection.update(i,r)):this.caret.end(t)}else this.caret.end(e)}},first:function(){var t=this.core.editor().children().first();return(0!==t.length||0!==t[0].length&&"BR"!==t[0].tagName&&"HR"!==t[0].tagName&&3!==t[0].nodeType)&&("UL"===t[0].tagName||"OL"===t[0].tagName?t.find("li").first():t)},last:function(){return this.core.editor().children().last()},lastChild:function(t){var e=t[0].lastChild;return!(null===e||!this.utils.isInlineTag(e.tagName))&&e},is:function(){return this.core.editor()[0]===document.activeElement}}},image:function(){return{is:function(){return!(!this.opts.imageUpload||!this.opts.imageUpload&&!this.opts.s3)},show:function(){this.modal.load("image",this.lang.get("image"),700),this.upload.init("#redactor-modal-image-droparea",this.opts.imageUpload,this.image.insert),this.modal.show()},insert:function(e,i,r){var o;if(void 0!==e.error)return this.modal.close(),this.events.dropImage=!1,void this.core.callback("imageUploadError",e,r);if(!1!==this.events.dropImage)return o=t(this.events.dropImage),this.core.callback("imageDelete",o[0].src,o),o.attr("src",e.url),this.events.dropImage=!1,void this.core.callback("imageUpload",o,e);this.placeholder.hide();var s=t("<"+this.opts.imageTag+">");o=t("<img>"),o.attr("src",e.url);var n=void 0===e.id?"":e.id,a=void 0===e.s3?"image":"s3";o.attr("data-"+a,n),s.append(o);var l=this.utils.isTag(this.selection.current(),"pre");if(i){this.air.collapsed(),this.marker.remove();var c=this.insert.nodeToPoint(r,this.marker.get()),d=t(c).next();this.selection.restore(),this.buffer.set(),void 0!==d&&0!==d.length&&"IMG"===d[0].tagName?(this.core.callback("imageDelete",d[0].src,d),d.closest("figure, p",this.core.editor()[0]).replaceWith(s),this.caret.after(s)):(l?t(l).after(s):this.insert.node(s),this.caret.after(s))}else this.modal.close(),this.buffer.set(),this.air.collapsed(),l?t(l).after(s):this.insert.node(s),this.caret.after(s);this.events.dropImage=!1,this.storage.add({type:a,node:o[0],url:e.url,id:n});var h=o[0].nextSibling,u=s.next(),p=t(h).text().replace(/\u200B/g,""),f=u.text().replace(/\u200B/g,"");""===p&&t(h).remove(),1===u.length&&"FIGURE"===u[0].tagName&&""===f&&u.remove(),null!==i?this.core.callback("imageUpload",o,e):this.core.callback("imageInserted",o,e)},setEditable:function(e){if(e.on("dragstart",function(t){t.preventDefault()}),this.opts.imageResizable){var i=t.proxy(function(i){this.observe.image=e,this.image.resizer=this.image.loadEditableControls(e),t(document).on("mousedown.redactor-image-resize-hide."+this.uuid,t.proxy(this.image.hideResize,this)),this.image.resizer&&this.image.resizer.on("mousedown.redactor touchstart.redactor",t.proxy(function(t){this.image.setResizable(t,e)},this))},this);e.off("mousedown.redactor").on("mousedown.redactor",t.proxy(this.image.hideResize,this)),e.off("click.redactor touchstart.redactor").on("click.redactor touchstart.redactor",i)}else e.off("click.redactor touchstart.redactor").on("click.redactor touchstart.redactor",t.proxy(function(i){setTimeout(t.proxy(function(){this.image.showEdit(e)},this),200)},this))},setResizable:function(t,e){t.preventDefault(),this.image.resizeHandle={x:t.pageX,y:t.pageY,el:e,ratio:e.width()/e.height(),h:e.height()},t=t.originalEvent||t,t.targetTouches&&(this.image.resizeHandle.x=t.targetTouches[0].pageX,this.image.resizeHandle.y=t.targetTouches[0].pageY),this.image.startResize()},startResize:function(){t(document).on("mousemove.redactor-image-resize touchmove.redactor-image-resize",t.proxy(this.image.moveResize,this)),t(document).on("mouseup.redactor-image-resize touchend.redactor-image-resize",t.proxy(this.image.stopResize,this))},moveResize:function(t){t.preventDefault(),t=t.originalEvent||t;var e=this.image.resizeHandle.h;t.targetTouches?e+=t.targetTouches[0].pageY-this.image.resizeHandle.y:e+=t.pageY-this.image.resizeHandle.y;var i=Math.round(e*this.image.resizeHandle.ratio);e<50||i<100||this.core.editor().width()<=i||(this.image.resizeHandle.el.attr({width:i,height:e}),this.image.resizeHandle.el.width(i),this.image.resizeHandle.el.height(e),this.code.sync())},stopResize:function(){this.handle=!1,t(document).off(".redactor-image-resize"),this.image.hideResize()},hideResize:function(e){if(!e||0===t(e.target).closest("#redactor-image-box",this.$editor[0]).length){if(e&&"IMG"==e.target.tagName){t(e.target)}var i=this.$editor.find("#redactor-image-box");0!==i.length&&(t("#redactor-image-editter").remove(),t("#redactor-image-resizer").remove(),i.find("img").css({marginTop:i[0].style.marginTop,marginBottom:i[0].style.marginBottom,marginLeft:i[0].style.marginLeft,marginRight:i[0].style.marginRight}),i.css("margin",""),i.find("img").css("opacity",""),i.replaceWith(function(){return t(this).contents()}),t(document).off("mousedown.redactor-image-resize-hide."+this.uuid),void 0!==this.image.resizeHandle&&this.image.resizeHandle.el.attr("rel",this.image.resizeHandle.el.attr("style")))}},loadResizableControls:function(e,i){if(this.opts.imageResizable&&!this.detect.isMobile()){var r=t('<span id="redactor-image-resizer" data-redactor="verified"></span>');return this.detect.isDesktop()||r.css({width:"15px",height:"15px"}),r.attr("contenteditable",!1),i.append(r),i.append(e),r}return i.append(e),!1},loadEditableControls:function(e){if(0===t("#redactor-image-box").length){var i=t('<span id="redactor-image-box" data-redactor="verified">');if(i.css("float",e.css("float")).attr("contenteditable",!1),"auto"!=e[0].style.margin?(i.css({marginTop:e[0].style.marginTop,marginBottom:e[0].style.marginBottom,marginLeft:e[0].style.marginLeft,marginRight:e[0].style.marginRight}),e.css("margin","")):i.css({display:"block",margin:"auto"}),e.css("opacity",".5").after(i),this.opts.imageEditable){this.image.editter=t('<span id="redactor-image-editter" data-redactor="verified">'+this.lang.get("edit")+"</span>"),this.image.editter.attr("contenteditable",!1),this.image.editter.on("click",t.proxy(function(){this.image.showEdit(e)},this)),i.append(this.image.editter);var r=this.image.editter.innerWidth();this.image.editter.css("margin-left","-"+r/2+"px")}return this.image.loadResizableControls(e,i)}},showEdit:function(e){if(!this.events.imageEditing){this.observe.image=e;var i=e.closest("a",this.$editor[0]),r=e.closest("figure",this.$editor[0]),o=0!==r.length?r:e;if(this.modal.load("image-edit",this.lang.get("edit"),705),this.image.buttonDelete=this.modal.getDeleteButton().text(this.lang.get("delete")),this.image.buttonSave=this.modal.getActionButton().text(this.lang.get("save")),this.image.buttonDelete.on("click",t.proxy(this.image.remove,this)),this.image.buttonSave.on("click",t.proxy(this.image.update,this)),!1===this.opts.imageCaption)t("#redactor-image-caption").val("").hide().prev().hide();else{var s=e.closest(this.opts.imageTag,this.$editor[0]),n=s.find("figcaption");0!==n&&t("#redactor-image-caption").val(n.text()).show()}if(this.opts.imagePosition){var a=0!==r.length?"center"===o.css("text-align"):"block"==o.css("display")&&"none"==o.css("float"),l=a?"center":o.css("float");t("#redactor-image-align").val(l)}else t(".redactor-image-position-option").hide();t("#redactor-image-preview").html(t('<img src="'+e.attr("src")+'" style="max-width: 100%;">')),t("#redactor-image-title").val(e.attr("alt")),0!==i.length&&(t("#redactor-image-link").val(i.attr("href")),"_blank"===i.attr("target")&&t("#redactor-image-link-blank").prop("checked",!0)),t(".redactor-link-tooltip").remove(),this.modal.show(),this.detect.isDesktop()&&t("#redactor-image-title").focus()}},update:function(){var e=this.observe.image,i=e.closest("a",this.core.editor()[0]),r=t("#redactor-image-title").val().replace(/(<([^>]+)>)/gi,"");e.attr("alt",r).attr("title",r),this.image.setFloating(e);var o=t.trim(t("#redactor-image-link").val()).replace(/(<([^>]+)>)/gi,"");if(""!==o){var s="((xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,}",n=new RegExp("^(http|ftp|https)://"+s,"i"),a=new RegExp("^"+s,"i");-1===o.search(n)&&0===o.search(a)&&this.opts.linkProtocol&&(o=this.opts.linkProtocol+"://"+o);var l=!!t("#redactor-image-link-blank").prop("checked");if(0===i.length){var c=t('<a href="'+o+'" id="redactor-img-tmp">'+this.utils.getOuterHtml(e)+"</a>");l&&c.attr("target","_blank"),e=e.replaceWith(c),i=this.core.editor().find("#redactor-img-tmp"),i.removeAttr("id")}else i.attr("href",o),l?i.attr("target","_blank"):i.removeAttr("target")}else 0!==i.length&&i.replaceWith(this.utils.getOuterHtml(e));this.image.addCaption(e,i),this.modal.close(),this.buffer.set()},setFloating:function(e){var i=e.closest("figure",this.$editor[0]),r=0!==i.length?i:e,o=t("#redactor-image-align").val(),s="",n="",a="",l="";switch(o){case"left":s="left",a="0 "+this.opts.imageFloatMargin+" "+this.opts.imageFloatMargin+" 0";break;case"right":s="right",a="0 0 "+this.opts.imageFloatMargin+" "+this.opts.imageFloatMargin;break;case"center":0!==i.length?l="center":(n="block",a="auto")}r.css({float:s,display:n,margin:a,"text-align":l}),r.attr("rel",e.attr("style"))},addCaption:function(e,i){var r=t("#redactor-image-caption").val(),o=0!==i.length?i:e,s=o.next();0!==s.length&&"FIGCAPTION"===s[0].tagName||(s=!1),""!==r?!1===s?(s=t("<figcaption />").text(r),o.after(s)):s.text(r):!1!==s&&s.remove()},remove:function(e,i,r){i=void 0===i?t(this.observe.image):i,"boolean"!=typeof e&&this.buffer.set(),this.events.stopDetectChanges();var o=i.closest("a",this.core.editor()[0]),s=i.closest(this.opts.imageTag,this.core.editor()[0]);i.parent();if(!1===this.core.callback("imageDelete",e,i[0]))return e&&e.preventDefault(),!1;0!==t("#redactor-image-box").length&&t("#redactor-image-box").parent();var n,a;0!==s.length?(a=s.prev(),n=s.next(),s.remove()):0!==o.length?(o.parent(),o.remove()):i.remove(),t("#redactor-image-box").remove(),!1!==e&&(n&&0!==n.length?this.caret.start(n):a&&0!==a.length&&this.caret.end(a)),"boolean"!=typeof e&&this.modal.close(),this.utils.restoreScroll(),this.observe.image=!1,this.events.startDetectChanges(),this.placeholder.enable(),this.code.sync()}}},indent:function(){return{increase:function(){if(this.list.get()){var e=t(this.selection.current()).closest("li"),i=e.closest("ul, ol",this.core.editor()[0]),r=e.closest("li"),o=r.prev();if(0!==o.length&&"LI"===o[0].tagName)if(this.buffer.set(),this.utils.isCollapsed()){var s=i[0].tagName,n=t("<"+s+" />");this.selection.save();var a=o.find("ol").first();if(1===a.length)a.append(e);else{var s=i[0].tagName,n=t("<"+s+" />");n.append(e),o.append(n)}this.selection.restore()}else document.execCommand("indent"),this.selection.save(),this.indent.removeEmpty(),this.indent.normalize(),this.selection.restore()}},decrease:function(){if(this.list.get()){var e=t(this.selection.current()).closest("li");e.closest("ul, ol",this.core.editor()[0]);this.buffer.set(),document.execCommand("outdent");var i=t(this.selection.current()).closest("li",this.core.editor()[0]);if(this.utils.isCollapsed()&&this.indent.repositionItem(i),0===i.length){document.execCommand("formatblock",!1,"p"),i=t(this.selection.current());var r=i.next();0!==r.length&&"BR"===r[0].tagName&&r.remove()}this.selection.save(),this.indent.removeEmpty(),this.indent.normalize(),this.selection.restore()}},repositionItem:function(t){var e=t.next();0===e.length||"UL"===e[0].tagName&&"OL"===e[0].tagName||t.append(e);var i=t.prev();if(0!==i.length&&"LI"!==i[0].tagName){this.selection.save();t.parents("li",this.core.editor()[0]).after(t),this.selection.restore()}},normalize:function(){this.core.editor().find("li").each(t.proxy(function(e,i){var r=t(i),o="";0!==this.opts.keepStyleAttr.length&&(o=","+this.opts.keepStyleAttr.join(",")),r.find(this.opts.inlineTags.join(",")).not("img"+o).removeAttr("style");var s=r.parent();if(0!==s.length&&"LI"===s[0].tagName)return void s.after(r);var n=r.next();0===n.length||"UL"!==n[0].tagName&&"OL"!==n[0].tagName||r.append(n)},this))},removeEmpty:function(e){var i=this.core.editor().find("ul, ol"),r=this.core.editor().find("li");r.each(t.proxy(function(t,e){this.indent.removeItemEmpty(e)},this)),i.each(t.proxy(function(t,e){this.indent.removeItemEmpty(e)},this)),r.each(t.proxy(function(t,e){this.indent.removeItemEmpty(e)},this))},removeItemEmpty:function(e){var i=e.innerHTML.replace(/[\t\s\n]/g,"");""===(i=i.replace(/<span><\/span>/g,""))&&t(e).remove()}}},inline:function(){return{format:function(t,e,i,r){if(!this.utils.isCurrentOrParent(["PRE","CODE"])){var o=this.inline.getParams(e,i,r);t=this.inline.arrangeTag(t),this.placeholder.hide(),this.buffer.set(),this.utils.isCollapsed()?this.inline.formatCollapsed(t,o):this.inline.formatUncollapsed(t,o)}},formatCollapsed:function(e,i){var r,o=this.selection.inline();if(o){var s=o.tagName.toLowerCase();if(s===e)if(this.utils.isEmpty(o.innerHTML))this.caret.after(o),t(o).remove();else{var n=this.inline.insertBreakpoint(o,s);this.caret.after(n)}else if(0===t(o).closest(e).length)r=this.inline.insertInline(e),r=this.inline.setParams(r,i);else{var n=this.inline.insertBreakpoint(o,s);this.caret.after(n)}}else r=this.inline.insertInline(e),r=this.inline.setParams(r,i)},formatUncollapsed:function(e,i){this.selection.save();var r=this.inline.getClearedNodes();this.inline.setNodesStriked(r,e,i),this.selection.restore(),document.execCommand("strikethrough"),this.selection.saveInstant();var o=this;this.core.editor().find("strike").each(function(){var r=o.utils.replaceToTag(this,e);o.inline.setParams(r[0],i);var s=r.find(e),n=r.parent(),a=n.parent();if(0!==a.length&&a[0].tagName.toLowerCase()===e&&a.html()==n[0].outerHTML)return r.replaceWith(function(){return t(this).contents()}),void a.replaceWith(function(){return t(this).contents()});0!==s.length&&o.inline.cleanInsideOrParent(s,i),n.html()==r[0].outerHTML&&o.inline.cleanInsideOrParent(n,i),o.detect.isFirefox()&&o.core.editor().find(e+":empty").remove()}),this.selection.restoreInstant()},cleanInsideOrParent:function(t,e){if(e)for(var i in e.data)this.inline.removeSpecificAttr(t,i,e.data[i])},getClearedNodes:function(){for(var e=this.selection.nodes(),i=[],r=e.length,o=0,s=0;s<r;s++)if(t(e[s]).hasClass("redactor-selection-marker")){o=s+2;break}for(var s=0;s<r;s++)s>=o&&!this.utils.isBlockTag(e[s].tagName)&&i.push(e[s]);return i},isConvertableAttr:function(e,i,r){var o=t(e).attr(i);if(o)if("style"===i){r=t.trim(r).replace(/;$/,"");for(var s=r.split(";"),n=0,a=0;a<s.length;a++){var l=s[a].split(":"),c=t.trim(l[0]),d=t.trim(l[1]);if(-1!==c.search(/color/)){var h=t(e).css(c);!h||h!==d&&this.utils.rgb2hex(h)!==d||n++}else t(e).css(c)===d&&n++}if(n===s.length)return 1}else if(o===r)return 1;return 0},isConvertable:function(t,e,i,r){if(e===i){if(!r)return!0;var o=0;for(var s in r.data)o+=this.inline.isConvertableAttr(t,s,r.data[s]);if(o===Object.keys(r.data).length)return!0}return!1},setNodesStriked:function(e,i,r){for(var o=0;o<e.length;o++){var s=e[o].tagName?e[o].tagName.toLowerCase():void 0,n=e[o].parentNode,a=n&&n.tagName?n.tagName.toLowerCase():void 0,l=this.inline.isConvertable(n,a,i,r);if(l){t(n).replaceWith(function(){return t("<strike>").append(t(this).contents())}).attr("data-redactor-inline-converted")}var l=this.inline.isConvertable(e[o],s,i,r);if(l){t(e[o]).replaceWith(function(){return t("<strike>").append(t(this).contents())})}}},insertBreakpoint:function(e,i){var r=document.createElement("span");r.id="redactor-inline-breakpoint",r=this.insert.node(r);var o=this.utils.isEndOfElement(e),s=this.utils.getOuterHtml(e),n=o?"":"<"+i+">";s=s.replace(/<span id="redactor-inline-breakpoint"><\/span>/i,"</"+i+">"+n);var a=t(s);return t(e).replaceWith(a),""!==n&&this.utils.cloneAttributes(e,a.last()),a.first()},insertInline:function(t){var e=document.createElement(t);return this.insert.node(e),this.caret.start(e),e},arrangeTag:function(t){var e=["b","bold","i","italic","underline","strikethrough","deleted","superscript","subscript"],i=["strong","strong","em","em","u","del","del","sup","sub"];t=t.toLowerCase();for(var r=0;r<e.length;r++)t===e[r]&&(t=i[r]);return t},getStyleParams:function(t){for(var e={},i=t.trim().replace(/;$/,"").split(";"),r=0;r<i.length;r++){var o=i[r].split(":");o&&(e[o[0].trim()]=o[1].trim())}return e},getParams:function(t,e,i){var r=!1,o="toggle";return"object"==typeof t?(r=t,o=void 0!==e?e:o):void 0!==t&&void 0!==e&&(r={},r[t]=e,o=void 0!==i?i:o),!!r&&{func:o,data:r}},setParams:function(e,i){if(i)for(var r in i.data){var o=t(e);"style"===r?(e=this.inline[i.func+"Style"](i.data[r],e),o.attr("data-redactor-style-cache",o.attr("style"))):e="class"===r?this.inline[i.func+"Class"](i.data[r],e):"remove"===i.func?this.inline[i.func+"Attr"](r,e):this.inline[i.func+"Attr"](r,i.data[r],e),"style"===r&&"SPAN"===e.tagName&&o.attr("data-redactor-span",!0)}return e},eachInline:function(t,e){var i,r=void 0===t?this.selection.inlines():[t];if(r)for(var o=0;o<r.length;o++)i=e(r[o])[0];return i},replaceClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).removeAttr("class").addClass(e)})},toggleClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).toggleClass(e)})},addClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).addClass(e)})},removeClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).removeClass(e)})},removeAllClass:function(e){return this.inline.eachInline(e,function(e){return t(e).removeAttr("class")})},replaceAttr:function(e,i,r){return this.inline.eachInline(r,function(i){return t(i).removeAttr(e).attr(e.value)})},toggleAttr:function(e,i,r){return this.inline.eachInline(r,function(i){return t(i).attr(e)?t(i).removeAttr(e):t(i).attr(e.value)})},addAttr:function(e,i,r){return this.inline.eachInline(r,function(r){return t(r).attr(e,i)})},removeAttr:function(e,i){return this.inline.eachInline(i,function(i){var r=t(i);return r.removeAttr(e),"style"===e&&r.removeAttr("data-redactor-style-cache"),r})},removeAllAttr:function(e){return this.inline.eachInline(e,function(e){for(var i=t(e),r=e.attributes.length,o=0;o<r;o++)i.removeAttr(e.attributes[o].name);return i})},removeSpecificAttr:function(e,i,r){var o=t(e);if("style"===i){var s=r.split(":"),n=s[0].trim();o.css(n,""),this.utils.removeEmptyAttr(e,"style")&&o.removeAttr("data-redactor-style-cache")}else o.removeAttr(i)[0]},hasParentStyle:function(t){var e=t.parent();return 1===e.length&&e[0].tagName===t[0].tagName&&e.html()===t[0].outerHTML&&e},addParentStyle:function(e){var i=this.inline.hasParentStyle(e);if(i){var r=this.inline.getStyleParams(e.attr("style"));i.css(r),i.attr("data-redactor-style-cache",i.attr("style")),e.replaceWith(function(){return t(this).contents()})}else e.attr("data-redactor-style-cache",e.attr("style"));return e},replaceStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var o=t(i);o.removeAttr("style").css(e);var s=o.attr("style");return s&&o.attr("style",s.replace(/"/g,"'")),o=r.inline.addParentStyle(o)})},toggleStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var o=t(i);for(var s in e){var n=e[s],a=o.css(s);a=r.utils.isRgb(a)?r.utils.rgb2hex(a):a.replace(/"/g,""),n=r.utils.isRgb(n)?r.utils.rgb2hex(n):n.replace(/"/g,""),a===n?o.css(s,""):o.css(s,n)}var l=o.attr("style");return l&&o.attr("style",l.replace(/"/g,"'")),r.utils.removeEmptyAttr(i,"style")?o.removeAttr("data-redactor-style-cache"):o=r.inline.addParentStyle(o),o})},addStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var o=t(i);o.css(e);var s=o.attr("style");return s&&o.attr("style",s.replace(/"/g,"'")),o=r.inline.addParentStyle(o)})},removeStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var o=t(i);for(var s in e)o.css(s,"");return r.utils.removeEmptyAttr(i,"style")?o.removeAttr("data-redactor-style-cache"):o.attr("data-redactor-style-cache",o.attr("style")),o})},removeAllStyle:function(e){return this.inline.eachInline(e,function(e){return t(e).removeAttr("style").removeAttr("data-redactor-style-cache")})},removeStyleRule:function(e){var i=this.selection.parent(),r=this.selection.inlines();this.buffer.set(),i&&"SPAN"===i.tagName&&this.inline.removeStyleRuleAttr(t(i),e);for(var o=0;o<r.length;o++){var s=r[o],n=t(s);-1==t.inArray(s.tagName.toLowerCase(),this.opts.inlineTags)||n.hasClass("redactor-selection-marker")||this.inline.removeStyleRuleAttr(n,e)}},removeStyleRuleAttr:function(t,e){t.css(e,""),this.utils.removeEmptyAttr(t,"style")?t.removeAttr("data-redactor-style-cache"):t.attr("data-redactor-style-cache",t.attr("style"))},update:function(t,e,i,r){t=this.inline.arrangeTag(t);var o=this.inline.getParams(e,i,r),s=this.selection.inlines(),n=[];if(s)for(var a=0;a<s.length;a++){var l=s[a];"*"!==t&&l.tagName.toLowerCase()!==t||n.push(this.inline.setParams(l,o))}return n},removeFormat:function(){this.selection.save();for(var e=this.inline.getClearedNodes(),i=0;i<e.length;i++)1===e[i].nodeType&&t(e[i]).replaceWith(function(){return t(this).contents()});this.selection.restore()}}},insert:function(){return{set:function(t){this.placeholder.hide(),this.code.set(t),this.focus.end(),this.placeholder.enable()},html:function(e,i){this.placeholder.hide(),this.core.editor().focus();var r=this.selection.block(),o=this.selection.inline();void 0===i&&(i=this.clean.getCurrentType(e,!0),e=this.clean.onPaste(e,i,!0)),e=t.parseHTML(e);var s=t(e).last(),n=this.selection.get(),a=this.selection.range(n);if(a.deleteContents(),this.selection.update(n,a),i.lists){var l=t(e);if(0!==l.length&&("UL"===l[0].tagName||"OL"===l[0].tagName))return void this.insert.appendLists(r,l)}if(i.blocks&&r)if(this.utils.isSelectAll())this.core.editor().html(e),this.focus.end();else{var c=this.utils.breakBlockTag();if(!1===c)this.insert.placeHtml(e);else{var d=t(e).children().last();d.append(this.marker.get()),"start"===c.type?c.$block.before(e):c.$block.after(e),this.selection.restore(),this.core.editor().find("p").each(function(){""===t.trim(this.innerHTML)&&t(this).remove()})}}else{if(o){var h=t("<div/>").html(e);h.find(o.tagName.toLowerCase()).each(function(){t(this).contents().unwrap()}),e=h.html(),e=t.parseHTML(e),s=t(e).last()}if(this.utils.isSelectAll()){var u=t(this.opts.emptyHtml);this.core.editor().html("").append(u),u.html(e),this.caret.end(u)}else this.insert.placeHtml(e)}this.utils.disableSelectAll(),i.pre&&this.clean.cleanPre(),this.caret.end(s),this.linkify.format()},text:function(e){e=e.toString(),e=t.trim(e);var i=document.createElement("div");if(i.innerHTML=e,void 0!==(e=i.textContent||i.innerText)){this.placeholder.hide(),this.core.editor().focus();var r=this.selection.blocks();if(e=e.replace(/\n/g," "),this.utils.isSelectAll()){var o=t(this.opts.emptyHtml);this.core.editor().html("").append(o),o.html(e),this.caret.end(o)}else{var s=this.selection.get(),n=document.createTextNode(e);if(s.getRangeAt&&s.rangeCount){var a=s.getRangeAt(0);a.deleteContents(),a.insertNode(n),a.setStartAfter(n),a.collapse(!0),this.selection.update(s,a)}r.length>1&&(t(n).wrap("<p>"),this.caret.after(n))}this.utils.disableSelectAll(),this.linkify.format(),this.clean.normalizeCurrentHeading()}},raw:function(t){this.placeholder.hide(),this.core.editor().focus();var e=this.selection.get(),i=this.selection.range(e);i.deleteContents();var r=document.createElement("div");r.innerHTML=t;for(var o,s,n=document.createDocumentFragment();o=r.firstChild;)s=n.appendChild(o);i.insertNode(n),s&&(i=i.cloneRange(),i.setStartAfter(s),i.collapse(!0),e.removeAllRanges(),e.addRange(i))},node:function(e,i){this.placeholder.hide(),void 0!==this.start&&this.core.editor().focus(),e=e[0]||e;var r=this.selection.block(),o=this.utils.isBlockTag(e.tagName),s=!0;if(this.utils.isSelectAll())o?this.core.editor().html(e):this.core.editor().html(t("<p>").html(e)),this.code.sync();else if(o&&r){var n=this.utils.breakBlockTag();!1===n?this.insert.placeNode(e,i):("start"===n.type?n.$block.before(e):n.$block.after(e),this.core.editor().find("p:empty").remove())}else s=this.insert.placeNode(e,i);return this.utils.disableSelectAll(),s&&this.caret.end(e),e},appendLists:function(e,i){var r,o=t(e),s=this.utils.isEmpty(e.innerHTML);if(s||this.utils.isEndOfElement(e))r=o,i.find("li").each(function(){r.after(this),r=t(this)}),s&&o.remove();else if(this.utils.isStartOfElement(e))i.find("li").each(function(){o.before(this),r=t(this)});else{var n=this.selection.extractEndOfNode(e);o.after(t("<li>").append(n)),o.append(i),r=i}this.marker.remove(),r&&this.caret.end(r),this.linkify.format()},placeHtml:function(e){var i=document.createElement("span");i.id="redactor-insert-marker",i=this.insert.node(i),t(i).before(e),this.selection.restore(),this.caret.after(i),t(i).remove()},placeNode:function(t,e){var i=this.selection.get(),r=this.selection.range(i);if(null==r)return!1;!1!==e&&r.deleteContents(),r.insertNode(t),r.collapse(!1),this.selection.update(i,r)},nodeToPoint:function(e,i){if(this.placeholder.hide(),i=i[0]||i,this.utils.isEmpty())return i=this.utils.isBlock(i)?i:t("<p />").append(i),this.core.editor().html(i),i;var r,o=e.clientX,s=e.clientY;if(document.caretPositionFromPoint){var n=document.caretPositionFromPoint(o,s);r=document.getSelection().getRangeAt(0),r.setStart(n.offsetNode,n.offset),r.collapse(!0),r.insertNode(i)}else if(document.caretRangeFromPoint)r=document.caretRangeFromPoint(o,s),r.insertNode(i);else if(void 0!==document.body.createTextRange){r=document.body.createTextRange(),r.moveToPoint(o,s);var a=r.duplicate();a.moveToPoint(o,s),r.setEndPoint("EndToEnd",a),r.select()}return i},nodeToCaretPositionFromPoint:function(t,e){this.insert.nodeToPoint(t,e)},marker:function(){this.marker.insert()}}},keydown:function(){return{init:function(e){if(!this.rtePaste){var i=e.which,r=i>=37&&i<=40;this.keydown.ctrl=e.ctrlKey||e.metaKey,this.keydown.parent=this.selection.parent(),this.keydown.current=this.selection.current(),this.keydown.block=this.selection.block(),this.keydown.pre=this.utils.isTag(this.keydown.current,"pre"),this.keydown.blockquote=this.utils.isTag(this.keydown.current,"blockquote"),this.keydown.figcaption=this.utils.isTag(this.keydown.current,"figcaption"),this.keydown.figure=this.utils.isTag(this.keydown.current,"figure");if(!1===this.core.callback("keydown",e))return e.preventDefault(),!1;if(this.shortcuts.init(e,i),this.keydown.checkEvents(r,i),this.keydown.setupBuffer(e,i),this.utils.isSelectAll()&&(i===this.keyCode.ENTER||i===this.keyCode.BACKSPACE||i===this.keyCode.DELETE))return e.preventDefault(),this.code.set(this.opts.emptyHtml),void this.events.changeHandler();if(this.keydown.addArrowsEvent(r),this.keydown.setupSelectAll(e,i),!this.opts.enterKey&&i===this.keyCode.ENTER){e.preventDefault();var o=this.selection.get(),s=this.selection.range(o);return void(s.collapsed||s.deleteContents())}if(this.opts.enterKey&&i===this.keyCode.DOWN&&this.keydown.onArrowDown(),this.opts.enterKey&&i===this.keyCode.UP&&this.keydown.onArrowUp(),("textarea"===this.opts.type||"div"===this.opts.type)&&this.keydown.current&&3===this.keydown.current.nodeType&&t(this.keydown.parent).hasClass("redactor-in")&&this.keydown.wrapToParagraph(),!this.keyup.lastShiftKey&&i===this.keyCode.SPACE&&(e.ctrlKey||e.shiftKey))return e.preventDefault(),this.keydown.onShiftSpace();if(i===this.keyCode.ENTER&&(e.ctrlKey||e.shiftKey))return e.preventDefault(),this.keydown.onShiftEnter(e);if(i===this.keyCode.ENTER&&!e.shiftKey&&!e.ctrlKey&&!e.metaKey)return this.keydown.onEnter(e);if(i===this.keyCode.TAB||e.metaKey&&221===i||e.metaKey&&219===i)return this.keydown.onTab(e,i);if(this.detect.isFirefox()&&i===this.keyCode.BACKSPACE&&this.keydown.block&&"P"===this.keydown.block.tagName&&this.utils.isStartOfElement(this.keydown.block)){var n=t(this.keydown.block).prev();if(0!==n.length)return e.preventDefault(),n.append(this.marker.get()),n.append(t(this.keydown.block).html()),t(this.keydown.block).remove(),void this.selection.restore()}if(i===this.keyCode.BACKSPACE||i===this.keyCode.DELETE){if(this.observe.image&&void 0!==this.observe.image&&0!==t("#redactor-image-box").length){e.preventDefault();var n=this.observe.image.closest("figure, p").prev();return this.image.remove(!1),this.observe.image=!1,void(n&&0!==n.length?this.caret.end(n):this.core.editor().focus())}this.keydown.onBackspaceAndDeleteBefore()}if(i===this.keyCode.DELETE){var a=t(this.keydown.block).next();if(this.utils.isEndOfElement(this.keydown.block)&&0!==a.length&&"FIGURE"===a[0].tagName)return a.remove(),!1;if(!(!this.keydown.block||"LI"!==this.keydown.block.tagName)&&this.keydown.block){var l=t(this.keydown.block).parents("ul, ol").last(),c=l.next();if(this.utils.isRedactorParent(l)&&this.utils.isEndOfElement(l)&&0!==c.length&&("UL"===c[0].tagName||"OL"===c[0].tagName))return e.preventDefault(),l.append(c.contents()),c.remove(),!1}if(this.utils.isEndOfElement(this.keydown.block)&&0!==a.length&&"PRE"===a[0].tagName)return t(this.keydown.block).append(a.text()),a.remove(),!1}if(i===this.keyCode.DELETE&&0!==t("#redactor-image-box").length&&this.image.remove(),i===this.keyCode.BACKSPACE){if(this.detect.isFirefox()&&this.line.removeOnBackspace(e),this.list.combineAfterAndBefore(this.keydown.block))return void e.preventDefault();var d=this.selection.block();if(d&&"LI"===d.tagName&&this.utils.isCollapsed()&&this.utils.isStartOfElement())return this.indent.decrease(),void e.preventDefault();this.keydown.removeInvisibleSpace(),this.keydown.removeEmptyListInTable(e)}i!==this.keyCode.BACKSPACE&&i!==this.keyCode.DELETE||this.keydown.onBackspaceAndDeleteAfter(e)}},onShiftSpace:function(){return this.buffer.set(),this.insert.raw(" "),!1},onShiftEnter:function(t){return this.buffer.set(),this.keydown.pre?this.keydown.insertNewLine(t):this.insert.raw("<br>")},onBackspaceAndDeleteBefore:function(){this.utils.saveScroll()},onBackspaceAndDeleteAfter:function(e){setTimeout(t.proxy(function(){this.code.syncFire=!1,this.keydown.removeEmptyLists();var t="";0!==this.opts.keepStyleAttr.length&&(t=","+this.opts.keepStyleAttr.join(",")),this.core.editor().find("*[style]").not("img, figure, iframe, #redactor-image-box, #redactor-image-editter, [data-redactor-style-cache], [data-redactor-span]"+t).removeAttr("style"),this.keydown.formatEmpty(e),this.code.syncFire=!0},this),1)},onEnter:function(e){if(!1===this.core.callback("enter",e))return e.preventDefault(),!1;if(this.keydown.blockquote&&!0===this.keydown.exitFromBlockquote(e))return!1;if(this.keydown.pre)return this.keydown.insertNewLine(e);if(this.keydown.blockquote||this.keydown.figcaption)return this.keydown.insertBreakLine(e);if(this.keydown.figure)setTimeout(t.proxy(function(){this.keydown.replaceToParagraph("FIGURE")},this),1);else if(this.keydown.block){if(setTimeout(t.proxy(function(){this.keydown.replaceToParagraph("DIV")},this),1),"LI"===this.keydown.block.tagName){var i=this.selection.current(),r=t(i).closest("li",this.$editor[0]),o=r.parents("ul,ol",this.$editor[0]).last();if(0!==r.length&&this.utils.isEmpty(r.html())&&0===o.next().length&&this.utils.isEmpty(o.find("li").last().html())){o.find("li").last().remove();var s=t(this.opts.emptyHtml);return o.after(s),this.caret.start(s),!1}}}else if(!this.keydown.block)return this.keydown.insertParagraph(e)
-;if(this.detect.isFirefox()&&this.utils.isInline(this.keydown.parent))return void this.keydown.insertBreakLine(e);this.opts.keepInlineOnEnter||setTimeout(t.proxy(function(){var e=this.selection.inline();if(e&&this.utils.isEmpty(e.innerHTML)){var i=this.selection.block();t(e).remove();var r=document.createRange();r.setStart(i,0);var o=document.createTextNode("");r.insertNode(o),r.setStartAfter(o),r.collapse(!0);var s=window.getSelection();s.removeAllRanges(),s.addRange(r)}},this),1)},checkEvents:function(t,e){t||"click"!==this.core.getEvent()&&"arrow"!==this.core.getEvent()||(this.core.addEvent(!1),this.keydown.checkKeyEvents(e)&&this.buffer.set())},checkKeyEvents:function(e){var i=this.keyCode,r=[i.BACKSPACE,i.DELETE,i.ENTER,i.ESC,i.TAB,i.CTRL,i.META,i.ALT,i.SHIFT];return-1===t.inArray(e,r)},addArrowsEvent:function(t){if(t)return"click"===this.core.getEvent()||"arrow"===this.core.getEvent()?void this.core.addEvent(!1):void this.core.addEvent("arrow")},setupBuffer:function(t,e){return this.keydown.ctrl&&90===e&&!t.shiftKey&&!t.altKey&&this.sBuffer.length?(t.preventDefault(),void this.buffer.undo()):this.keydown.ctrl&&90===e&&t.shiftKey&&!t.altKey&&0!==this.sRebuffer.length?(t.preventDefault(),void this.buffer.redo()):void(this.keydown.ctrl||e!==this.keyCode.SPACE&&e!==this.keyCode.BACKSPACE&&e!==this.keyCode.DELETE&&(e!==this.keyCode.ENTER||t.ctrlKey||t.shiftKey)||this.buffer.set())},exitFromBlockquote:function(e){if(this.utils.isEndOfElement(this.keydown.blockquote)){if(-1!==this.clean.removeSpacesHard(t(this.keydown.blockquote).html()).search(/(<br\s?\/?>){1}$/i)){e.preventDefault();t(this.keydown.blockquote).children().last().filter("br").remove(),t(this.keydown.blockquote).children().last().filter("span").remove();var i=t(this.opts.emptyHtml);return t(this.keydown.blockquote).after(i),this.caret.start(i),!0}}},onArrowDown:function(){for(var t=[this.keydown.blockquote,this.keydown.pre,this.keydown.figcaption],e=0;e<t.length;e++)if(t[e])return this.keydown.insertAfterLastElement(t[e]),!1},onArrowUp:function(){for(var t=[this.keydown.blockquote,this.keydown.pre,this.keydown.figcaption],e=0;e<t.length;e++)if(t[e])return this.keydown.insertBeforeFirstElement(t[e]),!1},insertAfterLastElement:function(e){if(this.utils.isEndOfElement(e)){var i=this.core.editor().contents().last();if(0===("FIGCAPTION"===e.tagName?t(this.keydown.block).parent().next():t(this.keydown.block).next()).length){if(0===i.length&&i[0]!==e)return void this.caret.start(i);var r=t(this.opts.emptyHtml);"FIGCAPTION"===e.tagName?t(e).parent().after(r):t(e).after(r),this.caret.start(r)}}},insertBeforeFirstElement:function(e){if(this.utils.isStartOfElement()&&!(this.core.editor().contents().length>1&&this.core.editor().contents().first()[0]!==e)){var i=t(this.opts.emptyHtml);t(e).before(i),this.caret.start(i)}},onTab:function(t,e){if(!this.opts.tabKey)return!0;var i=this.keydown.block&&"LI"===this.keydown.block.tagName;if(this.utils.isEmpty(this.code.get())||!i&&!this.keydown.pre&&!1===this.opts.tabAsSpaces)return!0;t.preventDefault(),this.buffer.set();var r,o=i&&this.utils.isStartOfElement(this.keydown.block);return this.keydown.pre&&!t.shiftKey?(r=this.opts.preSpaces?document.createTextNode(Array(this.opts.preSpaces+1).join(" ")):document.createTextNode("\t"),this.insert.node(r)):!1===this.opts.tabAsSpaces||o?t.metaKey&&219===e?this.indent.decrease():t.metaKey&&221===e?this.indent.increase():t.shiftKey?this.indent.decrease():this.indent.increase():(r=document.createTextNode(Array(this.opts.tabAsSpaces+1).join(" ")),this.insert.node(r)),!1},setupSelectAll:function(t,e){this.keydown.ctrl&&65===e?this.utils.enableSelectAll():e===this.keyCode.LEFT_WIN||this.keydown.ctrl||this.utils.disableSelectAll()},insertNewLine:function(t){t.preventDefault();var e=document.createTextNode("\n"),i=this.selection.get(),r=this.selection.range(i);return r.deleteContents(),r.insertNode(e),this.caret.after(e),!1},insertParagraph:function(t){t.preventDefault();var e=document.createElement("p");e.innerHTML="<br>";var i=this.selection.get(),r=this.selection.range(i);return r.deleteContents(),r.insertNode(e),this.caret.start(e),!1},insertBreakLine:function(t){return this.keydown.insertBreakLineProcessing(t)},insertDblBreakLine:function(t){return this.keydown.insertBreakLineProcessing(t,!0)},insertBreakLineProcessing:function(t,e){t.stopPropagation();var i=document.createElement("br");if(this.insert.node(i),!0===e){var r=document.createElement("br");this.insert.node(r),this.caret.after(r)}else this.caret.after(i);return!1},wrapToParagraph:function(){var e=t(this.keydown.current),i=t("<p>").append(e.clone());e.replaceWith(i);var r=t(i).next();void 0!==r[0]&&"BR"===r[0].tagName&&r.remove(),this.caret.end(i)},replaceToParagraph:function(e){var i=this.selection.block(),r=t(i).prev(),o=i.innerHTML.replace(/<br\s?\/?>/gi,"");if(i.tagName===e&&this.utils.isEmpty(o)&&!t(i).hasClass("redactor-in")){var s=document.createElement("p");return t(i).replaceWith(s),this.keydown.setCaretToParagraph(s),!1}if("P"===i.tagName)return t(i).removeAttr("class").removeAttr("style"),this.detect.isIe()&&this.utils.isEmpty(o)&&this.utils.isInline(this.keydown.parent)&&t(i).on("input",t.proxy(function(){var e=this.selection.parent();if(this.utils.isInline(e)){var r=t(e).html();t(i).html(r),this.caret.end(i)}t(i).off("keyup")},this)),!1;if(r.hasClass(this.opts.videoContainerClass)){r.removeAttr("class");var s=document.createElement("p");return r.replaceWith(s),this.keydown.setCaretToParagraph(s),!1}},setCaretToParagraph:function(t){var e=document.createRange();e.setStart(t,0);var i=document.createTextNode("");e.insertNode(i),e.setStartAfter(i),e.collapse(!0);var r=window.getSelection();r.removeAllRanges(),r.addRange(e)},removeInvisibleSpace:function(){var e=t(this.keydown.current);0===e.text().search(/^\u200B$/g)&&e.remove()},removeEmptyListInTable:function(e){var i=t(this.keydown.current),r=t(this.keydown.parent),o=i.closest("td",this.$editor[0]);if(0!==o.length&&i.closest("li",this.$editor[0])&&1===r.children("li").length){if(!this.utils.isEmpty(i.text()))return;e.preventDefault(),i.remove(),r.remove(),this.caret.start(o)}},removeEmptyLists:function(){var e=function(){""===t.trim(this.innerHTML).replace(/\/t\/n/g,"")&&t(this).remove()};this.core.editor().find("li").each(e),this.core.editor().find("ul, ol").each(e)},formatEmpty:function(e){var i=t.trim(this.core.editor().html());if(this.utils.isEmpty(i))return e.preventDefault(),"inline"===this.opts.type||"pre"===this.opts.type?(this.core.editor().html(this.marker.html()),this.selection.restore()):(this.core.editor().html(this.opts.emptyHtml),this.focus.start()),!1}}},keyup:function(){return{init:function(e){if(!this.rtePaste){var i=e.which;this.keyup.block=this.selection.block(),this.keyup.current=this.selection.current(),this.keyup.parent=this.selection.parent(),this.keyup.lastShiftKey=e.shiftKey;if(!1===this.core.callback("keyup",e))return e.preventDefault(),!1;if(i===this.keyCode.ENTER&&this.keyup.block&&"FIGURE"===this.keyup.block.tagName){var r=t(this.keyup.block).prev();if(0!==r.length&&"FIGURE"===r[0].tagName){var o=this.utils.replaceToTag(r,"p");return void this.caret.start(o)}}if(i===this.keyCode.BACKSPACE||i===this.keyCode.DELETE){if(this.utils.isSelectAll())return this.focus.start(),void this.toolbar.setUnfixed();if(this.keyup.block&&this.keydown.block&&"FIGURE"===this.keyup.block.tagName&&this.utils.isStartOfElement(this.keydown.block)){e.preventDefault(),this.selection.save(),t(this.keyup.block).find("figcaption").remove(),t(this.keyup.block).find("img").first().remove(),this.utils.replaceToTag(this.keyup.block,"p");var s=this.marker.find();return t("html, body").animate({scrollTop:s.position().top+20},500),void this.selection.restore()}if(this.keyup.block&&"P"===this.keyup.block.tagName){var n=t(this.keyup.block).find("img").length;""===t(this.keyup.block).text().replace(/\u200B/g,"")&&0!==n&&this.utils.replaceToTag(this.keyup.block,"figure")}this.keyup.block&&"FIGURE"===this.keyup.block.tagName&&0===t(this.keyup.block).find("img").length&&(this.selection.save(),this.utils.replaceToTag(this.keyup.block,"p"),this.selection.restore())}this.linkify.isKey(i)&&(this.selection.save(),this.linkify.format(),this.selection.restore())}}}},lang:function(){return{load:function(){this.opts.curLang=this.opts.langs[this.opts.lang]},get:function(t){return void 0!==this.opts.curLang[t]?this.opts.curLang[t]:""}}},line:function(){return{insert:function(){this.buffer.set(),this.insert.html(this.line.getLineHtml());var t=this.core.editor().find("#redactor-hr-tmp-id");return t.removeAttr("id"),this.core.callback("insertedLine",t),t},getLineHtml:function(){var t='<hr id="redactor-hr-tmp-id" />';return!this.detect.isFirefox()&&this.utils.isEmpty()&&(t+="<p>"+this.opts.emptyHtml+"</p>"),t},removeOnBackspace:function(e){if(this.utils.isCollapsed()){var i=t(this.selection.block());if(0!==i.length&&this.utils.isStartOfElement(i)){var r=i.prev();r&&0!==r.length&&"HR"===r[0].tagName&&(e.preventDefault(),r.remove())}}}}},link:function(){return{get:function(){return t(this.selection.inlines("a"))},is:function(){var e=this.selection.nodes(),i=t(this.selection.current()).closest("a",this.core.editor()[0]);return!(0===i.length||e.length>1)&&i},unlink:function(t){void 0!==t&&t.preventDefault&&t.preventDefault(),this.buffer.set();var e=this.selection.inlines("a");if(0!==e.length){var i=this.link.replaceLinksToText(e);this.observe.closeAllTooltip(),this.core.callback("deletedLink",i)}},insert:function(e,i){var r=this.link.is();if(!0!==i&&!1===(e=this.link.buildLinkFromObject(r,e)))return!1;if(this.buffer.set(),e=this.core.callback("beforeInsertingLink",e),!1===r){r=t("<a />"),r=this.link.update(r,e),r=t(this.insert.node(r));var o=r.parent();!1===this.utils.isRedactorParent(o)&&r.wrap("<p>"),o.hasClass("redactor-unlink")&&o.replaceWith(function(){return t(this).contents()}),this.caret.after(r),this.core.callback("insertedLink",r)}else r=this.link.update(r,e),this.caret.after(r);return r},update:function(t,e){return t.text(e.text),t.attr("href",e.url),this.link.target(t,e.target),t},target:function(t,e){return e?t.attr("target","_blank"):t.removeAttr("target")},show:function(e){void 0!==e&&e.preventDefault&&e.preventDefault(),this.observe.closeAllTooltip();var i=this.link.is();this.link.buildModal(i);var r=this.link.buildLinkFromElement(i);r.url=this.link.removeSelfHostFromUrl(r.url),this.opts.linkNewTab&&!i&&(r.target=!0),this.link.setModalValues(r),this.modal.show(),this.detect.isDesktop()&&t("#redactor-link-url").focus()},setModalValues:function(e){t("#redactor-link-blank").prop("checked",e.target),t("#redactor-link-url").val(e.url),t("#redactor-link-url-text").val(e.text)},buildModal:function(e){this.modal.load("link",this.lang.get(!1===e?"link-insert":"link-edit"),600),this.modal.getActionButton().text(this.lang.get(!1===e?"insert":"save")).on("click",t.proxy(this.link.callback,this))},callback:function(){var t=this.link.buildLinkFromModal();if(!1===t)return!1;this.modal.close(),this.link.insert(t,!0)},cleanUrl:function(e){return void 0===e?"":t.trim(e.replace(/[^\W\w\D\d+&\'@#\/%?=~_|!:,.;\(\)]/gi,""))},cleanText:function(e){return void 0===e?"":t.trim(e.replace(/(<([^>]+)>)/gi,""))},getText:function(t){return""===t.text&&""!==t.url?this.link.truncateUrl(t.url.replace(/<|>/g,"")):t.text},isUrl:function(t){return!!new RegExp("^((https?|ftp):\\/\\/)?(([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(t)&&t},isMailto:function(t){return-1!==t.search("@")&&!1===/(http|ftp|https):\/\//i.test(t)},isEmpty:function(t){return""===t.url||""===t.text&&""===t.url},truncateUrl:function(t){return t.length>this.opts.linkSize?t.substring(0,this.opts.linkSize)+"...":t},parse:function(t){return this.link.isMailto(t.url)?t.url="mailto:"+t.url.replace("mailto:",""):0!==t.url.search("#")&&this.opts.linkValidation&&(t.url=this.link.isUrl(t.url)?"http://"+t.url.replace(/(ftp|https?):\/\//gi,""):t.url),!this.link.isEmpty(t)&&!1!==t.url&&t},buildLinkFromModal:function(){var e={};return e.url=this.link.cleanUrl(t("#redactor-link-url").val()),e.text=this.link.cleanText(t("#redactor-link-url-text").val()),e.text=this.link.getText(e),e.target=!!t("#redactor-link-blank").prop("checked"),this.link.parse(e)},buildLinkFromObject:function(t,e){return e.url=this.link.cleanUrl(e.url),e.text=void 0===e.text&&this.selection.is()?this.selection.text():this.link.cleanText(e.text),e.text=this.link.getText(e),e.target=!1===t?e.target:this.link.buildTarget(t),this.link.parse(e)},buildLinkFromElement:function(t){var e={url:"",text:this.selection.is()?this.selection.text():"",target:!1};return!1!==t&&(e.url=t.attr("href"),e.text=t.text(),e.target=this.link.buildTarget(t)),e},buildTarget:function(t){return void 0!==t.attr("target")&&"_blank"===t.attr("target")},removeSelfHostFromUrl:function(t){var e=self.location.href.replace("#","").replace(/\/$/i,"");return t.replace(/^\/\#/,"#").replace(e,"").replace("mailto:","")},replaceLinksToText:function(e){var i,r=t.each(e,function(e,r){var o=t(r),s=t('<span class="redactor-unlink" />').append(o.contents());return o.replaceWith(s),0===e&&(i=s),o});return 1===e.length&&this.selection.isCollapsed()&&this.caret.after(i),r}}},linkify:function(){return{isKey:function(t){return t===this.keyCode.ENTER||t===this.keyCode.SPACE},isLink:function(t){return t.nodeValue.match(this.opts.regexps.linkyoutube)||t.nodeValue.match(this.opts.regexps.linkvimeo)||t.nodeValue.match(this.opts.regexps.linkimage)||t.nodeValue.match(this.opts.regexps.url)},isFiltered:function(e,i){return 3===i.nodeType&&""!==t.trim(i.nodeValue)&&!t(i).parent().is("pre")&&this.linkify.isLink(i)},handler:function(e,i){var r=t(i),o=r.text(),s=o;s=s.match(this.opts.regexps.linkyoutube)||s.match(this.opts.regexps.linkvimeo)?this.linkify.convertVideoLinks(s):s.match(this.opts.regexps.linkimage)?this.linkify.convertImages(s):this.linkify.convertLinks(s),r.before(o.replace(o,s)).remove()},format:function(){if(this.opts.linkify&&!this.utils.isCurrentOrParent("pre")){this.core.editor().find(":not(iframe,img,a,pre,code,.redactor-unlink)").addBack().contents().filter(t.proxy(this.linkify.isFiltered,this)).each(t.proxy(this.linkify.handler,this));var e,i=this.core.editor().find(".redactor-linkify-object").each(t.proxy(function(i,r){return e=t(r),e.removeClass("redactor-linkify-object"),""===e.attr("class")&&e.removeAttr("class"),"A"===r.tagName&&this.core.callback("insertedLink",e),e},this));setTimeout(t.proxy(function(){this.code.sync(),this.core.callback("linkify",i)},this),100)}},convertVideoLinks:function(t){var e='<div class="'+this.opts.videoContainerClass+' redactor-linkify-object"><iframe class="redactor-linkify-object" width="500" height="281" src="',i='" frameborder="0" allowfullscreen></iframe></div>';return t.match(this.opts.regexps.linkyoutube)&&(t=t.replace(this.opts.regexps.linkyoutube,e+"//www.youtube.com/embed/$1"+i)),t.match(this.opts.regexps.linkvimeo)&&(t=t.replace(this.opts.regexps.linkvimeo,e+"//player.vimeo.com/video/$2"+i)),t},convertImages:function(t){var e=t.match(this.opts.regexps.linkimage);return e?t.replace(t,'<img src="'+e+'" class="redactor-linkify-object" />'):t},convertLinks:function(e){var i=e.match(this.opts.regexps.url);if(!i)return e;i=t.grep(i,function(e,r){return t.inArray(e,i)===r});for(var r=i.length,o=0;o<r;o++){var s=i[o],n=s,a=null!==s.match(/(https?|ftp):\/\//i)?"":"http://";n.length>this.opts.linkSize&&(n=n.substring(0,this.opts.linkSize)+"..."),-1===n.search("%")&&(n=decodeURIComponent(n));var l="\\b";-1!==t.inArray(s.slice(-1),["/","&","="])&&(l="");var c=new RegExp("("+s.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")+l+")","g"),d="";!1!==this.opts.pasteLinkTarget&&(d=' target="'+this.opts.pasteLinkTarget+'"'),e=e.replace(c,'<a href="'+a+t.trim(s)+'"'+d+' class="redactor-linkify-object">'+t.trim(n)+"</a>")}return e}}},list:function(){return{toggle:function(e){if(!this.utils.inBlocks(["table","td","th","tr"])){e="orderedlist"===e?"ol":e,e="unorderedlist"===e?"ul":e,e=e.toLowerCase(),this.buffer.set(),this.selection.save();var i=this.list._getBlocks(),r=this.selection.block(),o=t(r).parents("ul, ol").last();return 0===i.length&&0!==o.length&&(i=[o.get(0)]),i=this.list._isUnformat(e,i)?this.list._unformat(e,i):this.list._format(e,i),this.selection.restore(),i}},get:function(){var e=this.selection.current(),i=t(e).closest("ul, ol",this.core.editor()[0]);return 0!==i.length&&i},combineAfterAndBefore:function(e){var i=t(e).prev(),r=t(e).next(),o=e&&"P"===e.tagName&&("<br>"===e.innerHTML||""===e.innerHTML),s=1===i.closest("ol, ul",this.core.editor()[0]).length&&1===r.closest("ol, ul",this.core.editor()[0]).length;return!(!o||!s)&&(i.children("li").last().append(this.marker.get()),i.append(r.contents()),this.selection.restore(),!0)},_getBlocks:function(){for(var e=[],i=this.selection.blocks(),r=0;r<i.length;r++){t(i[r]).parent().hasClass("redactor-in")&&e.push(i[r])}return e},_isUnformat:function(t,e){for(var i=0,r=0;r<e.length;r++)if(3!==e[r].nodeType){var o=e[r].tagName.toLowerCase();o!==t&&"figure"!==o||i++}return i===e.length},_uniteBlocks:function(e,i){for(var r=0,o={0:[]},s=!1,n=0;n<e.length;n++){var a=t(e[n]),l=a.closest("th, td");0!==l.length?(l.get(0)!==s&&(r++,o[r]=[]),this.list._isUniteBlock(e[n],i)&&o[r].push(e[n])):this.list._isUniteBlock(e[n],i)?o[r].push(e[n]):(r++,o[r]=[]),s=l.get()}return o},_isUniteBlock:function(t,e){return 3===t.nodeType||-1!==e.indexOf(t.tagName.toLowerCase())},_createList:function(e,i,r){var o=i[i.length-1],s=t(o),n=t("<"+e+">");return s.after(n),n},_createListItem:function(e){var i=t("<li>");if(3===e.nodeType)i.append(e);else{var r=t(e);i.append(r.contents()),r.remove()}return i},_format:function(e,i){var r=["p","div","blockquote","pre","h1","h2","h3","h4","h5","h6","ul","ol"],o=this.list._uniteBlocks(i,r),s=[];for(var n in o){for(var a=o[n],l=this.list._createList(e,o[n]),c=0;c<a.length;c++){var d;3===a[c].nodeType||"UL"!==a[c].tagName&&"OL"!==a[c].tagName?(d=this.list._createListItem(a[c]),l.append(d)):(d=t(a[c]).contents(),l.append(d))}s.push(l.get(0))}return s},_unformat:function(e,i){if(1===i.length){var r=t(i[0]),o=r.find("li"),s=this.selection.blocks(["li"]),n=this.selection.block(),a=t(n).closest("li");if(0===s.length&&0!==a.length&&(s=[a.get(0)]),s.length===o.length)return this.list._unformatEntire(i[0]);var l=this.list._getItemsPosition(o,s);if("Top"===l)return this.list._unformatAtSide("before",s,r);if("Bottom"===l)return s.reverse(),this.list._unformatAtSide("after",s,r);if("Middle"===l){var c=t(s[s.length-1]),d=!1,h=!1,u=t("<"+r.get(0).tagName.toLowerCase()+">");o.each(function(e,i){if(d){var r=t(i);r.children("ul, ol").length;0!==r.closest(".redactor-split-item").length||!1!==h&&0!==r.closest(h).length||r.addClass("redactor-split-item"),h=r}i===c.get(0)&&(d=!0)}),o.filter(".redactor-split-item").each(function(e,i){t(i).removeClass("redactor-split-item"),u.append(i)}),r.after(u),s.reverse();for(var p=0;p<s.length;p++){var f=t(s[p]),g=this.list._createUnformatContainer(f);r.after(g),g.find("ul, ol").remove(),f.remove()}return}}else for(var p=0;p<i.length;p++)3!==i[p].nodeType&&i[p].tagName.toLowerCase()===e&&this.list._unformatEntire(i[p])},_unformatEntire:function(e){var i=t(e);i.find("li").each(function(e,r){var o=t(r),s=this.list._createUnformatContainer(o);o.remove(),i.before(s)}.bind(this)),i.remove()},_unformatAtSide:function(e,i,r){for(var o=0;o<i.length;o++){var s=t(i[o]),n=this.list._createUnformatContainer(s);r[e](n);var a=n.find("ul, ol").first();s.append(a),a.each(function(e,r){var o=t(r),s=o.closest("li");s.get(0)===i[e]&&(o.unwrap(),s.addClass("r-unwrapped"))}),this.utils.isEmpty(s.html())&&s.remove()}r.find(".r-unwrapped").each(function(e){var i=t(e);""===i.html().trim()?i.remove():i.removeClass("r-unwrapped")})},_getItemsPosition:function(t,e){var i="Middle",r=e[0],o=e[e.length-1],s=t.first().get(0),n=t.last().get(0);return s===r&&n!==o?i="Top":s!==r&&n===o&&(i="Bottom"),i},_createUnformatContainer:function(e){var i=t("<p>");return i.append(e.contents()),i}}},marker:function(){return{get:function(t){t=void 0===t?1:t;var e=document.createElement("span");return e.id="selection-marker-"+t,e.className="redactor-selection-marker",e.innerHTML=this.opts.invisibleSpace,e},html:function(t){return this.utils.getOuterHtml(this.marker.get(t))},find:function(t){return t=void 0===t?1:t,this.core.editor().find("span#selection-marker-"+t)},insert:function(){var t=this.selection.get(),e=this.selection.range(t);this.marker.insertNode(e,this.marker.get(1),!0),e&&!1===e.collapsed&&this.marker.insertNode(e,this.marker.get(2),!1)},remove:function(){this.core.editor().find(".redactor-selection-marker").each(this.marker.iterateRemove)},insertNode:function(e,i,r){var o=this.selection.parent();if(null!==e&&0!==t(o).closest(".redactor-in").length){e=e.cloneRange();try{e.collapse(r),e.insertNode(i)}catch(t){this.focus.start()}}},iterateRemove:function(e,i){var r=t(i),o=r.text().replace(/\u200B/g,"");r.parent()[0];""===o?r.remove():r.replaceWith(function(){return t(this).contents()})}}},modal:function(){return{callbacks:{},templates:function(){this.opts.modal={"image-edit":String()+'<div class="redactor-modal-tab redactor-group" data-title="General"><div id="redactor-image-preview" class="redactor-modal-tab-side"></div><div class="redactor-modal-tab-area"><section><label>'+this.lang.get("title")+'</label><input type="text" id="redactor-image-title" /></section><section><label>'+this.lang.get("caption")+'</label><input type="text" id="redactor-image-caption" aria-label="'+this.lang.get("caption")+'" /></section><section><label>'+this.lang.get("link")+'</label><input type="text" id="redactor-image-link" aria-label="'+this.lang.get("link")+'" /></section><section><label class="redactor-image-position-option">'+this.lang.get("image-position")+'</label><select class="redactor-image-position-option" id="redactor-image-align" aria-label="'+this.lang.get("image-position")+'"><option value="none">'+this.lang.get("none")+'</option><option value="left">'+this.lang.get("left")+'</option><option value="center">'+this.lang.get("center")+'</option><option value="right">'+this.lang.get("right")+'</option></select></section><section><label class="checkbox"><input type="checkbox" id="redactor-image-link-blank" aria-label="'+this.lang.get("link-in-new-tab")+'"> '+this.lang.get("link-in-new-tab")+'</label></section><section><button id="redactor-modal-button-action">'+this.lang.get("insert")+'</button><button id="redactor-modal-button-cancel">'+this.lang.get("cancel")+'</button><button id="redactor-modal-button-delete" class="redactor-modal-button-offset">'+this.lang.get("delete")+"</button></section></div></div>",image:String()+'<div class="redactor-modal-tab" data-title="Upload"><section><div id="redactor-modal-image-droparea"></div></section></div>',file:String()+'<div class="redactor-modal-tab" data-title="Upload"><section><label>'+this.lang.get("filename")+' <span class="desc">('+this.lang.get("optional")+')</span></label><input type="text" id="redactor-filename" aria-label="'+this.lang.get("filename")+'" /><br><br></section><section><div id="redactor-modal-file-upload"></div></section></div>',link:String()+'<div class="redactor-modal-tab" data-title="General"><section><label>URL</label><input type="url" id="redactor-link-url" aria-label="URL" /></section><section><label>'+this.lang.get("text")+'</label><input type="text" id="redactor-link-url-text" aria-label="'+this.lang.get("text")+'" /></section><section><label class="checkbox"><input type="checkbox" id="redactor-link-blank"> '+this.lang.get("link-in-new-tab")+'</label></section><section><button id="redactor-modal-button-action">'+this.lang.get("insert")+'</button><button id="redactor-modal-button-cancel">'+this.lang.get("cancel")+"</button></section></div>"},t.extend(this.opts,this.opts.modal)},addCallback:function(t,e){this.modal.callbacks[t]=e},addTemplate:function(t,e){this.opts.modal[t]=e},getTemplate:function(t){return this.opts.modal[t]},getModal:function(){return this.$modalBody},getActionButton:function(){return this.$modalBody.find("#redactor-modal-button-action")},getCancelButton:function(){return this.$modalBody.find("#redactor-modal-button-cancel")},getDeleteButton:function(){return this.$modalBody.find("#redactor-modal-button-delete")},load:function(t,e,i){void 0!==this.$modalBox&&this.$modalBox.hasClass("open")||(this.modal.templateName=t,this.modal.width=i,this.modal.build(),this.modal.enableEvents(),this.modal.setTitle(e),this.modal.setDraggable(),this.modal.setContent(),void 0!==this.modal.callbacks[t]&&this.modal.callbacks[t].call(this))},show:function(){this.detect.isDesktop()||document.activeElement.blur(),this.selection.save(),this.modal.buildTabber(),this.detect.isMobile()&&(this.modal.width="96%"),setTimeout(t.proxy(this.modal.buildWidth,this),0),t(window).on("resize.redactor-modal",t.proxy(this.modal.buildWidth,this)),this.$modalOverlay.redactorAnimation("fadeIn",{duration:.25}),this.$modalBox.addClass("open").show(),this.$modal.redactorAnimation("fadeIn",{timing:"cubic-bezier(0.175, 0.885, 0.320, 1.105)"},t.proxy(function(){this.utils.saveScroll(),this.utils.disableBodyScroll(),this.core.callback("modalOpened",this.modal.templateName,this.$modal),t(document).off("focusin.modal"),this.$modal.find("input[type=text],input[type=url],input[type=email]").on("keydown.redactor-modal",t.proxy(this.modal.setEnter,this))},this))},buildWidth:function(){var e=t(window).height(),i=t(window).width(),r="number"==typeof this.modal.width;!r&&this.modal.width.match(/%$/)?this.$modal.css({width:this.modal.width,"margin-bottom":"16px"}):parseInt(this.modal.width)>i?this.$modal.css({width:"96%","margin-bottom":"2%"}):(r&&(this.modal.width+="px"),this.$modal.css({width:this.modal.width,"margin-bottom":"16px"}));var o=this.$modal.outerHeight(),s=e/2-o/2+"px";this.detect.isMobile()?s="2%":o>e&&(s="16px"),this.$modal.css("margin-top",s)},buildTabber:function(){this.modal.tabs=this.$modal.find(".redactor-modal-tab"),this.modal.tabs.length<2||(this.modal.$tabsBox=t('<div id="redactor-modal-tabber" />'),t.each(this.modal.tabs,t.proxy(function(e,i){var r=t('<a href="#" rel="'+e+'" />').text(t(i).attr("data-title"));r.on("click",t.proxy(this.modal.showTab,this)),0===e&&r.addClass("active"),this.modal.$tabsBox.append(r)},this)),this.$modalBody.prepend(this.modal.$tabsBox))},showTab:function(e){e.preventDefault();var i=t(e.target),r=i.attr("rel");return this.modal.tabs.hide(),this.modal.tabs.eq(r).show(),t("#redactor-modal-tabber").find("a").removeClass("active"),i.addClass("active"),!1},setTitle:function(t){this.$modalHeader.html(t)},setContent:function(){this.$modalBody.html(this.modal.getTemplate(this.modal.templateName)),this.modal.getCancelButton().on("mousedown",t.proxy(this.modal.close,this))},setDraggable:function(){void 0!==t.fn.draggable&&(this.$modal.draggable({handle:this.$modalHeader}),this.$modalHeader.css("cursor","move"))},setEnter:function(t){13===t.which&&(t.preventDefault(),this.modal.getActionButton().click())},build:function(){this.modal.buildOverlay(),this.$modalBox=t('<div id="redactor-modal-box"/>').hide(),this.$modal=t('<div id="redactor-modal" role="dialog" />'),this.$modalHeader=t('<div id="redactor-modal-header" />'),this.$modalClose=t('<button type="button" id="redactor-modal-close" aria-label="'+this.lang.get("close")+'" />').html("×"),this.$modalBody=t('<div id="redactor-modal-body" />'),this.$modal.append(this.$modalHeader),this.$modal.append(this.$modalBody),this.$modal.append(this.$modalClose),this.$modalBox.append(this.$modal),this.$modalBox.appendTo(document.body)},buildOverlay:function(){this.$modalOverlay=t('<div id="redactor-modal-overlay">').hide(),t("body").prepend(this.$modalOverlay)},enableEvents:function(){this.$modalClose.on("mousedown.redactor-modal",t.proxy(this.modal.close,this)),t(document).on("keyup.redactor-modal",t.proxy(this.modal.closeHandler,this)),this.core.editor().on("keyup.redactor-modal",t.proxy(this.modal.closeHandler,this)),this.$modalBox.on("click.redactor-modal",t.proxy(this.modal.close,this))},disableEvents:function(){this.$modalClose.off("mousedown.redactor-modal"),t(document).off("keyup.redactor-modal"),this.core.editor().off("keyup.redactor-modal"),this.$modalBox.off("click§.redactor-modal"),t(window).off("resize.redactor-modal")},closeHandler:function(t){t.which===this.keyCode.ESC&&this.modal.close(!1)},close:function(e){if(e){if("redactor-modal-button-cancel"!==t(e.target).attr("id")&&e.target!==this.$modalClose[0]&&e.target!==this.$modalBox[0])return;e.preventDefault()}this.$modalBox&&(this.selection.restore(),this.modal.disableEvents(),this.utils.enableBodyScroll(),this.utils.restoreScroll(),this.$modalOverlay.redactorAnimation("fadeOut",{duration:.4},t.proxy(function(){this.$modalOverlay.remove()},this)),this.$modal.redactorAnimation("fadeOut",{duration:.3,timing:"cubic-bezier(0.175, 0.885, 0.320, 1.175)"},t.proxy(function(){void 0!==this.$modalBox&&(this.$modalBox.remove(),this.$modalBox=void 0),this.core.callback("modalClosed",this.modal.templateName)},this)))}}},observe:function(){return{load:function(){void 0===this.opts.destroyed&&(this.observe.links(),this.observe.images())},isCurrent:function(e,i){return void 0===i&&(i=t(this.selection.current())),i.is(e)||i.parents(e).length>0},toolbar:function(){this.observe.buttons(),this.observe.dropdowns()},buttons:function(e,i){var r=this.selection.current(),o=this.selection.parent();if(!1!==e?this.button.setInactiveAll():this.button.setInactiveAll(i),!1===e&&"html"!==i)return void(-1!==t.inArray(i,this.opts.activeButtons)&&this.button.toggleActive(i));this.utils.isRedactorParent(r)&&("none"!==this.core.editor().css("display")&&(this.utils.isCurrentOrParentHeader()||this.utils.isCurrentOrParent(["table","pre","blockquote","li"])?this.button.disable("horizontalrule"):this.button.enable("horizontalrule")),t.each(this.opts.activeButtonsStates,t.proxy(function(e,i){var s=t(o).closest(e,this.$editor[0]),n=t(r).closest(e,this.$editor[0]);(0===s.length||this.utils.isRedactorParent(s))&&this.utils.isRedactorParent(n)&&(0===s.length&&0===n.closest(e,this.$editor[0]).length||this.button.setActive(i))},this)))},dropdowns:function(){var e=t("<div />").html(this.selection.html()).find("a").length,i=t(this.selection.current()),r=this.utils.isRedactorParent(i);t.each(this.opts.observe.dropdowns,t.proxy(function(t,o){var s=o.observe,n=s.element,a=o.item,l=void 0!==s.in&&s.in,c=void 0!==s.out&&s.out;i.closest(n).length>0&&r||"a"===n&&0!==e?this.observe.setDropdownProperties(a,l,c):this.observe.setDropdownProperties(a,c,l)},this))},setDropdownProperties:function(t,e,i){i&&void 0!==i.attr&&this.observe.setDropdownAttr(t,i.attr,!0),void 0!==e.attr&&this.observe.setDropdownAttr(t,e.attr),void 0!==e.title&&t.find("span").text(e.title)},setDropdownAttr:function(e,i,r){t.each(i,function(t,i){"class"===t?r?e.removeClass(i):e.addClass(i):r?e.removeAttr(t):e.attr(t,i)})},addDropdown:function(t,e,i){void 0!==i.observe&&(i.item=t,this.opts.observe.dropdowns.push(i))},images:function(){this.opts.imageEditable&&(this.core.editor().addClass("redactor-layer-img-edit"),this.core.editor().find("img").each(t.proxy(function(e,i){var r=t(i);r.closest("a",this.$editor[0]).on("click",function(t){t.preventDefault()}),this.image.setEditable(r)},this)))},links:function(){this.opts.linkTooltip&&this.core.editor().find("a").each(t.proxy(function(e,i){var r=t(i);!0!==r.data("cached")&&(r.data("cached",!0),r.on("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.showTooltip,this)))},this))},getTooltipPosition:function(t){return t.offset()},showTooltip:function(e){var i=t(e.target);if("IMG"!==i[0].tagName&&("A"!==i[0].tagName&&(i=i.closest("a",this.$editor[0])),"A"===i[0].tagName)){var r=i,o=this.observe.getTooltipPosition(r),s=t('<span class="redactor-link-tooltip"></span>'),n=r.attr("href")
-;void 0===n&&(n=""),n.length>24&&(n=n.substring(0,24)+"...");var a=t('<a href="'+r.attr("href")+'" target="_blank" />').html(n).addClass("redactor-link-tooltip-action"),l=t('<a href="#" />').html(this.lang.get("edit")).on("click",t.proxy(this.link.show,this)).addClass("redactor-link-tooltip-action"),c=t('<a href="#" />').html(this.lang.get("unlink")).on("click",t.proxy(this.link.unlink,this)).addClass("redactor-link-tooltip-action");s.append(a).append(" | ").append(l).append(" | ").append(c);var d=parseInt(r.css("line-height"),10),h=Math.ceil((e.pageY-o.top)/d),u=o.top+h*d;s.css({top:u+"px",left:o.left+"px"}),t(".redactor-link-tooltip").remove(),t("body").append(s),this.core.editor().on("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this)),t(document).on("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this))}},closeAllTooltip:function(){t(".redactor-link-tooltip").remove()},closeTooltip:function(e){e=e.originalEvent||e;var i=e.target,r=t(i).closest("a",this.$editor[0]);0!==r.length&&"A"===r[0].tagName&&"A"!==i.tagName||"A"===i.tagName&&this.utils.isRedactorParent(i)||t(i).hasClass("redactor-link-tooltip-action")||(this.observe.closeAllTooltip(),this.core.editor().off("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this)),t(document).off("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this)))}}},offset:function(){return{get:function(e){var i=this.offset.clone(e);if(!1===i)return 0;var r=document.createElement("div");return r.appendChild(i.cloneContents()),r.innerHTML=r.innerHTML.replace(/<img(.*?[^>])>$/gi,"i"),t.trim(t(r).text()).replace(/[\t\n\r\n]/g,"").replace(/\u200B/g,"").length},clone:function(t){var e=this.selection.get(),i=this.selection.range(e);if(null===i&&void 0===t)return!1;if(!1===(t=void 0===t?this.$editor:t))return!1;t=t[0]||t;var r=i.cloneRange();return r.selectNodeContents(t),r.setEnd(i.endContainer,i.endOffset),r},set:function(t,e){e=void 0===e?t:e,this.focus.is()||this.focus.start();for(var i,r=this.selection.get(),o=this.selection.range(r),s=0,n=document.createTreeWalker(this.$editor[0],NodeFilter.SHOW_TEXT,null,null);null!==(i=n.nextNode());)if(s+=i.nodeValue.length,s>t&&(o.setStart(i,i.nodeValue.length+t-s),t=1/0),s>=e){o.setEnd(i,i.nodeValue.length+e-s);break}o.collapse(!1),this.selection.update(r,o)}}},paragraphize:function(){return{load:function(e){return!1===this.opts.paragraphize||"inline"===this.opts.type||"pre"===this.opts.type?e:""===e||"<p></p>"===e?this.opts.emptyHtml:(e+="\n",this.paragraphize.safes=[],this.paragraphize.z=0,e=e.replace(/(<br\s?\/?>){1,}\n?<\/blockquote>/gi,"</blockquote>"),e=e.replace(/<\/pre>/gi,"</pre>\n\n"),e=e.replace(/<p>\s<br><\/p>/gi,"<p></p>"),e=this.paragraphize.getSafes(e),e=e.replace("<br>","\n"),e=this.paragraphize.convert(e),e=this.paragraphize.clear(e),e=this.paragraphize.restoreSafes(e),e=e.replace(new RegExp("<br\\s?/?>\n?<("+this.opts.paragraphizeBlocks.join("|")+")(.*?[^>])>","gi"),"<p><br /></p>\n<$1$2>"),t.trim(e))},getSafes:function(e){var i=t("<div />").append(e);return i.find("blockquote p").replaceWith(function(){return t(this).append("<br />").contents()}),i.find(this.opts.paragraphizeBlocks.join(", ")).each(t.proxy(function(e,i){return this.paragraphize.z++,this.paragraphize.safes[this.paragraphize.z]=i.outerHTML,t(i).replaceWith("\n#####replace"+this.paragraphize.z+"#####\n\n")},this)),i.find("span.redactor-selection-marker").each(t.proxy(function(e,i){return this.paragraphize.z++,this.paragraphize.safes[this.paragraphize.z]=i.outerHTML,t(i).replaceWith("\n#####replace"+this.paragraphize.z+"#####\n\n")},this)),i.html()},restoreSafes:function(e){return t.each(this.paragraphize.safes,function(t,i){i=void 0!==i?i.replace(/\$/g,"$"):i,e=e.replace("#####replace"+t+"#####",i)}),e},convert:function(e){e=e.replace(/\r\n/g,"xparagraphmarkerz"),e=e.replace(/\n/g,"xparagraphmarkerz"),e=e.replace(/\r/g,"xparagraphmarkerz");var i=/\s+/g;e=e.replace(i," "),e=t.trim(e);var r=/xparagraphmarkerzxparagraphmarkerz/gi;e=e.replace(r,"</p><p>");var o=/xparagraphmarkerz/gi;return e=e.replace(o,"<br>"),e="<p>"+e+"</p>",e=e.replace("<p></p>",""),e=e.replace("\r\n\r\n",""),e=e.replace(/<\/p><p>/g,"</p>\r\n\r\n<p>"),e=e.replace(new RegExp("<br\\s?/?></p>","g"),"</p>"),e=e.replace(new RegExp("<p><br\\s?/?>","g"),"<p>"),e=e.replace(new RegExp("<p><br\\s?/?>","g"),"<p>"),e=e.replace(new RegExp("<br\\s?/?></p>","g"),"</p>"),e=e.replace(/<p> <\/p>/gi,""),e=e.replace(/<p>\s?<br> <\/p>/gi,""),e=e.replace(/<p>\s?<br>/gi,"<p>")},clear:function(t){return t=t.replace(/<p>(.*?)#####replace(.*?)#####\s?<\/p>/gi,"<p>$1</p>#####replace$2#####"),t=t.replace(/(<br\s?\/?>){2,}<\/p>/gi,"</p>"),t=t.replace(new RegExp("</blockquote></p>","gi"),"</blockquote>"),t=t.replace(new RegExp("<p></blockquote>","gi"),"</blockquote>"),t=t.replace(new RegExp("<p><blockquote>","gi"),"<blockquote>"),t=t.replace(new RegExp("<blockquote></p>","gi"),"<blockquote>"),t=t.replace(new RegExp("<p><p ","gi"),"<p "),t=t.replace(new RegExp("<p><p>","gi"),"<p>"),t=t.replace(new RegExp("</p></p>","gi"),"</p>"),t=t.replace(new RegExp("<p>\\s?</p>","gi"),""),t=t.replace(new RegExp("\n</p>","gi"),"</p>"),t=t.replace(new RegExp("<p>\t?\t?\n?<p>","gi"),"<p>"),t=t.replace(new RegExp("<p>\t*</p>","gi"),"")}}},paste:function(){return{init:function(e){this.rtePaste=!0;var i=!("pre"!==this.opts.type&&!this.utils.isCurrentOrParent("pre"));if(this.detect.isDesktop()&&!this.paste.pre&&this.opts.clipboardImageUpload&&this.opts.imageUpload&&this.paste.detectClipboardUpload(e))return void(this.detect.isIe()&&setTimeout(t.proxy(this.paste.clipboardUpload,this),100));this.utils.saveScroll(),this.selection.save(),this.paste.createPasteBox(i),t(window).on("scroll.redactor-freeze",t.proxy(function(){t(window).scrollTop(this.saveBodyScroll)},this)),setTimeout(t.proxy(function(){var e=this.paste.getPasteBoxCode(i);this.buffer.set(),this.selection.restore(),this.utils.restoreScroll();var r=this.clean.getCurrentType(e);e=this.clean.onPaste(e,r);var o=this.core.callback("paste",e);e=void 0===o?e:o,this.paste.insert(e,r),this.rtePaste=!1,i&&this.clean.cleanPre(),t(window).off("scroll.redactor-freeze")},this),1)},getPasteBoxCode:function(t){var e=t?this.$pasteBox.val():this.$pasteBox.html();return this.$pasteBox.remove(),e},createPasteBox:function(e){var i={position:"fixed",width:"1px",top:0,left:"-9999px"};this.$pasteBox=e?t("<textarea>").css(i):t("<div>").attr("contenteditable","true").css(i),this.paste.appendPasteBox(),this.$pasteBox.focus()},appendPasteBox:function(){if(this.detect.isIe())this.core.box().append(this.$pasteBox);else{var e=t(".modal-body:visible");e.length>0?e.append(this.$pasteBox):t("body").prepend(this.$pasteBox)}},detectClipboardUpload:function(e){e=e.originalEvent||e;var i=e.clipboardData;if(this.detect.isIe()||this.detect.isFirefox())return!1;if(-1!==i.types.indexOf("public.tiff"))return e.preventDefault(),!1;if(i.items&&i.items.length){var r=i.items[0].getAsFile();if(null===r)return!1;var o=new FileReader;return o.readAsDataURL(r),o.onload=t.proxy(this.paste.insertFromClipboard,this),!0}},clipboardUpload:function(){var e=this.$editor.find("img");t.each(e,t.proxy(function(e,i){if(-1!==i.src.search(/^data\:image/i)){var r=window.FormData?new FormData:null;if(window.FormData){this.upload.direct=!0,this.upload.type="image",this.upload.url=this.opts.imageUpload,this.upload.callback=t.proxy(function(e){if(this.detect.isIe())t(i).wrap(t("<figure />"));else{var r=t(i).parent();this.utils.replaceToTag(r,"figure")}i.src=e.url,this.core.callback("imageUpload",t(i),e)},this);var o=this.utils.dataURItoBlob(i.src);r.append("clipboard",1),r.append(this.opts.imageUploadParam,o),this.progress.show(),this.upload.send(r,!1),this.code.sync(),this.rtePaste=!1}}},this))},insertFromClipboard:function(t){var e=window.FormData?new FormData:null;if(window.FormData){this.upload.direct=!0,this.upload.type="image",this.upload.url=this.opts.imageUpload,this.upload.callback=this.image.insert;var i=this.utils.dataURItoBlob(t.target.result);e.append("clipboard",1),e.append(this.opts.imageUploadParam,i),this.progress.show(),this.upload.send(e,t),this.rtePaste=!1}},insert:function(e,i){i.pre?this.insert.raw(e):i.text?this.insert.text(e):this.insert.html(e,i),this.detect.isFirefox()&&this.opts.imageUpload&&this.opts.clipboardImageUpload&&setTimeout(t.proxy(this.paste.clipboardUpload,this),100)}}},placeholder:function(){return{enable:function(){setTimeout(t.proxy(function(){return this.placeholder.isEditorEmpty()?this.placeholder.show():this.placeholder.hide()},this),5)},show:function(){this.core.editor().addClass("redactor-placeholder")},update:function(t){this.opts.placeholder=t,this.core.editor().attr("placeholder",t)},hide:function(){this.core.editor().removeClass("redactor-placeholder")},is:function(){return this.core.editor().hasClass("redactor-placeholder")},init:function(){this.placeholder.enabled()&&(this.utils.isEditorRelative()||this.utils.setEditorRelative(),this.placeholder.build(),this.placeholder.buildPosition(),this.placeholder.enable(),this.placeholder.enableEvents())},enabled:function(){return this.opts.placeholder?this.core.element().attr("placeholder",this.opts.placeholder):this.placeholder.isAttr()},enableEvents:function(){this.core.editor().on("keydown.redactor-placeholder."+this.uuid,t.proxy(this.placeholder.enable,this))},disableEvents:function(){this.core.editor().off(".redactor-placeholder."+this.uuid)},build:function(){this.core.editor().attr("placeholder",this.core.element().attr("placeholder"))},buildPosition:function(){var e=t("<style />");e.addClass("redactor-placeholder-style-tag"),e.html("#"+this.core.id()+".redactor-placeholder::after "+this.placeholder.getPosition()),t("head").append(e)},getPosition:function(){return"{ top: "+this.core.editor().css("padding-top")+"; left: "+this.core.editor().css("padding-left")+"; }"},isEditorEmpty:function(){var e=t.trim(this.core.editor().html()).replace(/[\t\n]/g,""),i=["","<p></p>","<p><br></p>",this.opts.emptyHtmlRendered];return-1!==t.inArray(e,i)},isAttr:function(){return void 0!==this.core.element().attr("placeholder")&&""!==this.core.element().attr("placeholder")},destroy:function(){this.core.editor().removeAttr("placeholder"),this.placeholder.hide(),this.placeholder.disableEvents(),t(".redactor-placeholder-style-tag").remove()}}},progress:function(){return{$box:null,$bar:null,target:document.body,show:function(){this.progress.is()?this.progress.$box.show():(this.progress.build(),this.progress.$box.redactorAnimation("fadeIn"))},hide:function(){this.progress.is()&&this.progress.$box.redactorAnimation("fadeOut",{duration:.35},t.proxy(this.progress.destroy,this))},update:function(t){this.progress.show(),this.progress.$bar.css("width",t+"%")},is:function(){return null!==this.progress.$box},build:function(){this.progress.$bar=t("<span />"),this.progress.$box=t('<div id="redactor-progress" />'),this.progress.$box.append(this.progress.$bar),t(this.progress.target).append(this.progress.$box)},destroy:function(){this.progress.is()&&this.progress.$box.remove(),this.progress.$box=null,this.progress.$bar=null}}},selection:function(){return{get:function(){return window.getSelection?window.getSelection():document.selection&&"Control"!==document.selection.type?document.selection:null},range:function(t){return void 0===t&&(t=this.selection.get()),t.getRangeAt&&t.rangeCount?t.getRangeAt(0):null},is:function(){return!this.selection.isCollapsed()},isRedactor:function(){var e=this.selection.range();if(null!==e){var i=e.startContainer.parentNode;if(t(i).hasClass("redactor-in")||0!==t(i).parents(".redactor-in").length)return!0}return!1},isCollapsed:function(){var t=this.selection.get();return null!==t&&t.isCollapsed},update:function(t,e){null!==e&&(t.removeAllRanges(),t.addRange(e))},current:function(){var t=this.selection.get();return null!==t&&t.anchorNode},parent:function(){var t=this.selection.current();return null!==t&&t.parentNode},block:function(e){for(e=e||this.selection.current();e;){if(this.utils.isBlockTag(e.tagName))return!t(e).hasClass("redactor-in")&&e;e=e.parentNode}return!1},inline:function(e){for(e=e||this.selection.current();e;){if(this.utils.isInlineTag(e.tagName))return!t(e).hasClass("redactor-in")&&e;e=e.parentNode}return!1},element:function(e){for(e||(e=this.selection.current());e;){if(1===e.nodeType)return!t(e).hasClass("redactor-in")&&e;e=e.parentNode}return!1},prev:function(){return null!==this.selection.current()&&this.selection.current().previousSibling},next:function(){return null!==this.selection.current()&&this.selection.current().nextSibling},blocks:function(e){var i=[],r=this.selection.nodes(e);t.each(r,t.proxy(function(t,e){this.utils.isBlock(e)&&i.push(e)},this));var o=this.selection.block();return 0===i.length&&!1===o?[]:0===i.length&&!1!==o?[o]:i},inlines:function(e){var i=[],r=this.selection.nodes(e);t.each(r,t.proxy(function(t,e){this.utils.isInline(e)&&i.push(e)},this));var o=this.selection.inline();return 0===i.length&&!1===o?[]:0===i.length&&!1!==o?[o]:i},nodes:function(e){var i=void 0===e?[]:t.isArray(e)?e:[e],r=this.selection.get(),o=this.selection.range(r),s=[],n=[];if(this.utils.isCollapsed())s=[this.selection.current()];else{var a=o.startContainer,l=o.endContainer;if(a===l)return[a];for(;a&&a!==l;)s.push(a=this.selection.nextNode(a));for(a=o.startContainer;a&&a!==o.commonAncestorContainer;)s.unshift(a),a=a.parentNode}return t.each(s,function(e,r){if(r){var o=1===r.nodeType&&r.tagName.toLowerCase();if(t(r).hasClass("redactor-script-tag")||t(r).hasClass("redactor-selection-marker"))return;if(o&&0!==i.length&&-1===t.inArray(o,i))return;n.push(r)}}),0===n.length?[]:n},nextNode:function(t){if(t.hasChildNodes())return t.firstChild;for(;t&&!t.nextSibling;)t=t.parentNode;return t?t.nextSibling:null},save:function(){this.marker.insert(),this.savedSel=this.core.editor().html()},restore:function(t){var e=this.marker.find(1),i=this.marker.find(2);this.detect.isFirefox()&&this.core.editor().focus(),0!==e.length&&0!==i.length?this.caret.set(e,i):0!==e.length?this.caret.start(e):this.core.editor().focus(),!1!==t&&(this.marker.remove(),this.savedSel=!1)},saveInstant:function(){var t=this.core.editor()[0],e=t.ownerDocument,i=e.defaultView,r=i.getSelection();if(r.getRangeAt&&r.rangeCount){var o=r.getRangeAt(0),s=o.cloneRange();s.selectNodeContents(t),s.setEnd(o.startContainer,o.startOffset);var n=s.toString().length;return this.saved={start:n,end:n+o.toString().length,node:o.startContainer},this.saved}},restoreInstant:function(t){if(void 0!==t||this.saved){this.saved=void 0!==t?t:this.saved;var e=this.core.editor().find(this.saved.node);if(0===e.length||0!==e.text().trim().replace(/\u200B/g,"").length){var i=this.core.editor()[0],r=i.ownerDocument,o=r.defaultView,s=0,n=r.createRange();n.setStart(i,0),n.collapse(!0);for(var a,l=[i],c=!1,d=!1;!d&&(a=l.pop());)if(3==a.nodeType){var h=s+a.length;!c&&this.saved.start>=s&&this.saved.start<=h&&(n.setStart(a,this.saved.start-s),c=!0),c&&this.saved.end>=s&&this.saved.end<=h&&(n.setEnd(a,this.saved.end-s),d=!0),s=h}else for(var u=a.childNodes.length;u--;)l.push(a.childNodes[u]);var p=o.getSelection();p.removeAllRanges(),p.addRange(n)}else try{var n=document.createRange();n.setStart(e[0],0);var p=window.getSelection();p.removeAllRanges(),p.addRange(n)}catch(t){}}},node:function(e){t(e).prepend(this.marker.get(1)),t(e).append(this.marker.get(2)),this.selection.restore()},all:function(){this.core.editor().focus();var t=this.selection.get(),e=this.selection.range(t);e.selectNodeContents(this.core.editor()[0]),this.selection.update(t,e)},remove:function(){this.selection.get().removeAllRanges()},replace:function(t){this.insert.html(t)},text:function(){return this.selection.get().toString()},html:function(){var t="",e=this.selection.get();if(e.rangeCount){for(var i=document.createElement("div"),r=e.rangeCount,o=0;o<r;++o)i.appendChild(e.getRangeAt(o).cloneContents());t=this.clean.onGet(i.innerHTML)}return t},extractEndOfNode:function(t){var e=this.selection.get(),i=this.selection.range(e),r=i.cloneRange();return r.selectNodeContents(t),r.setStart(i.endContainer,i.endOffset),r.extractContents()},removeMarkers:function(){this.marker.remove()},marker:function(t){return this.marker.get(t)},markerHtml:function(t){return this.marker.html(t)}}},shortcuts:function(){return{hotkeysSpecialKeys:{8:"backspace",9:"tab",10:"return",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",59:";",61:"=",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},hotkeysShiftNums:{"`":"~",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(",0:")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"},init:function(e,i){if(!1===this.opts.shortcuts)return!e.ctrlKey&&!e.metaKey||66!==i&&73!==i||e.preventDefault(),!1;t.each(this.opts.shortcuts,t.proxy(function(t,i){this.shortcuts.build(e,t,i)},this))},build:function(e,i,r){for(var o=t.proxy(function(){this.shortcuts.buildHandler(r)},this),s=i.split(","),n=s.length,a=0;a<n;a++)"string"==typeof s[a]&&this.shortcuts.handler(e,t.trim(s[a]),o)},buildHandler:function(t){var e;"-1"!==t.func.search(/\./)?(e=t.func.split("."),void 0!==this[e[0]]&&this[e[0]][e[1]].apply(this,t.params)):this[t.func].apply(this,t.params)},handler:function(e,i,r){i=i.toLowerCase().split(" ");var o=this.shortcuts.hotkeysSpecialKeys[e.keyCode],s=String.fromCharCode(e.which).toLowerCase(),n="",a={};t.each(["alt","ctrl","meta","shift"],function(t,i){e[i+"Key"]&&o!==i&&(n+=i+"+")}),o&&(a[n+o]=!0),s&&(a[n+s]=!0,a[n+this.shortcuts.hotkeysShiftNums[s]]=!0,"shift+"===n&&(a[this.shortcuts.hotkeysShiftNums[s]]=!0));for(var l=i.length,c=0;c<l;c++)if(a[i[c]])return e.preventDefault(),r.apply(this,arguments)}}},storage:function(){return{data:[],add:function(t){t.status=!0,t.url=decodeURI(t.url),this.storage.data[t.url]=t},status:function(t,e){this.storage.data[decodeURI(t)].status=e},observe:function(){var e=this;this.core.editor().find("[data-image]").each(function(i,r){e.storage.add({type:"image",node:r,url:r.src,id:t(r).attr("data-image")})}),this.core.editor().find("[data-file]").each(function(i,r){e.storage.add({type:"file",node:r,url:r.href,id:t(r).attr("data-file")})}),this.core.editor().find("[data-s3]").each(function(i,r){var o="IMG"===r.tagName?r.src:r.href;e.storage.add({type:"s3",node:r,url:o,id:t(r).attr("data-s3")})})},changes:function(){for(var t in this.storage.data){var e=this.storage.data[t],i="IMG"===e.node.tagName?"src":"href";0===this.core.editor().find("[data-"+e.type+"]["+i+'="'+e.url+'"]').length?this.storage.status(e.url,!1):this.storage.status(e.url,!0)}return this.storage.data}}},toolbar:function(){return{build:function(){this.button.hideButtons(),this.button.hideButtonsOnMobile(),this.$toolbarBox=t("<div />").addClass("redactor-toolbar-box"),this.$toolbar=this.toolbar.createContainer(),this.$toolbarBox.append(this.$toolbar),this.toolbar.append(),this.button.$toolbar=this.$toolbar,this.button.setFormatting(),this.button.load(this.$toolbar),this.toolbar.setOverflow(),this.toolbar.setFixed()},createContainer:function(){return t("<ul>").addClass("redactor-toolbar").attr({id:"redactor-toolbar-"+this.uuid,role:"toolbar"})},append:function(){this.opts.toolbarExternal?(this.$toolbar.addClass("redactor-toolbar-external"),t(this.opts.toolbarExternal).html(this.$toolbarBox)):"textarea"===this.opts.type?this.$box.prepend(this.$toolbarBox):this.$element.before(this.$toolbarBox)},setOverflow:function(){this.opts.toolbarOverflow&&this.$toolbar.addClass("redactor-toolbar-overflow")},setFixed:function(){if(this.opts.toolbarFixed&&!this.opts.toolbarExternal){if(this.opts.toolbarFixedTarget!==document){var e=t(this.opts.toolbarFixedTarget);this.toolbar.toolbarOffsetTop=0===e.length?0:this.core.box().offset().top-e.offset().top}var i=0!==this.core.box().closest(".modal-body").length?1e3:0;setTimeout(t.proxy(function(){var e=this;this.toolbar.observeScroll(!1),this.detect.isDesktop()?t(this.opts.toolbarFixedTarget).on("scroll.redactor."+this.uuid,function(){e.core.editor().height()<100||e.placeholder.isEditorEmpty()||e.toolbar.observeScroll()}):t(this.opts.toolbarFixedTarget).on("scroll.redactor."+this.uuid,function(){e.core.editor().height()<100||e.placeholder.isEditorEmpty()||(e.core.toolbar().hide(),clearTimeout(t.data(this,"scrollCheck")),t.data(this,"scrollCheck",setTimeout(function(){e.core.toolbar().show(),e.toolbar.observeScroll()},250)))})},this),i)}},setUnfixed:function(){this.toolbar.observeScrollDisable()},getBoxTop:function(){return this.opts.toolbarFixedTarget===document?this.core.box().offset().top:this.toolbar.toolbarOffsetTop},observeScroll:function(e){var i=0;!1!==e&&(i=this.opts.toolbarFixedTarget===document?20:0);var r=t(this.opts.toolbarFixedTarget).scrollTop(),o=this.toolbar.getBoxTop();r!==o&&(r+this.opts.toolbarFixedTopOffset+i>o?this.toolbar.observeScrollEnable(r,o):this.toolbar.observeScrollDisable())},observeScrollResize:function(){this.$toolbar.css({width:this.core.box().innerWidth(),left:this.core.box().offset().left})},observeScrollEnable:function(e,i){if(void 0!==this.fullscreen&&!1===this.fullscreen.isOpened)return void this.toolbar.observeScrollDisable();var r=i+this.core.box().outerHeight()-32,o=this.core.box().innerWidth(),s=this.detect.isDesktop()?"fixed":"absolute",n=this.detect.isDesktop()?this.opts.toolbarFixedTopOffset:t(this.opts.toolbarFixedTarget).scrollTop()-i+this.opts.toolbarFixedTopOffset,a=this.detect.isDesktop()?this.core.box().offset().left:0;this.opts.toolbarFixedTarget!==document&&(s="absolute",n=this.opts.toolbarFixedTopOffset+t(this.opts.toolbarFixedTarget).scrollTop()-i,a=0),this.$toolbar.addClass("toolbar-fixed-box"),this.$toolbar.css({position:s,width:o,top:n,left:a}),e>r&&t(".redactor-dropdown-"+this.uuid+":visible").hide(),this.toolbar.setDropdownsFixed(),this.$toolbar.css("visibility",e<r?"visible":"hidden"),t(window).on("resize.redactor-toolbar."+this.uuid,t.proxy(this.toolbar.observeScrollResize,this))},observeScrollDisable:function(){this.$toolbar.css({position:"relative",width:"auto",top:0,left:0,visibility:"visible"}),this.toolbar.unsetDropdownsFixed(),this.$toolbar.removeClass("toolbar-fixed-box"),t(window).off("resize.redactor-toolbar."+this.uuid)},setDropdownsFixed:function(){var t=this.opts.toolbarFixedTarget===document&&this.detect.isDesktop()?"fixed":"absolute";this.toolbar.setDropdownPosition(t)},unsetDropdownsFixed:function(){this.toolbar.setDropdownPosition("absolute")},setDropdownPosition:function(e){var i=this;t(".redactor-dropdown-"+this.uuid).each(function(){var r=t(this),o=i.button.get(r.attr("rel")),s="fixed"===e?i.opts.toolbarFixedTopOffset:o.offset().top;r.css({position:e,top:o.innerHeight()+s+"px"})})}}},upload:function(){return{init:function(e,i,r){this.upload.direct=!1,this.upload.callback=r,this.upload.url=i,this.upload.$el=t(e),this.upload.$droparea=t('<div id="redactor-droparea" />'),this.upload.$placeholdler=t('<div id="redactor-droparea-placeholder" />').text(this.lang.get("upload-label")),this.upload.$input=t('<input type="file" name="file" multiple />'),this.upload.$placeholdler.append(this.upload.$input),this.upload.$droparea.append(this.upload.$placeholdler),this.upload.$el.append(this.upload.$droparea),this.upload.$droparea.off("redactor.upload"),this.upload.$input.off("redactor.upload"),this.upload.$droparea.on("dragover.redactor.upload",t.proxy(this.upload.onDrag,this)),this.upload.$droparea.on("dragleave.redactor.upload",t.proxy(this.upload.onDragLeave,this)),this.upload.$input.on("change.redactor.upload",t.proxy(function(t){t=t.originalEvent||t;for(var e=this.upload.$input[0].files.length,i=0;i<e;i++){var r=e-1-i;this.upload.traverseFile(this.upload.$input[0].files[r],t)}},this)),this.upload.$droparea.on("drop.redactor.upload",t.proxy(function(t){t.preventDefault(),this.upload.$droparea.removeClass("drag-hover").addClass("drag-drop"),this.upload.onDrop(t)},this))},directUpload:function(t,e){this.upload.direct=!0,this.upload.traverseFile(t,e)},onDrop:function(t){t=t.originalEvent||t;var e=t.dataTransfer.files;if(this.opts.multipleImageUpload)for(var i=e.length,r=0;r<i;r++)this.upload.traverseFile(e[r],t);else this.upload.traverseFile(e[0],t)},traverseFile:function(t,e){if(this.opts.s3)return this.upload.setConfig(t),void this.uploads3.send(t,e);var i=window.FormData?new FormData:null;if(window.FormData){this.upload.setConfig(t);var r="image"===this.upload.type?this.opts.imageUploadParam:this.opts.fileUploadParam;i.append(r,t)}!1!==this.core.callback("uploadStart",e,i)&&(this.progress.show(),this.upload.send(i,e))},setConfig:function(t){this.upload.getType(t),this.upload.direct&&(this.upload.url="image"===this.upload.type?this.opts.imageUpload:this.opts.fileUpload,this.upload.callback="image"===this.upload.type?this.image.insert:this.file.insert)},getType:function(t){this.upload.type=-1===this.opts.imageTypes.indexOf(t.type)?"file":"image",null===this.opts.imageUpload&&null!==this.opts.fileUpload&&(this.upload.type="file")},getHiddenFields:function(e,i){return!1===e||"object"!=typeof e?i:(t.each(e,t.proxy(function(e,r){null!==r&&0===r.toString().indexOf("#")&&(r=t(r).val()),i.append(e,r)},this)),i)},send:function(e,i){"image"===this.upload.type?(e=this.utils.appendFields(this.opts.imageUploadFields,e),e=this.utils.appendForms(this.opts.imageUploadForms,e),e=this.upload.getHiddenFields(this.upload.imageFields,e)):(e=this.utils.appendFields(this.opts.fileUploadFields,e),e=this.utils.appendForms(this.opts.fileUploadForms,e),e=this.upload.getHiddenFields(this.upload.fileFields,e));var r=new XMLHttpRequest;r.open("POST",this.upload.url),r.setRequestHeader("X-Requested-With","XMLHttpRequest"),r.onreadystatechange=t.proxy(function(){if(4===r.readyState){var t=r.responseText;t=t.replace(/^\[/,""),t=t.replace(/\]$/,"");var e;try{e="string"==typeof t?JSON.parse(t):t}catch(t){e={error:!0}}this.progress.hide(),this.upload.direct||this.upload.$droparea.removeClass("drag-drop"),this.upload.callback(e,this.upload.direct,i)}},this),!1!==this.core.callback("uploadBeforeSend",r)&&r.send(e)},onDrag:function(t){t.preventDefault(),this.upload.$droparea.addClass("drag-hover")},onDragLeave:function(t){t.preventDefault(),this.upload.$droparea.removeClass("drag-hover")},clearImageFields:function(){this.upload.imageFields={}},addImageFields:function(t,e){this.upload.imageFields[t]=e},removeImageFields:function(t){delete this.upload.imageFields[t]},clearFileFields:function(){this.upload.fileFields={}},addFileFields:function(t,e){this.upload.fileFields[t]=e},removeFileFields:function(t){delete this.upload.fileFields[t]}}},uploads3:function(){return{send:function(e,i){this.uploads3.executeOnSignedUrl(e,t.proxy(function(t){this.uploads3.sendToS3(e,t,i)},this))},executeOnSignedUrl:function(t,e){var i=new XMLHttpRequest,r=-1===this.opts.s3.search(/\?/)?"?":"&";i.open("GET",this.opts.s3+r+"name="+t.name+"&type="+t.type,!0),i.overrideMimeType&&i.overrideMimeType("text/plain; charset=x-user-defined");var o=this;i.onreadystatechange=function(t){4===this.readyState&&200===this.status&&(o.progress.show(),e(decodeURIComponent(this.responseText)))},i.send()},createCORSRequest:function(t,e){var i=new XMLHttpRequest;return"withCredentials"in i?i.open(t,e,!0):"undefined"!=typeof XDomainRequest?(i=new XDomainRequest,i.open(t,e)):i=null,i},sendToS3:function(e,i,r){var o=this.uploads3.createCORSRequest("PUT",i);o&&(o.onload=t.proxy(function(){var t;if(this.progress.hide(),200!==o.status)return t={error:!0},void this.upload.callback(t,this.upload.direct,o);var e=i.split("?");if(!e[0])return!1;if(this.upload.direct||this.upload.$droparea.removeClass("drag-drop"),t={url:e[0],id:e[0],s3:!0},"file"===this.upload.type){var s=e[0].split("/");t.name=s[s.length-1]}this.upload.callback(t,this.upload.direct,r)},this),o.onerror=function(){},o.upload.onprogress=function(t){},o.setRequestHeader("Content-Type",e.type),o.setRequestHeader("x-amz-acl","public-read"),o.send(e))}}},utils:function(){return{isEmpty:function(e){return e=void 0===e?this.core.editor().html():e,e=e.replace(/[\u200B-\u200D\uFEFF]/g,""),e=e.replace(/ /gi,""),e=e.replace(/<\/?br\s?\/?>/g,""),e=e.replace(/\s/g,""),e=e.replace(/^<p>[^\W\w\D\d]*?<\/p>$/i,""),e=e.replace(/<iframe(.*?[^>])>$/i,"iframe"),e=e.replace(/<source(.*?[^>])>$/i,"source"),e=e.replace(/<[^\/>][^>]*><\/[^>]+>/gi,""),e=e.replace(/<[^\/>][^>]*><\/[^>]+>/gi,""),""===(e=t.trim(e))},isElement:function(t){try{return t instanceof HTMLElement}catch(e){return"object"==typeof t&&1===t.nodeType&&"object"==typeof t.style&&"object"==typeof t.ownerDocument}},strpos:function(t,e,i){var r=t.indexOf(e,i);return r>=0&&r},dataURItoBlob:function(t){var e;e=t.split(",")[0].indexOf("base64")>=0?atob(t.split(",")[1]):unescape(t.split(",")[1]);for(var i=t.split(",")[0].split(":")[1].split(";")[0],r=new Uint8Array(e.length),o=0;o<e.length;o++)r[o]=e.charCodeAt(o);return new Blob([r],{type:i})},getOuterHtml:function(e){return t("<div>").append(t(e).eq(0).clone()).html()},cloneAttributes:function(e,i){e=e[0]||e,i=t(i);for(var r=e.attributes,o=r.length;o--;){var s=r[o];i.attr(s.name,s.value)}return i},breakBlockTag:function(){var e=this.selection.block();if(!e)return!1;var i=this.utils.isEmpty(e.innerHTML),r=e.tagName.toLowerCase();if("pre"===r||"li"===r||"td"===r||"th"===r)return!1;if(!i&&this.utils.isStartOfElement(e))return{$block:t(e),$next:t(e).next(),type:"start"};if(!i&&this.utils.isEndOfElement(e))return{$block:t(e),$next:t(e).next(),type:"end"};var o=this.selection.extractEndOfNode(e),s=t("<"+r+" />").append(o);return s=this.utils.cloneAttributes(e,s),t(e).after(s),{$block:t(e),$next:s,type:"break"}},inBlocks:function(e){e=t.isArray(e)?e:[e];for(var i=this.selection.blocks(),r=i.length,o=!1,s=0;s<r;s++)if(!1!==i[s]){var n=i[s].tagName.toLowerCase();-1!==t.inArray(n,e)&&(o=!0)}return o},inInlines:function(e){e=t.isArray(e)?e:[e];for(var i=this.selection.inlines(),r=i.length,o=!1,s=0;s<r;s++){var n=i[s].tagName.toLowerCase();-1!==t.inArray(n,e)&&(o=!0)}return o},isTag:function(e,i){var r=t(e).closest(i,this.core.editor()[0]);return 1===r.length&&r[0]},isBlock:function(t){return null!==t&&((t=t[0]||t)&&this.utils.isBlockTag(t.tagName))},isBlockTag:function(t){return void 0!==t&&this.reIsBlock.test(t)},isInline:function(t){return(t=t[0]||t)&&this.utils.isInlineTag(t.tagName)},isInlineTag:function(t){return void 0!==t&&this.reIsInline.test(t)},isRedactorParent:function(e){return!!e&&(0!==t(e).parents(".redactor-in").length&&!t(e).hasClass("redactor-in")&&e)},isCurrentOrParentHeader:function(){return this.utils.isCurrentOrParent(["H1","H2","H3","H4","H5","H6"])},isCurrentOrParent:function(e){var i=this.selection.parent(),r=this.selection.current();if(t.isArray(e)){var o=0;return t.each(e,t.proxy(function(t,e){this.utils.isCurrentOrParentOne(r,i,e)&&o++},this)),0!==o}return this.utils.isCurrentOrParentOne(r,i,e)},isCurrentOrParentOne:function(t,e,i){return i=i.toUpperCase(),e&&e.tagName===i?e:!(!t||t.tagName!==i)&&t},isEditorRelative:function(){var e=this.core.editor().css("position"),i=["absolute","fixed","relative"];return-1!==t.inArray(i,e)},setEditorRelative:function(){this.core.editor().addClass("redactor-relative")},getScrollTarget:function(){var e=t(this.opts.scrollTarget);return 0!==e.length?e:t(document)},freezeScroll:function(){this.freezeScrollTop=this.utils.getScrollTarget().scrollTop(),this.utils.getScrollTarget().scrollTop(this.freezeScrollTop)},unfreezeScroll:function(){
-void 0!==this.freezeScrollTop&&this.utils.getScrollTarget().scrollTop(this.freezeScrollTop)},saveScroll:function(){this.tmpScrollTop=this.utils.getScrollTarget().scrollTop()},restoreScroll:function(){void 0!==this.tmpScrollTop&&this.utils.getScrollTarget().scrollTop(this.tmpScrollTop)},isStartOfElement:function(t){return!(void 0===t&&!(t=this.selection.block()))&&0===this.offset.get(t)},isEndOfElement:function(e){if(void 0===e&&!(e=this.selection.block()))return!1;var i=t.trim(t(e).text()).replace(/[\t\n\r\n]/g,"").replace(/\u200B/g,"");return this.offset.get(e)===i.length},removeEmptyAttr:function(e,i){var r=t(e);return void 0===r.attr(i)||""===r.attr(i)&&(r.removeAttr(i),!0)},replaceToTag:function(e,i){var r;return t(e).replaceWith(function(){r=t("<"+i+" />").append(t(this).contents());for(var e=0;e<this.attributes.length;e++)r.attr(this.attributes[e].name,this.attributes[e].value);return r}),r},isSelectAll:function(){return this.selectAll},enableSelectAll:function(){this.selectAll=!0},disableSelectAll:function(){this.selectAll=!1},disableBodyScroll:function(){var e=t("html"),i=window.innerWidth;if(!i){var r=document.documentElement.getBoundingClientRect();i=r.right-Math.abs(r.left)}var o=document.body.clientWidth<i,s=this.utils.measureScrollbar();e.css("overflow","hidden"),o&&e.css("padding-right",s)},measureScrollbar:function(){var e=t("body"),i=document.createElement("div");i.className="redactor-scrollbar-measure",e.append(i);var r=i.offsetWidth-i.clientWidth;return e[0].removeChild(i),r},enableBodyScroll:function(){t("html").css({overflow:"","padding-right":""}),t("body").remove("redactor-scrollbar-measure")},appendFields:function(e,i){if(!e)return i;if("object"==typeof e)return t.each(e,function(e,r){null!==r&&0===r.toString().indexOf("#")&&(r=t(r).val()),i.append(e,r)}),i;var r=t(e);if(0===r.length)return i;return r.each(function(){i.append(t(this).attr("name"),t(this).val())}),i},appendForms:function(e,i){if(!e)return i;var r=t(e);if(0===r.length)return i;var o=r.serializeArray();return t.each(o,function(t,e){i.append(e.name,e.value)}),i},isRgb:function(t){return 0===t.search(/^rgb/i)},rgb2hex:function(t){return t=t.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i),t&&4===t.length?"#"+("0"+parseInt(t[1],10).toString(16)).slice(-2)+("0"+parseInt(t[2],10).toString(16)).slice(-2)+("0"+parseInt(t[3],10).toString(16)).slice(-2):""},isCollapsed:function(){return this.selection.isCollapsed()},isMobile:function(){return this.detect.isMobile()},isDesktop:function(){return this.detect.isDesktop()},isPad:function(){return this.detect.isIpad()}}},browser:function(){return{webkit:function(){return this.detect.isWebkit()},ff:function(){return this.detect.isFirefox()},ie:function(){return this.detect.isIe()}}}},t(window).on("load.tools.redactor",function(){t('[data-tools="redactor"]').redactor()}),e.prototype.init.prototype=e.prototype}(jQuery),function(t){function e(e,i,r,o){var s={duration:.5,iterate:1,delay:0,prefix:"redactor-",timing:"linear"};this.animation=i,this.slide="slideDown"===this.animation||"slideUp"===this.animation,this.$element=t(e),this.prefixes=["","-moz-","-o-animation-","-webkit-"],this.queue=[],"function"==typeof r?(o=r,this.opts=s):this.opts=t.extend(s,r),this.slide&&this.$element.height(this.$element.height()),this.init(o)}t.fn.redactorAnimation=function(t,i,r){return this.each(function(){new e(this,t,i,r)})},e.prototype={init:function(t){this.queue.push(this.animation),this.clean(),"show"===this.animation?(this.opts.timing="linear",this.$element.removeClass("hide").show(),"function"==typeof t&&t(this)):"hide"===this.animation?(this.opts.timing="linear",this.$element.hide(),"function"==typeof t&&t(this)):this.animate(t)},animate:function(e){this.$element.addClass("redactor-animated").css("display","").removeClass("hide"),this.$element.addClass(this.opts.prefix+this.queue[0]),this.set(this.opts.duration+"s",this.opts.delay+"s",this.opts.iterate,this.opts.timing);var i=this.queue.length>1?null:e;this.complete("AnimationEnd",t.proxy(function(){this.$element.hasClass(this.opts.prefix+this.queue[0])&&(this.clean(),this.queue.shift(),this.queue.length&&this.animate(e))},this),i)},set:function(t,e,i,r){for(var o=this.prefixes.length;o--;)this.$element.css(this.prefixes[o]+"animation-duration",t),this.$element.css(this.prefixes[o]+"animation-delay",e),this.$element.css(this.prefixes[o]+"animation-iteration-count",i),this.$element.css(this.prefixes[o]+"animation-timing-function",r)},clean:function(){this.$element.removeClass("redactor-animated"),this.$element.removeClass(this.opts.prefix+this.queue[0]),this.set("","","","")},complete:function(e,i,r){this.$element.one(e.toLowerCase()+" webkit"+e+" o"+e+" MS"+e,t.proxy(function(){"function"==typeof i&&i(),"function"==typeof r&&r(this);var e=["fadeOut","slideUp","zoomOut","slideOutUp","slideOutRight","slideOutLeft"];-1!==t.inArray(this.animation,e)&&this.$element.css("display","none"),this.slide&&this.$element.css("height","")},this))}}}(jQuery); })(this);
+(function (window, undefined) { !function(t){"use strict";function e(t,i){return new e.prototype.init(t,i)}Function.prototype.bind||(Function.prototype.bind=function(t){var e=this;return function(){return e.apply(t)}});var i=0;t.fn.redactor=function(i){var r=[],n=Array.prototype.slice.call(arguments,1);return"string"==typeof i?this.each(function(){var e,s=t.data(this,"redactor");if("-1"!==i.search(/\./)?(e=i.split("."),void 0!==s[e[0]]&&(e=s[e[0]][e[1]])):e=s[i],void 0!==s&&t.isFunction(e)){var o=e.apply(s,n);void 0!==o&&o!==s&&r.push(o)}else t.error('No such method "'+i+'" for Redactor')}):this.each(function(){t.data(this,"redactor",{}),t.data(this,"redactor",e(this,i))}),0===r.length?this:1===r.length?r[0]:r},t.Redactor=e,t.Redactor.VERSION="2.99",t.Redactor.modules=["air","autosave","block","buffer","build","button","caret","clean","code","core","detect","dropdown","events","file","focus","image","indent","inline","insert","keydown","keyup","lang","line","link","linkify","list","marker","modal","observe","offset","paragraphize","paste","placeholder","progress","selection","shortcuts","storage","toolbar","upload","uploads3","utils","browser"],t.Redactor.settings={},t.Redactor.opts={animation:!1,lang:"en",direction:"ltr",spellcheck:!0,overrideStyles:!0,stylesClass:!1,scrollTarget:document,focus:!1,focusEnd:!1,clickToEdit:!1,structure:!1,tabindex:!1,minHeight:!1,maxHeight:!1,maxWidth:!1,plugins:!1,callbacks:{},placeholder:!1,linkify:!0,enterKey:!0,pastePlainText:!1,pasteImages:!0,pasteLinks:!0,pasteBlockTags:["pre","h1","h2","h3","h4","h5","h6","table","tbody","thead","tfoot","th","tr","td","ul","ol","li","blockquote","p","figure","figcaption"],pasteInlineTags:["br","strong","ins","code","del","span","samp","kbd","sup","sub","mark","var","cite","small","b","u","em","i"],preClass:!1,preSpaces:4,tabAsSpaces:!1,tabKey:!0,autosave:!1,autosaveName:!1,autosaveFields:!1,imageUpload:null,imageUploadParam:"file",imageUploadFields:!1,imageUploadForms:!1,imageTag:"figure",imageEditable:!0,imageCaption:!0,imagePosition:!1,imageResizable:!1,imageFloatMargin:"10px",dragImageUpload:!0,multipleImageUpload:!0,clipboardImageUpload:!0,fileUpload:null,fileUploadParam:"file",fileUploadFields:!1,fileUploadForms:!1,dragFileUpload:!0,s3:!1,linkNewTab:!1,linkTooltip:!0,linkNofollow:!1,linkSize:30,linkValidation:!0,pasteLinkTarget:!1,videoContainerClass:"video-container",toolbar:!0,toolbarFixed:!0,toolbarFixedTarget:document,toolbarFixedTopOffset:0,toolbarExternal:!1,toolbarOverflow:!1,air:!1,airWidth:!1,formatting:["p","blockquote","pre","h1","h2","h3","h4","h5","h6"],formattingAdd:!1,buttons:["format","bold","italic","deleted","lists","image","file","link","horizontalrule"],buttonsTextLabeled:!1,buttonsHide:[],buttonsHideOnMobile:[],script:!0,removeNewlines:!1,removeComments:!0,replaceTags:{b:"strong",i:"em",strike:"del"},keepStyleAttr:[],keepInlineOnEnter:!1,shortcuts:{"ctrl+shift+m, meta+shift+m":{func:"inline.removeFormat"},"ctrl+b, meta+b":{func:"inline.format",params:["bold"]},"ctrl+i, meta+i":{func:"inline.format",params:["italic"]},"ctrl+h, meta+h":{func:"inline.format",params:["superscript"]},"ctrl+l, meta+l":{func:"inline.format",params:["subscript"]},"ctrl+k, meta+k":{func:"link.show"},"ctrl+shift+7":{func:"list.toggle",params:["orderedlist"]},"ctrl+shift+8":{func:"list.toggle",params:["unorderedlist"]}},shortcutsAdd:!1,activeButtons:["deleted","italic","bold"],activeButtonsStates:{b:"bold",strong:"bold",i:"italic",em:"italic",del:"deleted",strike:"deleted"},langs:{en:{format:"Format",image:"Image",file:"File",link:"Link",bold:"Bold",italic:"Italic",deleted:"Strikethrough",underline:"Underline","bold-abbr":"B","italic-abbr":"I","deleted-abbr":"S","underline-abbr":"U",lists:"Lists","link-insert":"Insert link","link-edit":"Edit link","link-in-new-tab":"Open link in new tab",unlink:"Unlink",cancel:"Cancel",close:"Close",insert:"Insert",save:"Save",delete:"Delete",text:"Text",edit:"Edit",title:"Title",paragraph:"Normal text",quote:"Quote",code:"Code",heading1:"Heading 1",heading2:"Heading 2",heading3:"Heading 3",heading4:"Heading 4",heading5:"Heading 5",heading6:"Heading 6",filename:"Name",optional:"optional",unorderedlist:"Unordered List",orderedlist:"Ordered List",outdent:"Outdent",indent:"Indent",horizontalrule:"Line","upload-label":"Drop file here or ",caption:"Caption",bulletslist:"Bullets",numberslist:"Numbers","image-position":"Position",none:"None",left:"Left",right:"Right",center:"Center","accessibility-help-label":"Rich text editor"}},type:"textarea",inline:!1,inlineTags:["a","span","strong","strike","b","u","em","i","code","del","ins","samp","kbd","sup","sub","mark","var","cite","small"],blockTags:["pre","ul","ol","li","p","h1","h2","h3","h4","h5","h6","dl","dt","dd","div","td","blockquote","output","figcaption","figure","address","section","header","footer","aside","article","iframe"],paragraphize:!0,paragraphizeBlocks:["table","div","pre","form","ul","ol","h1","h2","h3","h4","h5","h6","dl","blockquote","figcaption","address","section","header","footer","aside","article","object","style","script","iframe","select","input","textarea","button","option","map","area","math","hr","fieldset","legend","hgroup","nav","figure","details","menu","summary","p"],emptyHtml:"<p>​</p>",invisibleSpace:"​",emptyHtmlRendered:t("").html("").html(),imageTypes:["image/png","image/jpeg","image/gif"],userAgent:navigator.userAgent.toLowerCase(),observe:{dropdowns:[]},regexps:{linkyoutube:/https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/gi,linkvimeo:/https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/,linkimage:/((https?|www)[^\s]+\.)(jpe?g|png|gif)(\?[^\s-]+)?/gi,url:/(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/gi}},e.fn=t.Redactor.prototype={keyCode:{BACKSPACE:8,DELETE:46,UP:38,DOWN:40,ENTER:13,SPACE:32,ESC:27,TAB:9,CTRL:17,META:91,SHIFT:16,ALT:18,RIGHT:39,LEFT:37,LEFT_WIN:91},init:function(e,r){if(this.$element=t(e),this.uuid=i++,this.sBuffer=[],this.sRebuffer=[],this.loadOptions(r),this.loadModules(),this.opts.clickToEdit&&!this.$element.hasClass("redactor-click-to-edit"))return this.loadToEdit(r);this.$element.hasClass("redactor-click-to-edit")&&this.$element.removeClass("redactor-click-to-edit"),this.reIsBlock=new RegExp("^("+this.opts.blockTags.join("|").toUpperCase()+")$","i"),this.reIsInline=new RegExp("^("+this.opts.inlineTags.join("|").toUpperCase()+")$","i"),this.opts.dragImageUpload=null!==this.opts.imageUpload&&this.opts.dragImageUpload,this.opts.dragFileUpload=null!==this.opts.fileUpload&&this.opts.dragFileUpload,this.formatting={},this.lang.load(),t.extend(this.opts.shortcuts,this.opts.shortcutsAdd),this.$editor=this.$element,this.detectType(),this.core.callback("start"),this.core.callback("startToEdit"),this.start=!0,this.build.start()},detectType:function(){this.build.isInline()||this.opts.inline?this.opts.type="inline":this.build.isTag("DIV")?this.opts.type="div":this.build.isTag("PRE")&&(this.opts.type="pre")},loadToEdit:function(e){this.$element.on("click.redactor-click-to-edit",t.proxy(function(){this.initToEdit(e)},this)),this.$element.addClass("redactor-click-to-edit")},initToEdit:function(e){t.extend(e.callbacks,{startToEdit:function(){this.insert.node(this.marker.get(),!1)},initToEdit:function(){this.selection.restore(),this.clickToCancelStorage=this.code.get(),t(this.opts.clickToCancel).off(".redactor-click-to-edit"),t(this.opts.clickToCancel).show().on("click.redactor-click-to-edit",t.proxy(function(i){i.preventDefault(),this.core.destroy(),this.events.syncFire=!1,this.$element.html(this.clickToCancelStorage),this.core.callback("cancel",this.clickToCancelStorage),this.events.syncFire=!0,this.clickToCancelStorage="",t(this.opts.clickToCancel).hide(),t(this.opts.clickToSave).hide(),this.$element.on("click.redactor-click-to-edit",t.proxy(function(){this.initToEdit(e)},this)),this.$element.addClass("redactor-click-to-edit")},this)),t(this.opts.clickToSave).off(".redactor-click-to-edit"),t(this.opts.clickToSave).show().on("click.redactor-click-to-edit",t.proxy(function(i){i.preventDefault(),this.core.destroy(),this.core.callback("save",this.code.get()),t(this.opts.clickToCancel).hide(),t(this.opts.clickToSave).hide(),this.$element.on("click.redactor-click-to-edit",t.proxy(function(){this.initToEdit(e)},this)),this.$element.addClass("redactor-click-to-edit")},this))}}),this.$element.redactor(e),this.$element.off(".redactor-click-to-edit")},loadOptions:function(e){var i={};void 0!==t.Redactor.settings.namespace?this.$element.hasClass(t.Redactor.settings.namespace)&&(i=t.Redactor.settings):i=t.Redactor.settings,this.opts=t.extend({},t.Redactor.opts,this.$element.data(),e),this.opts=t.extend({},this.opts,i)},getModuleMethods:function(t){return Object.getOwnPropertyNames(t).filter(function(e){return"function"==typeof t[e]})},loadModules:function(){for(var e=t.Redactor.modules.length,i=0;i<e;i++)this.bindModuleMethods(t.Redactor.modules[i])},bindModuleMethods:function(t){if(void 0!==this[t]){this[t]=this[t]();for(var e=this.getModuleMethods(this[t]),i=e.length,r=0;r<i;r++)this[t][e[r]]=this[t][e[r]].bind(this)}},air:function(){return{enabled:!1,collapsed:function(){},collapsedEnd:function(){},build:function(){},append:function(){},createContainer:function(){},show:function(){},bindHide:function(){},hide:function(){}}},autosave:function(){return{enabled:!1,html:!1,init:function(){},is:function(){},send:function(){},getHiddenFields:function(){},success:function(){},disable:function(){}}},block:function(){return{format:function(e,i,r,n){if(e="quote"===e?"blockquote":e,this.block.tags=["p","blockquote","pre","h1","h2","h3","h4","h5","h6","div","figure"],-1!==t.inArray(e,this.block.tags))return"p"===e&&void 0===i&&(i="class"),this.buffer.set(),this.utils.isCollapsed()?this.block.formatCollapsed(e,i,r,n):this.block.formatUncollapsed(e,i,r,n)},formatCollapsed:function(e,i,r,n){this.selection.save();var s=this.selection.block(),o=s.tagName.toLowerCase();if(-1===t.inArray(o,this.block.tags))return void this.selection.restore();var a=!1;o===e&&void 0===i&&(e="p",a=!0),a&&(this.block.removeAllClass(),this.block.removeAllAttr());var l;if("blockquote"===o&&this.utils.isEndOfElement(s)){this.marker.remove(),l=document.createElement("p"),l.innerHTML=this.opts.invisibleSpace,t(s).after(l),this.caret.start(l);var c=t(s).children().last();0!==c.length&&"BR"===c[0].tagName&&c.remove()}else l=this.utils.replaceToTag(s,e);if("object"==typeof i){n=r;for(var h in i)l=this.block.setAttr(l,h,i[h],n)}else l=this.block.setAttr(l,i,r,n);return"pre"===e&&1===l.length&&t(l).html(t.trim(t(l).html())),this.selection.restore(),this.block.removeInlineTags(l),l},formatUncollapsed:function(e,i,r,n){this.selection.save();var s=[],o=this.selection.blocks();o[0]&&(t(o[0]).hasClass("redactor-in")||t(o[0]).hasClass("redactor-box"))&&(o=this.core.editor().find(this.opts.blockTags.join(", ")));for(var a=o.length,l=0;l<a;l++){var c=o[l].tagName.toLowerCase();if(-1!==t.inArray(c,this.block.tags)&&"figure"!==c){var h=this.utils.replaceToTag(o[l],e);if("object"==typeof i){n=r;for(var d in i)h=this.block.setAttr(h,d,i[d],n)}else h=this.block.setAttr(h,i,r,n);s.push(h),this.block.removeInlineTags(h)}}if(this.selection.restore(),"pre"===e&&0!==s.length){var u=s[0];t.each(s,function(e,i){0!==e&&(t(u).append("\n"+t.trim(i.html())),t(i).remove())}),s=[],s.push(u)}return s},removeInlineTags:function(e){e=e[0]||e;var i=this.opts.inlineTags,r=["PRE","H1","H2","H3","H4","H5","H6"];if(-1!==t.inArray(e.tagName,r)){if("PRE"!==e.tagName){var n=i.indexOf("a");i.splice(n,1)}t(e).find(i.join(",")).not(".redactor-selection-marker").contents().unwrap()}},setAttr:function(t,e,i,r){if(void 0===e)return t;var n=void 0===r?"replace":r;return t="class"===e?this.block[n+"Class"](i,t):"remove"===n?this.block[n+"Attr"](e,t):"removeAll"===n?this.block[n+"Attr"](e,t):this.block[n+"Attr"](e,i,t)},getBlocks:function(e){if(e=void 0===e?this.selection.blocks():e,t(e).hasClass("redactor-box")){var i=[],r=this.core.editor().children();return t.each(r,t.proxy(function(t,e){this.utils.isBlock(e)&&i.push(e)},this)),i}return e},replaceClass:function(e,i){return t(this.block.getBlocks(i)).removeAttr("class").addClass(e)[0]},toggleClass:function(e,i){return t(this.block.getBlocks(i)).toggleClass(e)[0]},addClass:function(e,i){return t(this.block.getBlocks(i)).addClass(e)[0]},removeClass:function(e,i){return t(this.block.getBlocks(i)).removeClass(e)[0]},removeAllClass:function(e){return t(this.block.getBlocks(e)).removeAttr("class")[0]},replaceAttr:function(e,i,r){return r=this.block.removeAttr(e,r),t(r).attr(e,i)[0]},toggleAttr:function(e,i,r){r=this.block.getBlocks(r);var n=this,s=[];return t.each(r,function(r,o){t(o).attr(e)?s.push(n.block.removeAttr(e,o)):s.push(n.block.addAttr(e,i,o))}),s},addAttr:function(e,i,r){return t(this.block.getBlocks(r)).attr(e,i)[0]},removeAttr:function(e,i){return t(this.block.getBlocks(i)).removeAttr(e)[0]},removeAllAttr:function(e){e=this.block.getBlocks(e);var i=[];return t.each(e,function(t,e){if(void 0!==e.attributes)for(;e.attributes.length;)e.removeAttribute(e.attributes[0].name);i.push(e)}),i}}},buffer:function(){return{set:function(t){void 0===t&&this.buffer.clear(),void 0===t||"undo"===t?this.buffer.setUndo():this.buffer.setRedo()},setUndo:function(){var t=this.selection.saveInstant(),e=this.sBuffer[this.sBuffer.length-1],i=this.core.editor().html();(void 0===e||e[0]!==i)&&this.sBuffer.push([i,t])},setRedo:function(){var t=this.selection.saveInstant();this.sRebuffer.push([this.core.editor().html(),t])},add:function(){this.sBuffer.push([this.core.editor().html(),0])},undo:function(){if(0!==this.sBuffer.length){var t=this.sBuffer.pop();this.buffer.set("redo"),this.core.editor().html(t[0]),this.selection.restoreInstant(t[1]),this.selection.restore(),this.observe.load()}},redo:function(){if(0!==this.sRebuffer.length){var t=this.sRebuffer.pop();this.buffer.set("undo"),this.core.editor().html(t[0]),this.selection.restoreInstant(t[1]),this.selection.restore(),this.observe.load()}},clear:function(){this.sRebuffer=[]}}},build:function(){return{start:function(){if("textarea"!==this.opts.type)throw new Error("Only `<textarea>` types are allowed.");this.build.startTextarea(),this.build.setIn(),this.build.setId(),this.build.enableEditor(),this.build.setOptions(),this.build.callEditor()},createContainerBox:function(){this.$box=t('<div class="redactor-box" role="application" />')},setIn:function(){this.core.editor().addClass("redactor-in")},setId:function(){var t="textarea"===this.opts.type?"redactor-uuid-"+this.uuid:this.$element.attr("id");this.core.editor().attr("id",void 0===t?"redactor-uuid-"+this.uuid:t)},getName:function(){var t=this.$element.attr("name");return void 0===t?"content-"+this.uuid:t},buildTextarea:function(){},loadFromTextarea:function(){this.$editor=t("<div />"),this.$textarea=this.$element,this.$element.attr("name",this.build.getName()),this.$box.insertAfter(this.$element).append(this.$editor).append(this.$element),this.build.setStartAttrs(),this.$editor.addClass("redactor-layer"),this.opts.overrideStyles&&this.$editor.addClass("redactor-styles"),this.$element.hide(),this.$box.prepend('<span class="redactor-voice-label" id="redactor-voice-'+this.uuid+'" aria-hidden="false">'+this.lang.get("accessibility-help-label")+"</span>")},setStartAttrs:function(){this.$editor.attr({"aria-labelledby":"redactor-voice-"+this.uuid,role:"presentation"})},startTextarea:function(){this.build.createContainerBox(),this.build.loadFromTextarea(),this.code.start(this.core.textarea().val()),this.core.textarea().val(this.clean.onSync(this.$editor.html()))},isTag:function(t){return this.$element[0].tagName===t},isInline:function(){return!this.build.isTag("TEXTAREA")&&!this.build.isTag("DIV")&&!this.build.isTag("PRE")},enableEditor:function(){this.core.editor().attr({contenteditable:!0})},setOptions:function(){"inline"===this.opts.type&&(this.opts.enterKey=!1),"inline"!==this.opts.type&&"pre"!==this.opts.type||(this.opts.toolbarMobile=!1,this.opts.toolbar=!1),this.core.editor().attr("spellcheck",this.opts.spellcheck),this.opts.structure&&this.core.editor().addClass("redactor-structure"),this.opts.stylesClass&&this.core.editor().addClass(this.opts.stylesClass),"textarea"===this.opts.type&&(this.core.box().attr("dir",this.opts.direction),this.core.editor().attr("dir",this.opts.direction),this.opts.tabindex&&this.core.editor().attr("tabindex",this.opts.tabindex),this.opts.minHeight?this.core.editor().css("min-height",this.opts.minHeight):this.core.editor().css("min-height","40px"),this.opts.maxHeight&&this.core.editor().css("max-height",this.opts.maxHeight),this.opts.maxWidth&&this.core.editor().css({"max-width":this.opts.maxWidth,margin:"auto"}))},callEditor:function(){this.build.disableBrowsersEditing(),this.events.init(),this.build.setHelpers(),this.toolbarsButtons=this.button.init(),this.toolbar.build(),this.core.editor().on("mouseup.redactor-observe."+this.uuid+" keyup.redactor-observe."+this.uuid+" focus.redactor-observe."+this.uuid+" touchstart.redactor-observe."+this.uuid,t.proxy(this.observe.toolbar,this)),this.core.element().on("blur.callback.redactor",t.proxy(function(){this.button.setInactiveAll()},this)),this.modal.templates(),this.build.plugins(),this.code.html=this.code.cleaned(this.core.editor().html()),this.core.callback("init"),this.core.callback("initToEdit"),this.start=!1},setHelpers:function(){this.opts.focus?setTimeout(this.focus.start,100):this.opts.focusEnd&&setTimeout(this.focus.end,100)},disableBrowsersEditing:function(){try{document.execCommand("enableObjectResizing",!1,!1),document.execCommand("enableInlineTableEditing",!1,!1),document.execCommand("AutoUrlDetect",!1,!1)}catch(t){}},plugins:function(){this.opts.plugins&&t.each(this.opts.plugins,t.proxy(function(i,r){var n="undefined"!=typeof RedactorPlugins&&void 0!==RedactorPlugins[r]?RedactorPlugins:e.fn;if(t.isFunction(n[r])){this[r]=n[r]();for(var s=this.getModuleMethods(this[r]),o=s.length,a=0;a<o;a++)this[r][s[a]]=this[r][s[a]].bind(this);if(void 0!==this[r].langs){var l={};void 0!==this[r].langs[this.opts.lang]?l=this[r].langs[this.opts.lang]:void 0===this[r].langs[this.opts.lang]&&void 0!==this[r].langs.en&&(l=this[r].langs.en);var c=this;t.each(l,function(t,e){void 0===c.opts.curLang[t]&&(c.opts.curLang[t]=e)})}"function"==typeof this[r].init&&this[r].init()}},this))}}},button:function(){return{toolbar:function(){return void 0!==this.button.$toolbar&&this.button.$toolbar?this.button.$toolbar:this.$toolbar},init:function(){return{format:{title:this.lang.get("format"),icon:!0,dropdown:{p:{title:this.lang.get("paragraph"),func:"block.format"},blockquote:{title:this.lang.get("quote"),func:"block.format"},pre:{title:this.lang.get("code"),func:"block.format"},h1:{title:this.lang.get("heading1"),func:"block.format"},h2:{title:this.lang.get("heading2"),func:"block.format"},h3:{title:this.lang.get("heading3"),func:"block.format"},h4:{title:this.lang.get("heading4"),func:"block.format"},h5:{title:this.lang.get("heading5"),func:"block.format"},h6:{title:this.lang.get("heading6"),func:"block.format"}}},bold:{title:this.lang.get("bold-abbr"),icon:!0,label:this.lang.get("bold"),func:"inline.format"},italic:{title:this.lang.get("italic-abbr"),icon:!0,label:this.lang.get("italic"),func:"inline.format"},deleted:{title:this.lang.get("deleted-abbr"),icon:!0,label:this.lang.get("deleted"),func:"inline.format"},underline:{title:this.lang.get("underline-abbr"),icon:!0,label:this.lang.get("underline"),func:"inline.format"},lists:{title:this.lang.get("lists"),icon:!0,dropdown:{unorderedlist:{title:"• "+this.lang.get("unorderedlist"),func:"list.toggle"},orderedlist:{title:"1. "+this.lang.get("orderedlist"),func:"list.toggle"},outdent:{title:"< "+this.lang.get("outdent"),func:"indent.decrease",observe:{element:"li",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},indent:{title:"> "+this.lang.get("indent"),func:"indent.increase",observe:{element:"li",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}}}},ul:{title:"• "+this.lang.get("bulletslist"),icon:!0,func:"list.toggle"},ol:{title:"1. "+this.lang.get("numberslist"),icon:!0,func:"list.toggle"},outdent:{title:this.lang.get("outdent"),icon:!0,func:"indent.decrease"},indent:{title:this.lang.get("indent"),icon:!0,func:"indent.increase"},image:{title:this.lang.get("image"),icon:!0,func:"image.show"},file:{title:this.lang.get("file"),icon:!0,func:"file.show"},link:{title:this.lang.get("link"),icon:!0,dropdown:{link:{title:this.lang.get("link-insert"),func:"link.show",observe:{element:"a",in:{title:this.lang.get("link-edit")},out:{title:this.lang.get("link-insert")}}},unlink:{title:this.lang.get("unlink"),func:"link.unlink",observe:{element:"a",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}}}},horizontalrule:{title:this.lang.get("horizontalrule"),icon:!0,func:"line.insert"}}},setFormatting:function(){for(var t in this.toolbarsButtons.format.dropdown)this.toolbarsButtons.format.dropdown.hasOwnProperty(t)&&-1===this.opts.formatting.indexOf(t)&&delete this.toolbarsButtons.format.dropdown[t]},hideButtons:function(){0!==this.opts.buttonsHide.length&&this.button.hideButtonsSlicer(this.opts.buttonsHide)},hideButtonsOnMobile:function(){this.detect.isMobile()&&0!==this.opts.buttonsHideOnMobile.length&&this.button.hideButtonsSlicer(this.opts.buttonsHideOnMobile)},hideButtonsSlicer:function(e){t.each(e,t.proxy(function(t,e){var i=this.opts.buttons.indexOf(e);-1!==i&&this.opts.buttons.splice(i,1)},this))},load:function(t){this.button.buttons=[],this.opts.buttons.forEach(function(e){if(("image"!==e||this.image.is())&&this.toolbarsButtons.hasOwnProperty(e)){var i=elCreate("li");i.appendChild(this.button.build(e,this.toolbarsButtons[e])[0]),t[0].appendChild(i)}}.bind(this))},buildButtonTooltip:function(){},build:function(e,i){var r=t('<a href="javascript:void(null);" rel="'+e+'" />');if(r.addClass("re-button re-"+e),r.attr({role:"button",tabindex:"-1"}),r.html(i.title),(i.func||i.command||i.dropdown)&&this.button.setEvent(r,e,i),i.dropdown){r.addClass("redactor-toolbar-link-dropdown").attr("aria-haspopup",!0);var n=t('<ul class="dropdownMenu redactor-dropdown-menu redactor-dropdown-menu-'+r[0].rel+'" data-dropdown-allow-flip="horizontal" data-dropdown-ignore-page-scroll="true" />');r.data("dropdown",n),this.dropdown.build(e,n,i.dropdown),this.button.setupDropdown(r[0],n[0])}return this.button.buttons.push(r),r},setupDropdown:function(t,e){require(["Ui/SimpleDropdown"],function(i){i.initFragment(t,e),i.registerCallback(t.id,function(t,e){"close"===e&&this.dropdown.hideOut()}.bind(this)),elData(t,"a11y-mouse-event","mousedown"),elData(t,"aria-expanded",!1),t.addEventListener("click",function(t){t.preventDefault(),t.stopPropagation()})}.bind(this))},getButtons:function(){return this.button.toolbar().find("a.re-button")},getButtonsKeys:function(){return this.button.buttons},setEvent:function(e,i,r){e.on("mousedown",t.proxy(function(t){if(t.preventDefault(),e.hasClass("redactor-button-disabled"))return!1;var n="func",s=r.func;return r.command?(n="command",s=r.command):r.dropdown&&(n="dropdown",s=!1),this.button.toggle(t,i,n,s),!1},this))},toggle:function(t,e,i,r,n){!this.detect.isIe()&&this.detect.isDesktop()||(this.utils.freezeScroll(),t.returnValue=!1),"command"===i?this.inline.format(r):"dropdown"===i?this.dropdown.show(t,e):this.button.clickCallback(t,r,e,n),"dropdown"!==i&&this.dropdown.hideAll(!1),!this.detect.isIe()&&this.detect.isDesktop()||this.utils.unfreezeScroll()},clickCallback:function(e,i,r,n){var s;if(e instanceof Event&&e.preventDefault(),n=void 0===n?r:n,t.isFunction(i))i.call(this,r);else if("-1"!==i.search(/\./)){if(s=i.split("."),void 0===this[s[0]])return;"object"==typeof n?this[s[0]][s[1]].apply(this,n):this[s[0]][s[1]].call(this,n)}else"object"==typeof n?this[i].apply(this,n):this[i].call(this,n);this.observe.buttons(e,r)},all:function(){return this.button.buttons},get:function(t){if(!1!==this.opts.toolbar)return this.button.toolbar().find("a.re-"+t)},set:function(t,e){if(!1!==this.opts.toolbar){var i=this.button.toolbar().find("a.re-"+t);return i.html(e).attr("aria-label",e),i}},add:function(e,i){if(!0!==this.button.isAdded(e))return t();var r=this.button.build(e,{title:i});return this.button.toolbar().append(t("<li>").append(r)),r},addFirst:function(e,i){if(!0!==this.button.isAdded(e))return t();var r=this.button.build(e,{title:i});return this.button.toolbar().prepend(t("<li>").append(r)),r},addAfter:function(e,i,r){if(!0!==this.button.isAdded(i))return t();var n=this.button.build(i,{title:r}),s=this.button.get(e);return 0!==s.length?s.parent().after(t("<li>").append(n)):this.button.toolbar().append(t("<li>").append(n)),n},addBefore:function(e,i,r){if(!0!==this.button.isAdded(i))return t();var n=this.button.build(i,{title:r}),s=this.button.get(e);return 0!==s.length?s.parent().before(t("<li>").append(n)):this.button.toolbar().append(t("<li>").append(n)),n},isAdded:function(t){var e=this.opts.buttonsHideOnMobile.indexOf(t);return!(!1===this.opts.toolbar||-1!==e&&this.detect.isMobile())},setIcon:function(t,e){this.opts.buttonsTextLabeled||t.html(e).addClass("re-button-icon")},changeIcon:function(t,e){var i=this.button.get(t);0!==i.length&&i.find("i").removeAttr("class").addClass("re-icon-"+e)},addCallback:function(e,i){if(void 0!==e&&!1!==this.opts.toolbar){var r="dropdown"===i?"dropdown":"func",n=e.attr("rel");e.on("mousedown",t.proxy(function(t){if(e.hasClass("redactor-button-disabled"))return!1;this.button.toggle(t,n,r,i)},this))}},addDropdown:function(e,i){if(!1!==this.opts.toolbar){e.addClass("redactor-toolbar-link-dropdown").attr("aria-haspopup",!0);var r=e.attr("rel");this.button.addCallback(e,"dropdown");var n=t('<ul class="dropdownMenu redactor-dropdown-menu redactor-dropdown-menu-'+r+'" data-dropdown-allow-flip="horizontal" data-dropdown-ignore-page-scroll="true" />');return e.data("dropdown",n),i&&(this.dropdown.build(r,n,i),this.button.setupDropdown(e[0],n[0])),n}},setActive:function(t){this.button.get(t).addClass("redactor-act").attr({"aria-pressed":!0,tabindex:0})},setInactive:function(t){this.button.get(t).removeClass("redactor-act").attr({"aria-pressed":!1,tabindex:"html"===t?0:-1})},setInactiveAll:function(t){var e=this.button.toolbar().find("a.re-button");void 0!==t&&(e=e.not(".re-"+t)),e.removeClass("redactor-act").attr({"aria-pressed":!1,tabindex:"html"===t?0:-1})},disable:function(t){this.button.get(t).addClass("redactor-button-disabled").attr("aria-disabled",!0)},enable:function(t){this.button.get(t).removeClass("redactor-button-disabled").attr("aria-disabled",!1)},disableAll:function(t){var e=this.button.toolbar().find("a.re-button");void 0!==t&&(Array.isArray(t)||(t=[t]),t=t.map(function(t){return".re-"+t}),e=e.not(t.join(","))),e.addClass("redactor-button-disabled").attr("aria-disabled",!0)},enableAll:function(){this.button.toolbar().find("a.re-button").removeClass("redactor-button-disabled").attr("aria-disabled",!1)},remove:function(t){this.button.get(t).remove()}}},caret:function(){return{set:function(t,e,i){var r=this.core.editor().scrollTop();this.core.editor().focus(),this.core.editor().scrollTop(r),i=void 0===i?0:1,t=t[0]||t,e=e[0]||e;var n=this.selection.get(),s=this.selection.range(n);try{s.setStart(t,0),s.setEnd(e,i)}catch(t){}this.selection.update(n,s)},prepare:function(t){return this.detect.isFirefox()&&void 0!==this.start&&this.core.editor().focus(),t[0]||t},start:function(e){var i,r;if(e=this.caret.prepare(e)){if("BR"===e.tagName)return this.caret.before(e);var n=t(e).children().first(),s=this.utils.isInlineTag(e.tagName);""===e.innerHTML||s?this.caret.setStartEmptyOrInline(e,s):n&&0!==n.length&&this.utils.isInlineTag(n[0].tagName)&&""===n.text()?this.caret.setStartEmptyOrInline(n[0],!0):(i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.selectNodeContents(e),r.collapse(!0),i.addRange(r))}},setStartEmptyOrInline:function(e,i){var r=window.getSelection(),n=document.createRange(),s=document.createTextNode("");n.setStart(e,0),n.insertNode(s),n.setStartAfter(s),n.collapse(!0),r.removeAllRanges(),r.addRange(n),i||this.core.editor().on("keydown.redactor-remove-textnode",function(){t(s).remove(),t(this).off("keydown.redactor-remove-textnode")})},end:function(e){var i,r;if(e=this.caret.prepare(e)){if("BR"!==e.tagName&&""===e.innerHTML)return this.caret.start(e);if("BR"===e.tagName){var n=document.createElement("span");return n.className="redactor-invisible-space",n.innerHTML="​",t(e).after(n),i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.setStartBefore(n),r.setEndBefore(n),i.addRange(r),void t(n).replaceWith(function(){return t(this).contents()})}if(e.lastChild&&1===e.lastChild.nodeType)return this.caret.after(e.lastChild);var i=window.getSelection();if(i.getRangeAt||i.rangeCount)try{var r=i.getRangeAt(0);r.selectNodeContents(e),r.collapse(!1),i.removeAllRanges(),i.addRange(r)}catch(t){}}},after:function(e){var i,r;if(e=this.caret.prepare(e)){if("BR"===e.tagName)return this.caret.end(e);if(this.utils.isBlockTag(e.tagName)){var n=this.caret.next(e);return void(void 0===n?this.caret.end(e):("TABLE"===n.tagName?n=t(n).find("th, td").first()[0]:"UL"!==n.tagName&&"OL"!==n.tagName||(n=t(n).find("li").first()[0]),this.caret.start(n)))}var s=document.createTextNode("");i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.setStartAfter(e),r.insertNode(s),r.setStartAfter(s),r.collapse(!0),i.addRange(r)}},before:function(e){var i,r;if(e=this.caret.prepare(e)){if(this.utils.isBlockTag(e.tagName)){var n=this.caret.prev(e);return void(void 0===n?this.caret.start(e):("TABLE"===n.tagName?n=t(n).find("th, td").last()[0]:"UL"!==n.tagName&&"OL"!==n.tagName||(n=t(n).find("li").last()[0]),this.caret.end(n)))}i=window.getSelection(),i.removeAllRanges(),r=document.createRange(),r.setStartBefore(e),r.collapse(!0),i.addRange(r)}},next:function(e){var i=t(e).next();return i.hasClass("redactor-script-tag, redactor-selection-marker")?i.next()[0]:i[0]},prev:function(e){var i=t(e).prev();return i.hasClass("redactor-script-tag, redactor-selection-marker")?i.prev()[0]:i[0]},offset:function(t){return this.offset.get(t)}}},clean:function(){return{onSet:function(e){e=this.clean.savePreCode(e),this.opts.script&&(e=e.replace(/<script(.*?[^>]?)>([\w\W]*?)<\/script>/gi,'<pre class="redactor-script-tag" $1>$2</pre>')),e=e.replace(/\$/g,"$"),e=e.replace(/&/g,"&"),e=e.replace(/<a href="(.*?[^>]?)®(.*?[^>]?)">/gi,'<a href="$1®$2">'),e=e.replace(/<span id="selection-marker-1"(.*?[^>]?)><\/span>/gi,"###marker1###"),e=e.replace(/<span id="selection-marker-2"(.*?[^>]?)><\/span>/gi,"###marker2###");var i=this,r=t("<div/>").html(t.parseHTML(e,document,!0)),n=this.opts.replaceTags;if(n){var s=Object.keys(this.opts.replaceTags);r.find(s.join(",")).each(function(t,e){i.utils.replaceToTag(e,n[e.tagName.toLowerCase()])})}r.find("span, a").attr("data-redactor-span",!0),r.find(this.opts.inlineTags.join(",")).each(function(){var e=t(this);e.attr("style")&&e.attr("data-redactor-style-cache",e.attr("style"))}),e=r.html();var o=["font","html","head","link","body","meta","applet"];return this.opts.script||o.push("script"),e=this.clean.stripTags(e,o),this.opts.removeComments&&(e=e.replace(/<!--[\s\S]*?-->/gi,"")),e=this.paragraphize.load(e),e=e.replace("###marker1###",'<span id="selection-marker-1" class="redactor-selection-marker"></span>'),e=e.replace("###marker2###",'<span id="selection-marker-2" class="redactor-selection-marker"></span>'),-1!==e.search(/^(||\s||<br\s?\/?>|| )$/i)?this.opts.emptyHtml:e},onGet:function(t){return this.clean.onSync(t)},onSync:function(e){if(e=e.replace(/\u200B/g,""),e=e.replace(/​/gi,""),-1!==e.search(/^<p>(||\s||<br\s?\/?>|| )<\/p>$/i))return"";e=e.replace(/<span(.*?)id="redactor-image-box"(.*?[^>])>([\w\W]*?)<img(.*?)><\/span>/gi,"$3<img$4>"),
+e=e.replace(/<span(.*?)id="redactor-image-resizer"(.*?[^>])>(.*?)<\/span>/gi,""),e=e.replace(/<span(.*?)id="redactor-image-editter"(.*?[^>])>(.*?)<\/span>/gi,""),e=e.replace(/<img(.*?)style="(.*?)opacity: 0\.5;(.*?)"(.*?)>/gi,'<img$1style="$2$3"$4>');var i=t("<div/>").html(t.parseHTML(e,document,!0));i.find('*[style=""]').removeAttr("style"),i.find('*[class=""]').removeAttr("class"),i.find('*[rel=""]').removeAttr("rel"),i.find('*[data-image=""]').removeAttr("data-image"),i.find('*[alt=""]').removeAttr("alt"),i.find('*[title=""]').removeAttr("title"),i.find("*[data-redactor-style-cache]").removeAttr("data-redactor-style-cache"),i.find(".redactor-invisible-space, .redactor-unlink").each(function(){t(this).contents().unwrap()}),i.find("span, a").removeAttr("data-redactor-span data-redactor-style-cache").each(function(){0===this.attributes.length&&t(this).contents().unwrap()}),i.find("img").removeAttr("rel"),i.find(".redactor-selection-marker, #redactor-insert-marker").remove(),e=i.html(),this.opts.script&&(e=e.replace(/<pre class="redactor-script-tag"(.*?[^>]?)>([\w\W]*?)<\/pre>/gi,"<script$1>$2<\/script>")),e=this.clean.restoreFormTags(e),e=e.replace(new RegExp("<br\\s?/?></h","gi"),"</h"),e=e.replace(new RegExp("<br\\s?/?></li>","gi"),"</li>"),e=e.replace(new RegExp("</li><br\\s?/?>","gi"),"</li>"),e=e.replace(/<pre>/gi,"<pre>\n"),this.opts.preClass&&(e=e.replace(/<pre>/gi,'<pre class="'+this.opts.preClass+'">')),this.opts.linkNofollow&&(e=e.replace(/<a(.*?)rel="nofollow"(.*?[^>])>/gi,"<a$1$2>"),e=e.replace(/<a(.*?[^>])>/gi,'<a$1 rel="nofollow">'));var r={"™":"™","©":"©","…":"…","—":"—","‐":"‐"};return t.each(r,function(t,i){e=e.replace(new RegExp(t,"g"),i)}),e=e.replace(/&/g,"&"),e=e.replace(/\n{2,}/g,"\n"),this.opts.removeNewlines&&(e=e.replace(/\r?\n/g,"")),e},onPaste:function(e,i,r){if(!0!==r){e=e.replace(/<b\sid="internal-source-marker(.*?)">([\w\W]*?)<\/b>/gi,"$2"),e=e.replace(/<b(.*?)id="docs-internal-guid(.*?)">([\w\W]*?)<\/b>/gi,"$3"),e=e.replace(/<span[^>]*(font-style: italic; font-weight: bold|font-weight: bold; font-style: italic)[^>]*>([\w\W]*?)<\/span>/gi,"<b><i>$2</i></b>"),e=e.replace(/<span[^>]*(font-style: italic; font-weight: 700|font-weight: 700; font-style: italic)[^>]*>([\w\W]*?)<\/span>/gi,"<b><i>$2</i></b>"),e=e.replace(/<span[^>]*font-style: italic[^>]*>([\w\W]*?)<\/span>/gi,"<i>$1</i>"),e=e.replace(/<span[^>]*font-weight: bold[^>]*>([\w\W]*?)<\/span>/gi,"<b>$1</b>"),e=e.replace(/<span[^>]*font-weight: 700[^>]*>([\w\W]*?)<\/span>/gi,"<b>$1</b>"),e=e.replace(/<o:p[^>]*>/gi,""),e=e.replace(/<\/o:p>/gi,"");this.clean.isHtmlMsWord(e)&&(e=this.clean.cleanMsWord(e))}return e=t.trim(e),i.pre?this.opts.preSpaces&&(e=e.replace(/\t/g,new Array(this.opts.preSpaces+1).join(" "))):(e=this.clean.replaceBrToNl(e),e=this.clean.removeTagsInsidePre(e)),!0!==r&&(e=this.clean.removeEmptyInlineTags(e),!1===i.encode&&(e=e.replace(/&/g,"&"),e=this.clean.convertTags(e,i),e=this.clean.getPlainText(e),e=this.clean.reconvertTags(e,i))),i.text&&(e=this.clean.replaceNbspToSpaces(e),e=this.clean.getPlainText(e)),i.lists&&(e=e.replace("\n","<br>")),i.encode&&(e=this.clean.encodeHtml(e)),i.paragraphize&&(e=e.replace(/ \n/g," "),e=e.replace(/\n /g," "),e=this.paragraphize.load(e),e=e.replace(/<p><\/p>/g,"")),e=e.replace(/<li><p>/g,"<li>"),e=e.replace(/<\/p><\/li>/g,"</li>")},getCurrentType:function(t,e){var i=this.selection.blocks(),r={text:!1,encode:!1,paragraphize:!0,line:this.clean.isHtmlLine(t),blocks:this.clean.isHtmlBlocked(t),pre:!1,lists:!1,block:!0,inline:!0,links:!0,images:!0};return 1===i.length&&this.utils.isCurrentOrParent(["h1","h2","h3","h4","h5","h6","a","figcaption"])?(r.text=!0,r.paragraphize=!1,r.inline=!1,r.images=!1,r.links=!1,r.line=!0):"inline"===this.opts.type||!1===this.opts.enterKey?(r.paragraphize=!1,r.block=!1,r.line=!0):1===i.length&&this.utils.isCurrentOrParent(["li"])?(r.lists=!0,r.block=!1,r.paragraphize=!1,r.images=!1):1===i.length&&this.utils.isCurrentOrParent(["th","td","blockquote"])?(r.block=!1,r.paragraphize=!1):("pre"===this.opts.type||1===i.length&&this.utils.isCurrentOrParent("pre"))&&(r.inline=!1,r.block=!1,r.encode=!0,r.pre=!0,r.paragraphize=!1,r.images=!1,r.links=!1),!0===r.line&&(r.paragraphize=!1),!0===e&&(r.text=!1),r},isHtmlBlocked:function(t){var e=t.match(new RegExp("</("+this.opts.blockTags.join("|").toUpperCase()+")>","gi")),i=t.match(new RegExp("<hr(.*?[^>])>","gi"));return null!==e||null!==i},isHtmlLine:function(t){if(this.clean.isHtmlBlocked(t))return!1;var e=t.match(/<br\s?\/?>/gi),i=t.match(/\n/gi);return!e&&!i},isHtmlMsWord:function(t){return t.match(/class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i)},removeEmptyInlineTags:function(e){var i=this.opts.inlineTags,r=t("<div/>").html(t.parseHTML(e,document,!0)),n=this,s=r.find("span"),o=r.find(i.join(","));return o.removeAttr("style"),o.each(function(){var e=t(this).html();0===this.attributes.length&&n.utils.isEmpty(e)&&t(this).replaceWith(function(){return t(this).contents()})}),s.each(function(){t(this).html();0===this.attributes.length&&t(this).replaceWith(function(){return t(this).contents()})}),e=r.html(),e=e.replace("\x3c!--?php","<?php"),e=e.replace("\x3c!--?","<?"),e=e.replace("?--\x3e","?>"),r.remove(),e},cleanMsWord:function(e){e=e.replace(/<!--[\s\S]*?-->/g,""),e=e.replace(/<o:p>[\s\S]*?<\/o:p>/gi,""),e=e.replace(/\n/g," "),e=e.replace(/<br\s?\/?>|<\/p>|<\/div>|<\/li>|<\/td>/gi,"\n\n");var i=t("<div/>").html(e),r=!1,n=1,s=[];return i.find("p[style]").each(function(){var e=t(this).attr("style").match(/mso\-list\:l([0-9]+)\slevel([0-9]+)/);if(e){var o=parseInt(e[1]),a=parseInt(e[2]),l=t(this).html().match(/^[\w]+\./)?"ol":"ul",c=t("<li/>").html(t(this).html());if(c.html(c.html().replace(/^([\w\.]+)</,"<")),c.find("span:first").remove(),1==a&&-1==t.inArray(o,s)){var h=t("<"+l+"/>").attr({"data-level":a,"data-list":o}).html(c);t(this).replaceWith(h),r=o,s.push(o)}else{if(a>n){for(var d=i.find('[data-level="'+n+'"][data-list="'+r+'"]'),u=d,p=n;p<a;p++)h=t("<"+l+"/>"),h.appendTo(u.find("li").last()),u=h;u.attr({"data-level":a,"data-list":o}).html(c)}else{var d=i.find('[data-level="'+a+'"][data-list="'+o+'"]').last();d.append(c)}n=a,r=o,t(this).remove()}}}),i.find("[data-level][data-list]").removeAttr("data-level data-list"),e=i.html()},replaceNbspToSpaces:function(t){return t.replace(" "," ")},replaceBrToNl:function(t){return t.replace(/<br\s?\/?>/gi,"\n")},replaceNlToBr:function(t){return t.replace(/\n/g,"<br />")},convertTags:function(e,i){var r=t("<div>").html(e);r.find("iframe").remove();var n=r.find("a");if(n.removeAttr("style"),!1!==this.opts.pasteLinkTarget&&n.attr("target",this.opts.pasteLinkTarget),i.links&&this.opts.pasteLinks&&r.find("a").each(function(t,e){if(e.href){for(var i,r='#####[a href="'+e.href+'"',n=0,s=e.attributes.length;n<s;n++)i=e.attributes.item(n),"href"!==i.name&&(r+=" "+i.name+'="'+i.value+'"');e.outerHTML=r+"]#####"+e.innerHTML+"#####[/a]#####"}}),e=r.html(),i.images&&this.opts.pasteImages&&(e=e.replace(/<img(.*?)src="(.*?)"(.*?[^>])>/gi,'#####[img$1src="$2"$3]#####')),this.opts.pastePlainText)return e;var s,o=i.lists?["ul","ol","li"]:this.opts.pasteBlockTags;s=i.block||i.lists?i.inline?o.concat(this.opts.pasteInlineTags):o:i.inline?this.opts.pasteInlineTags:[];for(var a=s.length,l=0;l<a;l++)e=e.replace(new RegExp("</"+s[l]+">","gi"),"###/"+s[l]+"###"),"td"===s[l]||"th"===s[l]?e=e.replace(new RegExp("<"+s[l]+'(.*?[^>])((colspan|rowspan)="(.*?[^>])")?(.*?[^>])>',"gi"),"###"+s[l]+" $2###"):this.utils.isInlineTag(s[l])?(e=e.replace(new RegExp("<"+s[l]+'([^>]*)class="([^>]*)"[^>]*>',"gi"),"###"+s[l]+' class="$2"###'),e=e.replace(new RegExp("<"+s[l]+'([^>]*)data-redactor-style-cache="([^>]*)"[^>]*>',"gi"),"###"+s[l]+' cache="$2"###'),e=e.replace(new RegExp("<"+s[l]+"[^>]*>","gi"),"###"+s[l]+"###")):e=e.replace(new RegExp("<"+s[l]+"[^>]*>","gi"),"###"+s[l]+"###");return e},reconvertTags:function(t,e){if((e.links&&this.opts.pasteLinks||e.images&&this.opts.pasteImages)&&(t=t.replace(new RegExp("#####\\[","gi"),"<"),t=t.replace(new RegExp("\\]#####","gi"),">")),this.opts.pastePlainText)return t;var i,r=e.lists?["ul","ol","li"]:this.opts.pasteBlockTags;i=e.block||e.lists?e.inline?r.concat(this.opts.pasteInlineTags):r:e.inline?this.opts.pasteInlineTags:[];for(var n=i.length,s=0;s<n;s++)t=t.replace(new RegExp("###/"+i[s]+"###","gi"),"</"+i[s]+">");for(var s=0;s<n;s++)t=t.replace(new RegExp("###"+i[s]+"###","gi"),"<"+i[s]+">");for(var s=0;s<n;s++)if("td"===i[s]||"th"===i[s])t=t.replace(new RegExp("###"+i[s]+"s?(.*?[^#])###","gi"),"<"+i[s]+"$1>");else if(this.utils.isInlineTag(i[s])){var o="span"===i[s]?' data-redactor-span="true"':"";t=t.replace(new RegExp("###"+i[s]+' cache="(.*?[^#])"###',"gi"),"<"+i[s]+' style="$1"'+o+' data-redactor-style-cache="$1">'),t=t.replace(new RegExp("###"+i[s]+"s?(.*?[^#])###","gi"),"<"+i[s]+"$1>")}return t},cleanPre:function(e){e=void 0===e?t(this.selection.block()).closest("pre",this.core.editor()[0]):e,t(e).find("br").replaceWith(function(){return document.createTextNode("\n")}),t(e).find("p").replaceWith(function(){return t(this).contents()})},removeTagsInsidePre:function(e){var i=t("<div />").append(e);return i.find("pre").replaceWith(function(){var e=t(this).html();return e=e.replace(/<br\s?\/?>|<\/p>|<\/div>|<\/li>|<\/td>/gi,"\n"),e=e.replace(/(<([^>]+)>)/gi,""),t("<pre />").append(e)}),e=i.html(),i.remove(),e},getPlainText:function(e){e=e.replace(/<!--[\s\S]*?-->/gi,""),e=e.replace(/<style[\s\S]*?style>/gi,""),e=e.replace(/<p><\/p>/g,""),e=e.replace(/<\/div>|<\/li>|<\/td>/gi,"\n"),e=e.replace(/<\/p>/gi,"\n\n"),e=e.replace(/<\/H[1-6]>/gi,"\n\n");var i=document.createElement("div");return i.innerHTML=e,e=i.textContent||i.innerText,t.trim(e)},savePreCode:function(t){return t=this.clean.savePreFormatting(t),t=this.clean.saveCodeFormatting(t),t=this.clean.restoreSelectionMarkers(t)},savePreFormatting:function(e){var i=e.match(/<pre(.*?)>([\w\W]*?)<\/pre>/gi);return null===i?e:(t.each(i,t.proxy(function(t,i){var r,n,s,o=[],a=!1;i.match(/<pre(.*?)>(([\n\r\s]+)?)<code(.*?)>/i)?(o=i.match(/<pre(.*?)>(([\n\r\s]+)?)<code(.*?)>([\w\W]*?)<\/code>(([\n\r\s]+)?)<\/pre>/i),a=!0,r=o[5],n=o[1],s=o[4]):(o=i.match(/<pre(.*?)>([\w\W]*?)<\/pre>/i),r=o[2],n=o[1]),r=r.replace(/<br\s?\/?>/g,"\n"),r=r.replace(/ /g," "),this.opts.preSpaces&&(r=r.replace(/\t/g,new Array(this.opts.preSpaces+1).join(" "))),r=this.clean.encodeEntities(r),r=r.replace(/\$/g,"$"),e=a?e.replace(i,"<pre"+n+"><code"+s+">"+r+"</code></pre>"):e.replace(i,"<pre"+n+">"+r+"</pre>")},this)),e)},saveCodeFormatting:function(e){var i=e.match(/<code(.*?)>([\w\W]*?)<\/code>/gi);return null===i?e:(t.each(i,t.proxy(function(t,i){var r=i.match(/<code(.*?)>([\w\W]*?)<\/code>/i);r[2]=r[2].replace(/ /g," "),r[2]=this.clean.encodeEntities(r[2]),r[2]=r[2].replace(/\$/g,"$"),e=e.replace(i,"<code"+r[1]+">"+r[2]+"</code>")},this)),e)},restoreSelectionMarkers:function(t){return t=t.replace(/<span id="selection-marker-([0-9])" class="redactor-selection-marker"><\/span>/g,'<span id="selection-marker-$1" class="redactor-selection-marker"></span>')},saveFormTags:function(t){return t},restoreFormTags:function(t){return t.replace(/<section(.*?) rel="redactor-form-tag"(.*?)>([\w\W]*?)<\/section>/gi,"<form$1$2>$3</form>")},encodeHtml:function(t){return t=t.replace(/”/g,'"'),t=t.replace(/“/g,'"'),t=t.replace(/‘/g,"'"),t=t.replace(/’/g,"'"),t=this.clean.encodeEntities(t)},encodeEntities:function(t){return t=String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"'),t=t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},stripTags:function(t,e){if(void 0===e)return t.replace(/(<([^>]+)>)/gi,"");var i=/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi;return t.replace(i,function(t,i){return-1===e.indexOf(i.toLowerCase())?t:""})},removeMarkers:function(t){return t.replace(/<span(.*?[^>]?)class="redactor-selection-marker"(.*?[^>]?)>([\w\W]*?)<\/span>/gi,"")},removeSpaces:function(e){return e=t.trim(e),e=e.replace(/\n/g,""),e=e.replace(/[\t]*/g,""),e=e.replace(/\n\s*\n/g,"\n"),e=e.replace(/^[\s\n]*/g," "),e=e.replace(/[\s\n]*$/g," "),e=e.replace(/>\s{2,}</g,"> <"),e=e.replace(/\n\n/g,"\n"),e=e.replace(/\u200B/g,"")},removeSpacesHard:function(e){return e=t.trim(e),e=e.replace(/\n/g,""),e=e.replace(/[\t]*/g,""),e=e.replace(/\n\s*\n/g,"\n"),e=e.replace(/^[\s\n]*/g,""),e=e.replace(/[\s\n]*$/g,""),e=e.replace(/>\s{2,}</g,"><"),e=e.replace(/\n\n/g,"\n"),e=e.replace(/\u200B/g,"")},normalizeCurrentHeading:function(){var t=this.selection.block();this.utils.isCurrentOrParentHeader()&&t&&t.normalize()}}},code:function(){return{syncFire:!0,html:!1,start:function(e){e=t.trim(e),e=e.replace(/^(<span id="selection-marker-1" class="redactor-selection-marker"><\/span>)/,""),e=this.clean.onSet(e),e=e.replace(/<p><span id="selection-marker-1" class="redactor-selection-marker"><\/span><\/p>/,""),this.events.stopDetectChanges(),this.core.editor().html(e),this.observe.load(),this.events.startDetectChanges()},set:function(e,i){e=t.trim(e),i=i||{},i.start&&(this.start=i.start),"textarea"===this.opts.type?e=this.clean.onSet(e):"div"===this.opts.type&&""===e&&(e=this.opts.emptyHtml),this.core.editor().html(e),"textarea"===this.opts.type&&this.code.sync()},get:function(){if("textarea"===this.opts.type)return this.core.textarea().val();var t=this.core.editor().html();return t=this.clean.onGet(t)},sync:function(){if(this.code.syncFire){var e=this.core.editor().html(),i=this.code.cleaned(e);if(!this.code.isSync(i)){if(this.code.html=i,"textarea"!==this.opts.type)return this.core.callback("sync",e),void this.core.callback("change",e);"textarea"===this.opts.type&&setTimeout(t.proxy(function(){this.code.startSync(e)},this),10)}}},startSync:function(t){t=this.core.callback("syncBefore",t),t=this.clean.onSync(t),this.core.textarea().val(t),this.core.callback("sync",t),!1===this.start&&this.core.callback("change",t),this.start=!1},isSync:function(t){var e=!1!==this.code.html&&this.code.html;return!1!==e&&e===t},cleaned:function(t){return t=t.replace(/\u200B/g,""),this.clean.removeMarkers(t)}}},core:function(){return{id:function(){return this.$editor.attr("id")},element:function(){return this.$element},editor:function(){return void 0===this.$editor?t():this.$editor},textarea:function(){return this.$textarea},box:function(){return"textarea"===this.opts.type?this.$box:this.$element},toolbar:function(){return!!this.$toolbar&&this.$toolbar},air:function(){return!!this.$air&&this.$air},object:function(){return t.extend({},this)},structure:function(){this.core.editor().toggleClass("redactor-structure")},addEvent:function(t){this.core.event=t},getEvent:function(){return this.core.event},callback:function(e,i,r){var n=!1,s=t._data(this.core.element()[0],"events");if(void 0!==s&&void 0!==s[e])for(var o=s[e].length,a=0;a<o;a++){var l=s[e][a].namespace;if("callback.redactor"===l){var c=s[e][a].handler,h=void 0===r?[i]:[i,r];n=void 0===h?c.call(this,i):c.call(this,i,h)}}if(n)return n;if(void 0===this.opts.callbacks[e])return void 0===r?i:r;var d=this.opts.callbacks[e];return t.isFunction(d)?void 0===r?d.call(this,i):d.call(this,i,r):void 0===r?i:r},destroy:function(){this.opts.destroyed=!0,this.core.callback("destroy"),t("#redactor-voice-"+this.uuid).remove(),this.core.editor().removeClass("redactor-in redactor-styles redactor-structure redactor-layer-img-edit"),this.core.editor().off("keydown.redactor-remove-textnode"),this.core.editor().off(".redactor-observe."+this.uuid),this.$element.off(".redactor").removeData("redactor"),this.core.editor().off(".redactor"),t(document).off(".redactor-air."+this.uuid),t(document).off("mousedown.redactor-blur."+this.uuid),t(document).off("mousedown.redactor."+this.uuid),t(document).off("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid),t(window).off(".redactor-toolbar."+this.uuid),t(window).off("touchmove.redactor."+this.uuid),t("body").off("scroll.redactor."+this.uuid),t(this.opts.toolbarFixedTarget).off("scroll.redactor."+this.uuid);var e=this;!1!==this.opts.plugins&&t.each(this.opts.plugins,function(i,r){t(window).off(".redactor-plugin-"+r),t(document).off(".redactor-plugin-"+r),t("body").off(".redactor-plugin-"+r),e.core.editor().off(".redactor-plugin-"+r)}),this.$element.off("click.redactor-click-to-edit"),this.$element.removeClass("redactor-click-to-edit"),this.core.editor().removeClass("redactor-layer"),this.core.editor().removeAttr("contenteditable");var i=this.code.get();this.opts.toolbar&&this.$toolbar&&this.$toolbar.find("a").each(function(){var e=t(this);e.data("dropdown")&&(e.data("dropdown").remove(),e.data("dropdown",{}))}),"textarea"===this.opts.type&&(this.$box.after(this.$element),this.$box.remove(),this.$element.val(i).show()),this.opts.toolbar&&this.$toolbar&&this.$toolbar.remove(),this.$modalBox&&this.$modalBox.remove(),this.$modalOverlay&&this.$modalOverlay.remove(),t(".redactor-link-tooltip").remove()}}},detect:function(){return{isWebkit:function(){return/webkit/.test(this.opts.userAgent)},isFirefox:function(){return this.opts.userAgent.indexOf("firefox")>-1},isIe:function(t){if(document.documentMode||/Edge/.test(navigator.userAgent))return"edge";var e;return e=RegExp("msie"+(isNaN(t)?"":"\\s"+t),"i").test(navigator.userAgent),e||(e=!!navigator.userAgent.match(/Trident.*rv[ :]*11\./)),e},isMobile:function(){return/(iPhone|iPod|BlackBerry|Android)/.test(navigator.userAgent)},isDesktop:function(){return!/(iPhone|iPod|iPad|BlackBerry|Android)/.test(navigator.userAgent)},isIpad:function(){return/iPad/.test(navigator.userAgent)}}},dropdown:function(){return{active:!1,button:!1,key:!1,position:[],getDropdown:function(){return this.dropdown.active},build:function(e,i,r){var n,s=document.createDocumentFragment();for(var o in r)if(r.hasOwnProperty(o)){n=r[o];var a=this.dropdown.buildItem(o,n);this.observe.addDropdown(t(a),o,n),s.appendChild(a)}for(var l=!1,c=0,h=s.childNodes.length;c<h;c++)if(s.childNodes[c].nodeType===Node.ELEMENT_NODE){l=!0;break}l&&(i[0].rel=e,i[0].appendChild(s))},buildFormatting:function(){},buildItem:function(t,e){var i=elCreate("li");return void 0!==e.classname&&i.classList.add(e.classname),0===t.toLowerCase().indexOf("divider")?(i.classList.add("redactor-dropdown-divider"),i):(i.innerHTML='<a href="#" class="redactor-dropdown-'+t+'" role="button"><span>'+e.title+"</span></a>",i.children[0].addEventListener("mousedown",function(i){i.preventDefault(),this.dropdown.buildClick(i,t,e)}.bind(this)),i)},buildClick:function(e,i,r){if(!t(e.target).hasClass("redactor-dropdown-link-inactive")){var n=this.dropdown.buildCommand(r);void 0!==r.args?this.button.toggle(e,i,n.type,n.callback,r.args):this.button.toggle(e,i,n.type,n.callback)}},buildCommand:function(t){var e={};return e.type="func",e.callback=t.func,t.command?(e.type="command",e.callback=t.command):t.dropdown&&(e.type="dropdown",e.callback=t.dropdown),e},show:function(e,i){this.detect.isDesktop()&&this.core.editor().focus(),this.dropdown.hideAll(!1,i),this.dropdown.key=i,this.dropdown.button=this.button.get(this.dropdown.key),require(["Ui/SimpleDropdown"],function(e){var i=this.dropdown.button[0].id;e.toggleDropdown(i),e.isOpen(i)?(this.dropdown.active=t(e.getDropdownMenu(i)),this.core.callback("dropdownShow",{dropdown:this.dropdown.active,key:this.dropdown.key,button:this.dropdown.button}),this.button.setActive(this.dropdown.key),this.dropdown.button.addClass("dropact").attr("aria-expanded",!0),this.dropdown.enableCallback()):this.dropdown.hide()}.bind(this)),e.preventDefault()},showIsFixedToolbar:function(){},showIsUnFixedToolbar:function(){},enableEvents:function(){},enableCallback:function(){this.core.callback("dropdownShown",{dropdown:this.dropdown.active,key:this.dropdown.key,button:this.dropdown.button})},getButtonPosition:function(){},closeHandler:function(){},hideAll:function(t,e){this.dropdown.hideOut(e)},hide:function(){this.dropdown.hideOut()},hideOut:function(t){if(!1!==this.dropdown.active&&this.dropdown.button[0].rel!==t){this.core.callback("dropdownHide",this.dropdown.active);var e=this.dropdown.button[0].id;require(["Ui/SimpleDropdown"],function(t){t.close(e)}),this.dropdown.button.removeClass("redactor-act dropact").attr("aria-expanded",!1),this.dropdown.button=!1,this.dropdown.key=!1,this.dropdown.active=!1}}}},events:function(){return{focused:!1,blured:!0,dropImage:!1,stopChanges:!1,stopDetectChanges:function(){this.events.stopChanges=!0},startDetectChanges:function(){var t=this;setTimeout(function(){t.events.stopChanges=!1},1)},dragover:function(e){e.preventDefault(),e.stopPropagation(),"IMG"===e.target.tagName&&t(e.target).addClass("redactor-image-dragover")},dragleave:function(t){this.core.editor().find("img").removeClass("redactor-image-dragover")},drop:function(t){return t=t.originalEvent||t,this.core.editor().find("img").removeClass("redactor-image-dragover"),"inline"===this.opts.type||"pre"===this.opts.type?(t.preventDefault(),!1):void 0===window.FormData||!t.dataTransfer||(0===t.dataTransfer.files.length?this.events.onDrop(t):(this.events.onDropUpload(t),void this.core.callback("drop",t)))},click:function(t){var e=this.core.getEvent(),i="click"!==e&&"arrow"!==e&&"click";this.core.addEvent(i),this.utils.disableSelectAll(),this.core.callback("click",t)},focus:function(t){if(!this.rtePaste&&(this.events.isCallback("focus")&&this.core.callback("focus",t),this.events.focused=!0,this.events.blured=!1,!1===this.selection.current())){var e=this.selection.get(),i=this.selection.range(e);i.setStart(this.core.editor()[0],0),i.setEnd(this.core.editor()[0],0),this.selection.update(e,i)}},blur:function(e){this.start||this.rtePaste||0===t(e.target).closest("#"+this.core.id()+", .redactor-toolbar, .redactor-dropdown, #redactor-modal-box").length&&(!this.events.blured&&this.events.isCallback("blur")&&this.core.callback("blur",e),this.events.focused=!1,this.events.blured=!0)},touchImageEditing:function(){var e=-1;this.events.imageEditing=!1,t(window).on("touchmove.redactor."+this.uuid,t.proxy(function(){this.events.imageEditing=!0,-1!==e&&clearTimeout(e),e=setTimeout(t.proxy(function(){this.events.imageEditing=!1},this),500)},this))},init:function(){this.core.editor().on("dragover.redactor dragenter.redactor",t.proxy(this.events.dragover,this)),this.core.editor().on("dragleave.redactor",t.proxy(this.events.dragleave,this)),this.core.editor().on("drop.redactor",t.proxy(this.events.drop,this)),this.core.editor().on("click.redactor",t.proxy(this.events.click,this)),this.core.editor().on("paste.redactor",t.proxy(this.paste.init,this)),this.core.editor().on("keydown.redactor",t.proxy(this.keydown.init,this)),this.core.editor().on("keyup.redactor",t.proxy(this.keyup.init,this)),this.core.editor().on("focus.redactor",t.proxy(this.events.focus,this)),t(document).on("mousedown.redactor-blur."+this.uuid,t.proxy(this.events.blur,this)),this.events.touchImageEditing(),this.events.createObserver(),this.events.setupObserver()},createObserver:function(){var e=this;this.events.observer=new MutationObserver(function(i){i.forEach(t.proxy(e.events.iterateObserver,e))})},iterateObserver:function(t){var e=!1;(("textarea"===this.opts.type||"div"===this.opts.type)&&!this.detect.isFirefox()&&t.target===this.core.editor()[0]||"class"===t.attributeName&&t.target===this.core.editor()[0]||"data-vivaldi-spatnav-clickable"==t.attributeName)&&(e=!0),e||(this.observe.load(),this.events.changeHandler())},setupObserver:function(){this.events.observer.observe(this.core.editor()[0],{attributes:!0,subtree:!0,childList:!0,characterData:!0,characterDataOldValue:!0})},changeHandler:function(){this.events.stopChanges||this.code.sync()},onDropUpload:function(t){if(t.preventDefault(),t.stopPropagation(),(this.opts.dragImageUpload||this.opts.dragFileUpload)&&(null!==this.opts.imageUpload||null!==this.opts.fileUpload)){"IMG"===t.target.tagName&&(this.events.dropImage=t.target);for(var e=t.dataTransfer.files,i=e.length,r=0;r<i;r++)this.upload.directUpload(e[r],t)}},onDrop:function(t){this.core.callback("drop",t)},isCallback:function(e){return void 0!==this.opts.callbacks[e]&&t.isFunction(this.opts.callbacks[e])},stopDetect:function(){this.events.stopDetectChanges()},startDetect:function(){this.events.startDetectChanges()}}},file:function(){return{is:function(){},show:function(){},insert:function(){},release:function(){},text:function(t){}}},focus:function(){return{start:function(){if(this.core.editor().focus(),"inline"!==this.opts.type){var t=this.focus.first();!1!==t&&this.caret.start(t)}},end:function(){this.core.editor().focus();var t=this.opts.inline?this.core.editor():this.focus.last();if(0!==t.length){var e=this.focus.lastChild(t);if(this.detect.isWebkit()||!1===e){var i=this.selection.get(),r=this.selection.range(i);null!==r?(r.selectNodeContents(t[0]),r.collapse(!1),this.selection.update(i,r)):this.caret.end(t)}else this.caret.end(e)}},first:function(){var t=this.core.editor().children().first();return(0!==t.length||0!==t[0].length&&"BR"!==t[0].tagName&&"HR"!==t[0].tagName&&3!==t[0].nodeType)&&("UL"===t[0].tagName||"OL"===t[0].tagName?t.find("li").first():t)},last:function(){return this.core.editor().children().last()},lastChild:function(t){var e=t[0].lastChild;return!(null===e||!this.utils.isInlineTag(e.tagName))&&e},is:function(){return this.core.editor()[0]===document.activeElement}}},image:function(){return{is:function(){return!(!this.opts.imageUpload||!this.opts.imageUpload&&!this.opts.s3)},show:function(){this.modal.load("image",this.lang.get("image"),700),this.upload.init("#redactor-modal-image-droparea",this.opts.imageUpload,this.image.insert),this.modal.show()},insert:function(e,i,r){var n;if(void 0!==e.error)return this.modal.close(),this.events.dropImage=!1,void this.core.callback("imageUploadError",e,r);if(!1!==this.events.dropImage)return n=t(this.events.dropImage),this.core.callback("imageDelete",n[0].src,n),n.attr("src",e.url),this.events.dropImage=!1,void this.core.callback("imageUpload",n,e);var s=t("<"+this.opts.imageTag+">");n=t("<img>"),n.attr("src",e.url);var o=void 0===e.id?"":e.id,a=void 0===e.s3?"image":"s3";n.attr("data-"+a,o),s.append(n);var l=this.utils.isTag(this.selection.current(),"pre");if(i){this.marker.remove();var c=this.insert.nodeToPoint(r,this.marker.get()),h=t(c).next();this.selection.restore(),this.buffer.set(),void 0!==h&&0!==h.length&&"IMG"===h[0].tagName?(this.core.callback("imageDelete",h[0].src,h),h.closest("figure, p",this.core.editor()[0]).replaceWith(s),this.caret.after(s)):(l?t(l).after(s):this.insert.node(s),this.caret.after(s))}else this.modal.close(),this.buffer.set(),l?t(l).after(s):this.insert.node(s),this.caret.after(s);this.events.dropImage=!1;var d=n[0].nextSibling,u=s.next(),p=t(d).text().replace(/\u200B/g,""),f=u.text().replace(/\u200B/g,"");""===p&&t(d).remove(),1===u.length&&"FIGURE"===u[0].tagName&&""===f&&u.remove(),null!==i?this.core.callback("imageUpload",n,e):this.core.callback("imageInserted",n,e)},setEditable:function(e){if(e.on("dragstart",function(t){t.preventDefault()}),this.opts.imageResizable){var i=t.proxy(function(i){this.observe.image=e,this.image.resizer=this.image.loadEditableControls(e),t(document).on("mousedown.redactor-image-resize-hide."+this.uuid,t.proxy(this.image.hideResize,this)),this.image.resizer&&this.image.resizer.on("mousedown.redactor touchstart.redactor",t.proxy(function(t){this.image.setResizable(t,e)},this))},this);e.off("mousedown.redactor").on("mousedown.redactor",t.proxy(this.image.hideResize,this)),e.off("click.redactor touchstart.redactor").on("click.redactor touchstart.redactor",i)}else e.off("click.redactor touchstart.redactor").on("click.redactor touchstart.redactor",t.proxy(function(i){setTimeout(t.proxy(function(){this.image.showEdit(e)},this),200)},this))},setResizable:function(t,e){t.preventDefault(),this.image.resizeHandle={x:t.pageX,y:t.pageY,el:e,ratio:e.width()/e.height(),h:e.height()},t=t.originalEvent||t,t.targetTouches&&(this.image.resizeHandle.x=t.targetTouches[0].pageX,this.image.resizeHandle.y=t.targetTouches[0].pageY),this.image.startResize()},startResize:function(){t(document).on("mousemove.redactor-image-resize touchmove.redactor-image-resize",t.proxy(this.image.moveResize,this)),t(document).on("mouseup.redactor-image-resize touchend.redactor-image-resize",t.proxy(this.image.stopResize,this))},moveResize:function(t){t.preventDefault(),t=t.originalEvent||t;var e=this.image.resizeHandle.h;t.targetTouches?e+=t.targetTouches[0].pageY-this.image.resizeHandle.y:e+=t.pageY-this.image.resizeHandle.y;var i=Math.round(e*this.image.resizeHandle.ratio);e<50||i<100||this.core.editor().width()<=i||(this.image.resizeHandle.el.attr({width:i,height:e}),this.image.resizeHandle.el.width(i),this.image.resizeHandle.el.height(e),this.code.sync())},stopResize:function(){this.handle=!1,t(document).off(".redactor-image-resize"),this.image.hideResize()},hideResize:function(e){if(!e||0===t(e.target).closest("#redactor-image-box",this.$editor[0]).length){if(e&&"IMG"==e.target.tagName){t(e.target)}var i=this.$editor.find("#redactor-image-box");0!==i.length&&(t("#redactor-image-editter").remove(),t("#redactor-image-resizer").remove(),i.find("img").css({marginTop:i[0].style.marginTop,marginBottom:i[0].style.marginBottom,marginLeft:i[0].style.marginLeft,marginRight:i[0].style.marginRight}),i.css("margin",""),i.find("img").css("opacity",""),i.replaceWith(function(){return t(this).contents()}),t(document).off("mousedown.redactor-image-resize-hide."+this.uuid),void 0!==this.image.resizeHandle&&this.image.resizeHandle.el.attr("rel",this.image.resizeHandle.el.attr("style")))}},loadResizableControls:function(e,i){if(this.opts.imageResizable&&!this.detect.isMobile()){var r=t('<span id="redactor-image-resizer" data-redactor="verified"></span>');return this.detect.isDesktop()||r.css({width:"15px",height:"15px"}),r.attr("contenteditable",!1),i.append(r),i.append(e),r}return i.append(e),!1},loadEditableControls:function(e){if(0===t("#redactor-image-box").length){var i=t('<span id="redactor-image-box" data-redactor="verified">');if(i.css("float",e.css("float")).attr("contenteditable",!1),"auto"!=e[0].style.margin?(i.css({marginTop:e[0].style.marginTop,marginBottom:e[0].style.marginBottom,marginLeft:e[0].style.marginLeft,marginRight:e[0].style.marginRight}),e.css("margin","")):i.css({display:"block",margin:"auto"}),e.css("opacity",".5").after(i),this.opts.imageEditable){this.image.editter=t('<span id="redactor-image-editter" data-redactor="verified">'+this.lang.get("edit")+"</span>"),this.image.editter.attr("contenteditable",!1),this.image.editter.on("click",t.proxy(function(){this.image.showEdit(e)},this)),i.append(this.image.editter);var r=this.image.editter.innerWidth();this.image.editter.css("margin-left","-"+r/2+"px")}return this.image.loadResizableControls(e,i)}},showEdit:function(e){if(!this.events.imageEditing){this.observe.image=e;var i=e.closest("a",this.$editor[0]),r=e.closest("figure",this.$editor[0]),n=0!==r.length?r:e;if(this.modal.load("image-edit",this.lang.get("edit"),705),this.image.buttonDelete=this.modal.getDeleteButton().text(this.lang.get("delete")),this.image.buttonSave=this.modal.getActionButton().text(this.lang.get("save")),this.image.buttonDelete.on("click",t.proxy(this.image.remove,this)),this.image.buttonSave.on("click",t.proxy(this.image.update,this)),!1===this.opts.imageCaption)t("#redactor-image-caption").val("").hide().prev().hide();else{var s=e.closest(this.opts.imageTag,this.$editor[0]),o=s.find("figcaption");0!==o&&t("#redactor-image-caption").val(o.text()).show()}if(this.opts.imagePosition){var a=0!==r.length?"center"===n.css("text-align"):"block"==n.css("display")&&"none"==n.css("float"),l=a?"center":n.css("float");t("#redactor-image-align").val(l)}else t(".redactor-image-position-option").hide();t("#redactor-image-preview").html(t('<img src="'+e.attr("src")+'" style="max-width: 100%;">')),t("#redactor-image-title").val(e.attr("alt")),
+0!==i.length&&(t("#redactor-image-link").val(i.attr("href")),"_blank"===i.attr("target")&&t("#redactor-image-link-blank").prop("checked",!0)),t(".redactor-link-tooltip").remove(),this.modal.show(),this.detect.isDesktop()&&t("#redactor-image-title").focus()}},update:function(){var e=this.observe.image,i=e.closest("a",this.core.editor()[0]),r=t("#redactor-image-title").val().replace(/(<([^>]+)>)/gi,"");e.attr("alt",r).attr("title",r),this.image.setFloating(e);var n=t.trim(t("#redactor-image-link").val()).replace(/(<([^>]+)>)/gi,"");if(""!==n){var s="((xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,}",o=new RegExp("^(http|ftp|https)://"+s,"i"),a=new RegExp("^"+s,"i");-1===n.search(o)&&0===n.search(a)&&this.opts.linkProtocol&&(n=this.opts.linkProtocol+"://"+n);var l=!!t("#redactor-image-link-blank").prop("checked");if(0===i.length){var c=t('<a href="'+n+'" id="redactor-img-tmp">'+this.utils.getOuterHtml(e)+"</a>");l&&c.attr("target","_blank"),e=e.replaceWith(c),i=this.core.editor().find("#redactor-img-tmp"),i.removeAttr("id")}else i.attr("href",n),l?i.attr("target","_blank"):i.removeAttr("target")}else 0!==i.length&&i.replaceWith(this.utils.getOuterHtml(e));this.image.addCaption(e,i),this.modal.close(),this.buffer.set()},setFloating:function(e){var i=e.closest("figure",this.$editor[0]),r=0!==i.length?i:e,n=t("#redactor-image-align").val(),s="",o="",a="",l="";switch(n){case"left":s="left",a="0 "+this.opts.imageFloatMargin+" "+this.opts.imageFloatMargin+" 0";break;case"right":s="right",a="0 0 "+this.opts.imageFloatMargin+" "+this.opts.imageFloatMargin;break;case"center":0!==i.length?l="center":(o="block",a="auto")}r.css({float:s,display:o,margin:a,"text-align":l}),r.attr("rel",e.attr("style"))},addCaption:function(e,i){var r=t("#redactor-image-caption").val(),n=0!==i.length?i:e,s=n.next();0!==s.length&&"FIGCAPTION"===s[0].tagName||(s=!1),""!==r?!1===s?(s=t("<figcaption />").text(r),n.after(s)):s.text(r):!1!==s&&s.remove()},remove:function(e,i,r){i=void 0===i?t(this.observe.image):i,"boolean"!=typeof e&&this.buffer.set(),this.events.stopDetectChanges();var n=i.closest("a",this.core.editor()[0]),s=i.closest(this.opts.imageTag,this.core.editor()[0]);i.parent();if(!1===this.core.callback("imageDelete",e,i[0]))return e&&e.preventDefault(),!1;0!==t("#redactor-image-box").length&&t("#redactor-image-box").parent();var o,a;0!==s.length?(a=s.prev(),o=s.next(),s.remove()):0!==n.length?(n.parent(),n.remove()):i.remove(),t("#redactor-image-box").remove(),!1!==e&&(o&&0!==o.length?this.caret.start(o):a&&0!==a.length&&this.caret.end(a)),"boolean"!=typeof e&&this.modal.close(),this.utils.restoreScroll(),this.observe.image=!1,this.events.startDetectChanges(),this.code.sync()}}},indent:function(){return{increase:function(){if(this.list.get()){var e=t(this.selection.current()).closest("li"),i=e.closest("ul, ol",this.core.editor()[0]),r=e.closest("li"),n=r.prev();if(0!==n.length&&"LI"===n[0].tagName)if(this.buffer.set(),this.utils.isCollapsed()){var s=i[0].tagName,o=t("<"+s+" />");this.selection.save();var a=n.find("ol").first();if(1===a.length)a.append(e);else{var s=i[0].tagName,o=t("<"+s+" />");o.append(e),n.append(o)}this.selection.restore()}else document.execCommand("indent"),this.selection.save(),this.indent.removeEmpty(),this.indent.normalize(),this.selection.restore()}},decrease:function(){if(this.list.get()){var e=t(this.selection.current()).closest("li");e.closest("ul, ol",this.core.editor()[0]);this.buffer.set(),document.execCommand("outdent");var i=t(this.selection.current()).closest("li",this.core.editor()[0]);if(this.utils.isCollapsed()&&this.indent.repositionItem(i),0===i.length){document.execCommand("formatblock",!1,"p"),i=t(this.selection.current());var r=i.next();0!==r.length&&"BR"===r[0].tagName&&r.remove()}this.selection.save(),this.indent.removeEmpty(),this.indent.normalize(),this.selection.restore()}},repositionItem:function(t){var e=t.next();0===e.length||"UL"===e[0].tagName&&"OL"===e[0].tagName||t.append(e);var i=t.prev();if(0!==i.length&&"LI"!==i[0].tagName){this.selection.save();t.parents("li",this.core.editor()[0]).after(t),this.selection.restore()}},normalize:function(){this.core.editor().find("li").each(t.proxy(function(e,i){var r=t(i),n="";0!==this.opts.keepStyleAttr.length&&(n=","+this.opts.keepStyleAttr.join(",")),r.find(this.opts.inlineTags.join(",")).not("img"+n).removeAttr("style");var s=r.parent();if(0!==s.length&&"LI"===s[0].tagName)return void s.after(r);var o=r.next();0===o.length||"UL"!==o[0].tagName&&"OL"!==o[0].tagName||r.append(o)},this))},removeEmpty:function(e){var i=this.core.editor().find("ul, ol"),r=this.core.editor().find("li");r.each(t.proxy(function(t,e){this.indent.removeItemEmpty(e)},this)),i.each(t.proxy(function(t,e){this.indent.removeItemEmpty(e)},this)),r.each(t.proxy(function(t,e){this.indent.removeItemEmpty(e)},this))},removeItemEmpty:function(e){var i=e.innerHTML.replace(/[\t\s\n]/g,"");""===(i=i.replace(/<span><\/span>/g,""))&&t(e).remove()}}},inline:function(){return{format:function(t,e,i,r){if(!this.utils.isCurrentOrParent(["PRE","CODE"])){var n=this.inline.getParams(e,i,r);t=this.inline.arrangeTag(t),this.buffer.set(),this.utils.isCollapsed()?this.inline.formatCollapsed(t,n):this.inline.formatUncollapsed(t,n)}},formatCollapsed:function(e,i){var r,n=this.selection.inline();if(n){var s=n.tagName.toLowerCase();if(s===e)if(this.utils.isEmpty(n.innerHTML))this.caret.after(n),t(n).remove();else{var o=this.inline.insertBreakpoint(n,s);this.caret.after(o)}else if(0===t(n).closest(e).length)r=this.inline.insertInline(e),r=this.inline.setParams(r,i);else{var o=this.inline.insertBreakpoint(n,s);this.caret.after(o)}}else r=this.inline.insertInline(e),r=this.inline.setParams(r,i)},formatUncollapsed:function(e,i){this.selection.save();var r=this.inline.getClearedNodes();this.inline.setNodesStriked(r,e,i),this.selection.restore(),document.execCommand("strikethrough"),this.selection.saveInstant();for(var n,s,o=this.core.editor()[0].querySelectorAll('[style*="line-through"]'),a=0,l=o.length;a<l;a++)n=o[0],s=document.createElement("strike"),n.parentNode.insertBefore(s,n),s.appendChild(n),n.style.removeProperty("text-decoration");var c=this;this.core.editor().find("strike").each(function(){var r=c.utils.replaceToTag(this,e);c.inline.setParams(r[0],i);var n=r.find(e),s=r.parent(),o=s.parent();if(0!==o.length&&o[0].tagName.toLowerCase()===e&&o.html()==s[0].outerHTML)return r.replaceWith(function(){return t(this).contents()}),void o.replaceWith(function(){return t(this).contents()});0!==n.length&&c.inline.cleanInsideOrParent(n,i),s.html()==r[0].outerHTML&&c.inline.cleanInsideOrParent(s,i),c.detect.isFirefox()&&c.core.editor().find(e+":empty").remove()}),this.selection.restoreInstant()},cleanInsideOrParent:function(t,e){if(e)for(var i in e.data)this.inline.removeSpecificAttr(t,i,e.data[i])},getClearedNodes:function(){for(var e=this.selection.nodes(),i=[],r=e.length,n=0,s=0;s<r;s++)if(t(e[s]).hasClass("redactor-selection-marker")){n=s+2;break}for(var s=0;s<r;s++)s>=n&&!this.utils.isBlockTag(e[s].tagName)&&i.push(e[s]);return i},isConvertableAttr:function(e,i,r){var n=t(e).attr(i);if(n)if("style"===i){r=t.trim(r).replace(/;$/,"");for(var s=r.split(";"),o=0,a=0;a<s.length;a++){var l=s[a].split(":"),c=t.trim(l[0]),h=t.trim(l[1]);if(-1!==c.search(/color/)){var d=t(e).css(c);!d||d!==h&&this.utils.rgb2hex(d)!==h||o++}else t(e).css(c)===h&&o++}if(o===s.length)return 1}else if(n===r)return 1;return 0},isConvertable:function(t,e,i,r){if(e===i){if(!r)return!0;var n=0;for(var s in r.data)n+=this.inline.isConvertableAttr(t,s,r.data[s]);if(n===Object.keys(r.data).length)return!0}return!1},setNodesStriked:function(e,i,r){for(var n=0;n<e.length;n++){var s=e[n].tagName?e[n].tagName.toLowerCase():void 0,o=e[n].parentNode,a=o&&o.tagName?o.tagName.toLowerCase():void 0,l=this.inline.isConvertable(o,a,i,r);if(l){t(o).replaceWith(function(){return t("<strike>").append(t(this).contents())}).attr("data-redactor-inline-converted")}var l=this.inline.isConvertable(e[n],s,i,r);if(l){t(e[n]).replaceWith(function(){return t("<strike>").append(t(this).contents())})}}},insertBreakpoint:function(e,i){var r=document.createElement("span");r.id="redactor-inline-breakpoint",r=this.insert.node(r);var n=this.utils.isEndOfElement(e),s=this.utils.getOuterHtml(e),o=n?"":"<"+i+">";s=s.replace(/<span id="redactor-inline-breakpoint"><\/span>/i,"</"+i+">"+o);var a=t(s);return t(e).replaceWith(a),""!==o&&this.utils.cloneAttributes(e,a.last()),a.first()},insertInline:function(t){var e=document.createElement(t);return this.insert.node(e),this.caret.start(e),e},arrangeTag:function(t){var e=["b","bold","i","italic","underline","strikethrough","deleted","superscript","subscript"],i=["strong","strong","em","em","u","del","del","sup","sub"];t=t.toLowerCase();for(var r=0;r<e.length;r++)t===e[r]&&(t=i[r]);return t},getStyleParams:function(t){for(var e={},i=t.trim().replace(/;$/,"").split(";"),r=0;r<i.length;r++){var n=i[r].split(":");n&&(e[n[0].trim()]=n[1].trim())}return e},getParams:function(t,e,i){var r=!1,n="toggle";return"object"==typeof t?(r=t,n=void 0!==e?e:n):void 0!==t&&void 0!==e&&(r={},r[t]=e,n=void 0!==i?i:n),!!r&&{func:n,data:r}},setParams:function(e,i){if(i)for(var r in i.data){var n=t(e);"style"===r?(e=this.inline[i.func+"Style"](i.data[r],e),n.attr("data-redactor-style-cache",n.attr("style"))):e="class"===r?this.inline[i.func+"Class"](i.data[r],e):"remove"===i.func?this.inline[i.func+"Attr"](r,e):this.inline[i.func+"Attr"](r,i.data[r],e),"style"===r&&"SPAN"===e.tagName&&n.attr("data-redactor-span",!0)}return e},eachInline:function(t,e){var i,r=void 0===t?this.selection.inlines():[t];if(r)for(var n=0;n<r.length;n++)i=e(r[n])[0];return i},replaceClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).removeAttr("class").addClass(e)})},toggleClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).toggleClass(e)})},addClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).addClass(e)})},removeClass:function(e,i){return this.inline.eachInline(i,function(i){return t(i).removeClass(e)})},removeAllClass:function(e){return this.inline.eachInline(e,function(e){return t(e).removeAttr("class")})},replaceAttr:function(e,i,r){return this.inline.eachInline(r,function(i){return t(i).removeAttr(e).attr(e.value)})},toggleAttr:function(e,i,r){return this.inline.eachInline(r,function(i){return t(i).attr(e)?t(i).removeAttr(e):t(i).attr(e.value)})},addAttr:function(e,i,r){return this.inline.eachInline(r,function(r){return t(r).attr(e,i)})},removeAttr:function(e,i){return this.inline.eachInline(i,function(i){var r=t(i);return r.removeAttr(e),"style"===e&&r.removeAttr("data-redactor-style-cache"),r})},removeAllAttr:function(e){return this.inline.eachInline(e,function(e){for(var i=t(e),r=e.attributes.length,n=0;n<r;n++)i.removeAttr(e.attributes[n].name);return i})},removeSpecificAttr:function(e,i,r){var n=t(e);if("style"===i){var s=r.split(":"),o=s[0].trim();n.css(o,""),this.utils.removeEmptyAttr(e,"style")&&n.removeAttr("data-redactor-style-cache")}else n.removeAttr(i)[0]},hasParentStyle:function(t){var e=t.parent();return 1===e.length&&e[0].tagName===t[0].tagName&&e.html()===t[0].outerHTML&&e},addParentStyle:function(e){var i=this.inline.hasParentStyle(e);if(i){var r=this.inline.getStyleParams(e.attr("style"));i.css(r),i.attr("data-redactor-style-cache",i.attr("style")),e.replaceWith(function(){return t(this).contents()})}else e.attr("data-redactor-style-cache",e.attr("style"));return e},replaceStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var n=t(i);n.removeAttr("style").css(e);var s=n.attr("style");return s&&n.attr("style",s.replace(/"/g,"'")),n=r.inline.addParentStyle(n)})},toggleStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var n=t(i);for(var s in e){var o=e[s],a=n.css(s);a=r.utils.isRgb(a)?r.utils.rgb2hex(a):a.replace(/"/g,""),o=r.utils.isRgb(o)?r.utils.rgb2hex(o):o.replace(/"/g,""),a===o?n.css(s,""):n.css(s,o)}var l=n.attr("style");return l&&n.attr("style",l.replace(/"/g,"'")),r.utils.removeEmptyAttr(i,"style")?n.removeAttr("data-redactor-style-cache"):n=r.inline.addParentStyle(n),n})},addStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var n=t(i);n.css(e);var s=n.attr("style");return s&&n.attr("style",s.replace(/"/g,"'")),n=r.inline.addParentStyle(n)})},removeStyle:function(e,i){e=this.inline.getStyleParams(e);var r=this;return this.inline.eachInline(i,function(i){var n=t(i);for(var s in e)n.css(s,"");return r.utils.removeEmptyAttr(i,"style")?n.removeAttr("data-redactor-style-cache"):n.attr("data-redactor-style-cache",n.attr("style")),n})},removeAllStyle:function(e){return this.inline.eachInline(e,function(e){return t(e).removeAttr("style").removeAttr("data-redactor-style-cache")})},removeStyleRule:function(e){var i=this.selection.parent(),r=this.selection.inlines();this.buffer.set(),i&&"SPAN"===i.tagName&&this.inline.removeStyleRuleAttr(t(i),e);for(var n=0;n<r.length;n++){var s=r[n],o=t(s);-1==t.inArray(s.tagName.toLowerCase(),this.opts.inlineTags)||o.hasClass("redactor-selection-marker")||this.inline.removeStyleRuleAttr(o,e)}},removeStyleRuleAttr:function(t,e){t.css(e,""),this.utils.removeEmptyAttr(t,"style")?t.removeAttr("data-redactor-style-cache"):t.attr("data-redactor-style-cache",t.attr("style"))},update:function(t,e,i,r){t=this.inline.arrangeTag(t);var n=this.inline.getParams(e,i,r),s=this.selection.inlines(),o=[];if(s)for(var a=0;a<s.length;a++){var l=s[a];"*"!==t&&l.tagName.toLowerCase()!==t||o.push(this.inline.setParams(l,n))}return o},removeFormat:function(){this.selection.save();for(var e=this.inline.getClearedNodes(),i=0;i<e.length;i++)1===e[i].nodeType&&t(e[i]).replaceWith(function(){return t(this).contents()});this.selection.restore()}}},insert:function(){return{set:function(t){this.code.set(t),this.focus.end()},html:function(e,i){this.core.editor().focus();var r=this.selection.block(),n=this.selection.inline();void 0===i&&(i=this.clean.getCurrentType(e,!0),e=this.clean.onPaste(e,i,!0)),e=t.parseHTML(e);var s=t(e).last(),o=this.selection.get(),a=this.selection.range(o);if(a.deleteContents(),this.selection.update(o,a),i.lists){var l=t(e);if(0!==l.length&&("UL"===l[0].tagName||"OL"===l[0].tagName))return void this.insert.appendLists(r,l)}if(i.blocks&&r)if(this.utils.isSelectAll())this.core.editor().html(e),this.focus.end();else{var c=this.utils.breakBlockTag();if(!1===c)this.insert.placeHtml(e);else{var h=t(e).children().last();h.append(this.marker.get()),"start"===c.type?c.$block.before(e):c.$block.after(e),this.selection.restore(),this.core.editor().find("p").each(function(){""===t.trim(this.innerHTML)&&t(this).remove()})}}else{if(n){var d=t("<div/>").html(e);d.find(n.tagName.toLowerCase()).each(function(){t(this).contents().unwrap()}),e=d.html(),e=t.parseHTML(e),s=t(e).last()}if(this.utils.isSelectAll()){var u=t(this.opts.emptyHtml);this.core.editor().html("").append(u),u.html(e),this.caret.end(u)}else this.insert.placeHtml(e)}this.utils.disableSelectAll(),i.pre&&this.clean.cleanPre(),this.caret.end(s)},text:function(e){e=e.toString(),e=t.trim(e);var i=document.createElement("div");if(i.innerHTML=e,void 0!==(e=i.textContent||i.innerText)){this.core.editor().focus();var r=this.selection.blocks();if(e=e.replace(/\n/g," "),this.utils.isSelectAll()){var n=t(this.opts.emptyHtml);this.core.editor().html("").append(n),n.html(e),this.caret.end(n)}else{var s=this.selection.get(),o=document.createTextNode(e);if(s.getRangeAt&&s.rangeCount){var a=s.getRangeAt(0);a.deleteContents(),a.insertNode(o),a.setStartAfter(o),a.collapse(!0),this.selection.update(s,a)}r.length>1&&(t(o).wrap("<p>"),this.caret.after(o))}this.utils.disableSelectAll(),this.clean.normalizeCurrentHeading()}},raw:function(t){this.core.editor().focus();var e=this.selection.get(),i=this.selection.range(e);i.deleteContents();var r=document.createElement("div");r.innerHTML=t;for(var n,s,o=document.createDocumentFragment();n=r.firstChild;)s=o.appendChild(n);i.insertNode(o),s&&(i=i.cloneRange(),i.setStartAfter(s),i.collapse(!0),e.removeAllRanges(),e.addRange(i))},node:function(e,i){void 0!==this.start&&this.core.editor().focus(),e=e[0]||e;var r=this.selection.block(),n=this.utils.isBlockTag(e.tagName),s=!0;if(this.utils.isSelectAll())n?this.core.editor().html(e):this.core.editor().html(t("<p>").html(e)),this.code.sync();else if(n&&r){var o=this.utils.breakBlockTag();!1===o?this.insert.placeNode(e,i):("start"===o.type?o.$block.before(e):o.$block.after(e),this.core.editor().find("p:empty").remove())}else s=this.insert.placeNode(e,i);return this.utils.disableSelectAll(),s&&this.caret.end(e),e},appendLists:function(e,i){var r,n=t(e),s=this.utils.isEmpty(e.innerHTML);if(s||this.utils.isEndOfElement(e))r=n,i.find("li").each(function(){r.after(this),r=t(this)}),s&&n.remove();else if(this.utils.isStartOfElement(e))i.find("li").each(function(){n.before(this),r=t(this)});else{var o=this.selection.extractEndOfNode(e);n.after(t("<li>").append(o)),n.append(i),r=i}this.marker.remove(),r&&this.caret.end(r)},placeHtml:function(e){var i=document.createElement("span");i.id="redactor-insert-marker",i=this.insert.node(i),t(i).before(e),this.selection.restore(),this.caret.after(i),t(i).remove()},placeNode:function(t,e){var i=this.selection.get(),r=this.selection.range(i);if(null==r)return!1;!1!==e&&r.deleteContents(),r.insertNode(t),r.collapse(!1),this.selection.update(i,r)},nodeToPoint:function(e,i){if(i=i[0]||i,this.utils.isEmpty())return i=this.utils.isBlock(i)?i:t("<p />").append(i),this.core.editor().html(i),i;var r,n=e.clientX,s=e.clientY;if(document.caretPositionFromPoint){var o=document.caretPositionFromPoint(n,s);r=document.getSelection().getRangeAt(0),r.setStart(o.offsetNode,o.offset),r.collapse(!0),r.insertNode(i)}else if(document.caretRangeFromPoint)r=document.caretRangeFromPoint(n,s),r.insertNode(i);else if(void 0!==document.body.createTextRange){r=document.body.createTextRange(),r.moveToPoint(n,s);var a=r.duplicate();a.moveToPoint(n,s),r.setEndPoint("EndToEnd",a),r.select()}return i},nodeToCaretPositionFromPoint:function(t,e){this.insert.nodeToPoint(t,e)},marker:function(){this.marker.insert()}}},keydown:function(){return{init:function(e){if(!this.rtePaste){var i=e.which,r=i>=37&&i<=40;this.keydown.ctrl=e.ctrlKey||e.metaKey,this.keydown.parent=this.selection.parent(),this.keydown.current=this.selection.current(),this.keydown.block=this.selection.block(),this.keydown.pre=this.utils.isTag(this.keydown.current,"pre"),this.keydown.blockquote=this.utils.isTag(this.keydown.current,"blockquote"),this.keydown.figcaption=this.utils.isTag(this.keydown.current,"figcaption"),this.keydown.figure=this.utils.isTag(this.keydown.current,"figure");if(!1===this.core.callback("keydown",e))return e.preventDefault(),!1;if(this.shortcuts.init(e,i),this.keydown.checkEvents(r,i),this.keydown.setupBuffer(e,i),this.utils.isSelectAll()&&(i===this.keyCode.ENTER||i===this.keyCode.BACKSPACE||i===this.keyCode.DELETE))return e.preventDefault(),this.code.set(this.opts.emptyHtml),void this.events.changeHandler();if(this.keydown.addArrowsEvent(r),this.keydown.setupSelectAll(e,i),!this.opts.enterKey&&i===this.keyCode.ENTER){e.preventDefault();var n=this.selection.get(),s=this.selection.range(n);return void(s.collapsed||s.deleteContents())}if(this.opts.enterKey&&i===this.keyCode.DOWN&&this.keydown.onArrowDown(),this.opts.enterKey&&i===this.keyCode.UP&&this.keydown.onArrowUp(),("textarea"===this.opts.type||"div"===this.opts.type)&&this.keydown.current&&3===this.keydown.current.nodeType&&t(this.keydown.parent).hasClass("redactor-in")&&this.keydown.wrapToParagraph(),!this.keyup.lastShiftKey&&i===this.keyCode.SPACE&&(e.ctrlKey||e.shiftKey))return e.preventDefault(),this.keydown.onShiftSpace();if(i===this.keyCode.ENTER&&(e.ctrlKey||e.shiftKey))return e.preventDefault(),this.keydown.onShiftEnter(e);if(i===this.keyCode.ENTER&&!e.shiftKey&&!e.ctrlKey&&!e.metaKey)return this.keydown.onEnter(e);if(i===this.keyCode.TAB||e.metaKey&&221===i||e.metaKey&&219===i)return this.keydown.onTab(e,i);if(this.detect.isFirefox()&&i===this.keyCode.BACKSPACE&&this.keydown.block&&"P"===this.keydown.block.tagName&&this.utils.isStartOfElement(this.keydown.block)){var o=t(this.keydown.block).prev();if(0!==o.length)return e.preventDefault(),o.append(this.marker.get()),o.append(t(this.keydown.block).html()),t(this.keydown.block).remove(),void this.selection.restore()}if(i===this.keyCode.BACKSPACE||i===this.keyCode.DELETE){if(this.observe.image&&void 0!==this.observe.image&&0!==t("#redactor-image-box").length){e.preventDefault();var o=this.observe.image.closest("figure, p").prev();return this.image.remove(!1),this.observe.image=!1,void(o&&0!==o.length?this.caret.end(o):this.core.editor().focus())}this.keydown.onBackspaceAndDeleteBefore()}if(i===this.keyCode.DELETE){var a=t(this.keydown.block).next();if(this.utils.isEndOfElement(this.keydown.block)&&0!==a.length&&"FIGURE"===a[0].tagName)return a.remove(),!1;if(!(!this.keydown.block||"LI"!==this.keydown.block.tagName)&&this.keydown.block){var l=t(this.keydown.block).parents("ul, ol").last(),c=l.next();if(this.utils.isRedactorParent(l)&&this.utils.isEndOfElement(l)&&0!==c.length&&("UL"===c[0].tagName||"OL"===c[0].tagName))return e.preventDefault(),l.append(c.contents()),c.remove(),!1}if(this.utils.isEndOfElement(this.keydown.block)&&0!==a.length&&"PRE"===a[0].tagName)return t(this.keydown.block).append(a.text()),a.remove(),!1}if(i===this.keyCode.DELETE&&0!==t("#redactor-image-box").length&&this.image.remove(),i===this.keyCode.BACKSPACE){if(this.detect.isFirefox()&&this.line.removeOnBackspace(e),this.list.combineAfterAndBefore(this.keydown.block))return void e.preventDefault();var h=this.selection.block();if(h&&"LI"===h.tagName&&this.utils.isCollapsed()&&this.utils.isStartOfElement())return this.indent.decrease(),void e.preventDefault();this.keydown.removeInvisibleSpace(),this.keydown.removeEmptyListInTable(e)}i!==this.keyCode.BACKSPACE&&i!==this.keyCode.DELETE||this.keydown.onBackspaceAndDeleteAfter(e)}},onShiftSpace:function(){return this.buffer.set(),this.insert.raw(" "),!1},onShiftEnter:function(t){return this.buffer.set(),this.keydown.pre?this.keydown.insertNewLine(t):this.insert.raw("<br>")},onBackspaceAndDeleteBefore:function(){this.utils.saveScroll()},onBackspaceAndDeleteAfter:function(e){setTimeout(t.proxy(function(){this.code.syncFire=!1,this.keydown.removeEmptyLists();var t="";0!==this.opts.keepStyleAttr.length&&(t=","+this.opts.keepStyleAttr.join(",")),this.core.editor().find("*[style]").not("img, figure, iframe, #redactor-image-box, #redactor-image-editter, [data-redactor-style-cache], [data-redactor-span]"+t).removeAttr("style"),this.keydown.formatEmpty(e),this.code.syncFire=!0},this),1)},onEnter:function(e){if(!1===this.core.callback("enter",e))return e.preventDefault(),!1;if(this.keydown.blockquote&&!0===this.keydown.exitFromBlockquote(e))return!1;if(this.keydown.pre)return this.keydown.insertNewLine(e);if(this.keydown.blockquote||this.keydown.figcaption)return this.keydown.insertBreakLine(e);if(this.keydown.figure)setTimeout(t.proxy(function(){this.keydown.replaceToParagraph("FIGURE")},this),1);else if(this.keydown.block){if(setTimeout(t.proxy(function(){this.keydown.replaceToParagraph("DIV")},this),1),"LI"===this.keydown.block.tagName){var i=this.selection.current(),r=t(i).closest("li",this.$editor[0]),n=r.parents("ul,ol",this.$editor[0]).last();if(0!==r.length&&this.utils.isEmpty(r.html())&&0===n.next().length&&this.utils.isEmpty(n.find("li").last().html())){n.find("li").last().remove();var s=t(this.opts.emptyHtml);return n.after(s),this.caret.start(s),!1}}}else if(!this.keydown.block)return this.keydown.insertParagraph(e);if(this.detect.isFirefox()&&this.utils.isInline(this.keydown.parent))return void this.keydown.insertBreakLine(e);this.opts.keepInlineOnEnter||setTimeout(t.proxy(function(){var e=this.selection.inline();if(e&&this.utils.isEmpty(e.innerHTML)){var i=this.selection.block();t(e).remove();var r=document.createRange();r.setStart(i,0);var n=document.createTextNode("");r.insertNode(n),r.setStartAfter(n),r.collapse(!0);var s=window.getSelection();s.removeAllRanges(),s.addRange(r)}},this),1)},checkEvents:function(t,e){t||"click"!==this.core.getEvent()&&"arrow"!==this.core.getEvent()||(this.core.addEvent(!1),this.keydown.checkKeyEvents(e)&&this.buffer.set())},checkKeyEvents:function(e){var i=this.keyCode,r=[i.BACKSPACE,i.DELETE,i.ENTER,i.ESC,i.TAB,i.CTRL,i.META,i.ALT,i.SHIFT];return-1===t.inArray(e,r)},addArrowsEvent:function(t){if(t)return"click"===this.core.getEvent()||"arrow"===this.core.getEvent()?void this.core.addEvent(!1):void this.core.addEvent("arrow")},setupBuffer:function(t,e){return this.keydown.ctrl&&90===e&&!t.shiftKey&&!t.altKey&&this.sBuffer.length?(t.preventDefault(),void this.buffer.undo()):this.keydown.ctrl&&90===e&&t.shiftKey&&!t.altKey&&0!==this.sRebuffer.length?(t.preventDefault(),void this.buffer.redo()):void(this.keydown.ctrl||e!==this.keyCode.SPACE&&e!==this.keyCode.BACKSPACE&&e!==this.keyCode.DELETE&&(e!==this.keyCode.ENTER||t.ctrlKey||t.shiftKey)||this.buffer.set())},exitFromBlockquote:function(e){if(this.utils.isEndOfElement(this.keydown.blockquote)){if(-1!==this.clean.removeSpacesHard(t(this.keydown.blockquote).html()).search(/(<br\s?\/?>){1}$/i)){e.preventDefault();t(this.keydown.blockquote).children().last().filter("br").remove(),t(this.keydown.blockquote).children().last().filter("span").remove();var i=t(this.opts.emptyHtml);return t(this.keydown.blockquote).after(i),this.caret.start(i),!0}}},onArrowDown:function(){for(var t=[this.keydown.blockquote,this.keydown.pre,this.keydown.figcaption],e=0;e<t.length;e++)if(t[e])return this.keydown.insertAfterLastElement(t[e]),!1},onArrowUp:function(){for(var t=[this.keydown.blockquote,this.keydown.pre,this.keydown.figcaption],e=0;e<t.length;e++)if(t[e])return this.keydown.insertBeforeFirstElement(t[e]),!1},insertAfterLastElement:function(e){if(this.utils.isEndOfElement(e)){var i=this.core.editor().contents().last();if(0===("FIGCAPTION"===e.tagName?t(this.keydown.block).parent().next():t(this.keydown.block).next()).length){if(0===i.length&&i[0]!==e)return void this.caret.start(i);var r=t(this.opts.emptyHtml);"FIGCAPTION"===e.tagName?t(e).parent().after(r):t(e).after(r),this.caret.start(r)}}},insertBeforeFirstElement:function(e){if(this.utils.isStartOfElement()&&!(this.core.editor().contents().length>1&&this.core.editor().contents().first()[0]!==e)){var i=t(this.opts.emptyHtml);t(e).before(i),this.caret.start(i)}},onTab:function(t,e){if(!this.opts.tabKey)return!0;var i=this.keydown.block&&"LI"===this.keydown.block.tagName;if(this.utils.isEmpty(this.code.get())||!i&&!this.keydown.pre&&!1===this.opts.tabAsSpaces)return!0;t.preventDefault(),this.buffer.set();var r,n=i&&this.utils.isStartOfElement(this.keydown.block);return this.keydown.pre&&!t.shiftKey?(r=this.opts.preSpaces?document.createTextNode(Array(this.opts.preSpaces+1).join(" ")):document.createTextNode("\t"),this.insert.node(r)):!1===this.opts.tabAsSpaces||n?t.metaKey&&219===e?this.indent.decrease():t.metaKey&&221===e?this.indent.increase():t.shiftKey?this.indent.decrease():this.indent.increase():(r=document.createTextNode(Array(this.opts.tabAsSpaces+1).join(" ")),this.insert.node(r)),!1},setupSelectAll:function(t,e){this.keydown.ctrl&&65===e?this.utils.enableSelectAll():e===this.keyCode.LEFT_WIN||this.keydown.ctrl||this.utils.disableSelectAll()},insertNewLine:function(t){t.preventDefault();var e=document.createTextNode("\n"),i=this.selection.get(),r=this.selection.range(i);return r.deleteContents(),r.insertNode(e),this.caret.after(e),!1},insertParagraph:function(t){t.preventDefault();var e=document.createElement("p");e.innerHTML="<br>";var i=this.selection.get(),r=this.selection.range(i);return r.deleteContents(),r.insertNode(e),this.caret.start(e),!1},insertBreakLine:function(t){return this.keydown.insertBreakLineProcessing(t)},insertDblBreakLine:function(t){return this.keydown.insertBreakLineProcessing(t,!0)},insertBreakLineProcessing:function(t,e){t.stopPropagation();var i=document.createElement("br");if(this.insert.node(i),!0===e){var r=document.createElement("br");this.insert.node(r),this.caret.after(r)}else this.caret.after(i);return!1},wrapToParagraph:function(){var e=t(this.keydown.current),i=t("<p>").append(e.clone());e.replaceWith(i);var r=t(i).next();void 0!==r[0]&&"BR"===r[0].tagName&&r.remove(),this.caret.end(i)},replaceToParagraph:function(e){var i=this.selection.block(),r=t(i).prev(),n=i.innerHTML.replace(/<br\s?\/?>/gi,"");if(i.tagName===e&&this.utils.isEmpty(n)&&!t(i).hasClass("redactor-in")){var s=document.createElement("p");return t(i).replaceWith(s),this.keydown.setCaretToParagraph(s),!1}if("P"===i.tagName)return t(i).removeAttr("class").removeAttr("style"),this.detect.isIe()&&this.utils.isEmpty(n)&&this.utils.isInline(this.keydown.parent)&&t(i).on("input",t.proxy(function(){var e=this.selection.parent();if(this.utils.isInline(e)){var r=t(e).html();t(i).html(r),this.caret.end(i)}t(i).off("keyup")},this)),!1;if(r.hasClass(this.opts.videoContainerClass)){r.removeAttr("class");var s=document.createElement("p");return r.replaceWith(s),this.keydown.setCaretToParagraph(s),!1}},setCaretToParagraph:function(t){var e=document.createRange();e.setStart(t,0);var i=document.createTextNode("");e.insertNode(i),e.setStartAfter(i),e.collapse(!0);var r=window.getSelection();r.removeAllRanges(),r.addRange(e)},removeInvisibleSpace:function(){var e=t(this.keydown.current);0===e.text().search(/^\u200B$/g)&&e.remove()},removeEmptyListInTable:function(e){var i=t(this.keydown.current),r=t(this.keydown.parent),n=i.closest("td",this.$editor[0]);if(0!==n.length&&i.closest("li",this.$editor[0])&&1===r.children("li").length){if(!this.utils.isEmpty(i.text()))return;e.preventDefault(),i.remove(),r.remove(),this.caret.start(n)}},removeEmptyLists:function(){var e=function(){""===t.trim(this.innerHTML).replace(/\/t\/n/g,"")&&t(this).remove()};this.core.editor().find("li").each(e),this.core.editor().find("ul, ol").each(e)},formatEmpty:function(e){var i=t.trim(this.core.editor().html());if(this.utils.isEmpty(i))return e.preventDefault(),"inline"===this.opts.type||"pre"===this.opts.type?(this.core.editor().html(this.marker.html()),this.selection.restore()):(this.core.editor().html(this.opts.emptyHtml),this.focus.start()),!1}}},keyup:function(){return{init:function(e){if(!this.rtePaste){var i=e.which;this.keyup.block=this.selection.block(),this.keyup.current=this.selection.current(),this.keyup.parent=this.selection.parent(),this.keyup.lastShiftKey=e.shiftKey;if(!1===this.core.callback("keyup",e))return e.preventDefault(),!1;if(i===this.keyCode.ENTER&&this.keyup.block&&"FIGURE"===this.keyup.block.tagName){var r=t(this.keyup.block).prev();if(0!==r.length&&"FIGURE"===r[0].tagName){var n=this.utils.replaceToTag(r,"p");return void this.caret.start(n)}}if(i===this.keyCode.BACKSPACE||i===this.keyCode.DELETE){if(this.utils.isSelectAll())return void this.focus.start();if(this.keyup.block&&this.keydown.block&&"FIGURE"===this.keyup.block.tagName&&this.utils.isStartOfElement(this.keydown.block)){e.preventDefault(),this.selection.save(),t(this.keyup.block).find("figcaption").remove(),t(this.keyup.block).find("img").first().remove(),this.utils.replaceToTag(this.keyup.block,"p");var s=this.marker.find();return t("html, body").animate({scrollTop:s.position().top+20},500),void this.selection.restore()}if(this.keyup.block&&"P"===this.keyup.block.tagName){var o=t(this.keyup.block).find("img").length;""===t(this.keyup.block).text().replace(/\u200B/g,"")&&0!==o&&this.utils.replaceToTag(this.keyup.block,"figure")}this.keyup.block&&"FIGURE"===this.keyup.block.tagName&&0===t(this.keyup.block).find("img").length&&(this.selection.save(),this.utils.replaceToTag(this.keyup.block,"p"),this.selection.restore())}}}}},lang:function(){return{
+load:function(){this.opts.curLang=this.opts.langs[this.opts.lang]},get:function(t){return void 0!==this.opts.curLang[t]?this.opts.curLang[t]:""}}},line:function(){return{insert:function(){this.buffer.set(),this.insert.html(this.line.getLineHtml());var t=this.core.editor().find("#redactor-hr-tmp-id");return t.removeAttr("id"),this.core.callback("insertedLine",t),t},getLineHtml:function(){var t='<hr id="redactor-hr-tmp-id" />';return!this.detect.isFirefox()&&this.utils.isEmpty()&&(t+="<p>"+this.opts.emptyHtml+"</p>"),t},removeOnBackspace:function(e){if(this.utils.isCollapsed()){var i=t(this.selection.block());if(0!==i.length&&this.utils.isStartOfElement(i)){var r=i.prev();r&&0!==r.length&&"HR"===r[0].tagName&&(e.preventDefault(),r.remove())}}}}},link:function(){return{get:function(){return t(this.selection.inlines("a"))},is:function(){var e=this.selection.nodes(),i=t(this.selection.current()).closest("a",this.core.editor()[0]);return!(0===i.length||e.length>1)&&i},unlink:function(t){void 0!==t&&t.preventDefault&&t.preventDefault(),this.buffer.set();var e=this.selection.inlines("a");if(0!==e.length){var i=this.link.replaceLinksToText(e);this.observe.closeAllTooltip(),this.core.callback("deletedLink",i)}},insert:function(e,i){var r=this.link.is();if(!0!==i&&!1===(e=this.link.buildLinkFromObject(r,e)))return!1;if(this.buffer.set(),e=this.core.callback("beforeInsertingLink",e),!1===r){r=t("<a />"),r=this.link.update(r,e),r=t(this.insert.node(r));var n=r.parent();!1===this.utils.isRedactorParent(n)&&r.wrap("<p>"),n.hasClass("redactor-unlink")&&n.replaceWith(function(){return t(this).contents()}),this.caret.after(r),this.core.callback("insertedLink",r)}else r=this.link.update(r,e),this.caret.after(r);return r},update:function(t,e){return t.text(e.text),t.attr("href",e.url),this.link.target(t,e.target),t},target:function(t,e){return e?t.attr("target","_blank"):t.removeAttr("target")},show:function(e){void 0!==e&&e.preventDefault&&e.preventDefault(),this.observe.closeAllTooltip();var i=this.link.is();this.link.buildModal(i);var r=this.link.buildLinkFromElement(i);r.url=this.link.removeSelfHostFromUrl(r.url),this.opts.linkNewTab&&!i&&(r.target=!0),this.link.setModalValues(r),this.modal.show(),this.detect.isDesktop()&&t("#redactor-link-url").focus()},setModalValues:function(e){t("#redactor-link-blank").prop("checked",e.target),t("#redactor-link-url").val(e.url),t("#redactor-link-url-text").val(e.text)},buildModal:function(e){this.modal.load("link",this.lang.get(!1===e?"link-insert":"link-edit"),600),this.modal.getActionButton().text(this.lang.get(!1===e?"insert":"save")).on("click",t.proxy(this.link.callback,this))},callback:function(){var t=this.link.buildLinkFromModal();if(!1===t)return!1;this.modal.close(),this.link.insert(t,!0)},cleanUrl:function(e){return void 0===e?"":t.trim(e.replace(/[^\W\w\D\d+&\'@#\/%?=~_|!:,.;\(\)]/gi,""))},cleanText:function(e){return void 0===e?"":t.trim(e.replace(/(<([^>]+)>)/gi,""))},getText:function(t){return""===t.text&&""!==t.url?this.link.truncateUrl(t.url.replace(/<|>/g,"")):t.text},isUrl:function(t){return!!new RegExp("^((https?|ftp):\\/\\/)?(([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$","i").test(t)&&t},isMailto:function(t){return-1!==t.search("@")&&!1===/(http|ftp|https):\/\//i.test(t)},isEmpty:function(t){return""===t.url||""===t.text&&""===t.url},truncateUrl:function(t){return t.length>this.opts.linkSize?t.substring(0,this.opts.linkSize)+"...":t},parse:function(t){return this.link.isMailto(t.url)?t.url="mailto:"+t.url.replace("mailto:",""):0!==t.url.search("#")&&this.opts.linkValidation&&(t.url=this.link.isUrl(t.url)?"http://"+t.url.replace(/(ftp|https?):\/\//gi,""):t.url),!this.link.isEmpty(t)&&!1!==t.url&&t},buildLinkFromModal:function(){var e={};return e.url=this.link.cleanUrl(t("#redactor-link-url").val()),e.text=this.link.cleanText(t("#redactor-link-url-text").val()),e.text=this.link.getText(e),e.target=!!t("#redactor-link-blank").prop("checked"),this.link.parse(e)},buildLinkFromObject:function(t,e){return e.url=this.link.cleanUrl(e.url),e.text=void 0===e.text&&this.selection.is()?this.selection.text():this.link.cleanText(e.text),e.text=this.link.getText(e),e.target=!1===t?e.target:this.link.buildTarget(t),this.link.parse(e)},buildLinkFromElement:function(t){var e={url:"",text:this.selection.is()?this.selection.text():"",target:!1};return!1!==t&&(e.url=t.attr("href"),e.text=t.text(),e.target=this.link.buildTarget(t)),e},buildTarget:function(t){return void 0!==t.attr("target")&&"_blank"===t.attr("target")},removeSelfHostFromUrl:function(t){var e=self.location.href.replace("#","").replace(/\/$/i,"");return t.replace(/^\/\#/,"#").replace(e,"").replace("mailto:","")},replaceLinksToText:function(e){var i,r=t.each(e,function(e,r){var n=t(r),s=t('<span class="redactor-unlink" />').append(n.contents());return n.replaceWith(s),0===e&&(i=s),n});return 1===e.length&&this.selection.isCollapsed()&&this.caret.after(i),r}}},linkify:function(){return{isKey:function(){},isLink:function(){},isFiltered:function(){},handler:function(){},format:function(){},convertVideoLinks:function(){},convertImages:function(){},convertLinks:function(){}}},list:function(){return{toggle:function(e){if(!this.utils.inBlocks(["table","td","th","tr"])){e="orderedlist"===e?"ol":e,e="unorderedlist"===e?"ul":e,e=e.toLowerCase(),this.buffer.set(),this.selection.save();var i=this.list._getBlocks(),r=this.selection.block(),n=t(r).parents("ul, ol").last();return 0===i.length&&0!==n.length&&(i=[n.get(0)]),i=this.list._isUnformat(e,i)?this.list._unformat(e,i):this.list._format(e,i),this.selection.restore(),i}},get:function(){var e=this.selection.current(),i=t(e).closest("ul, ol",this.core.editor()[0]);return 0!==i.length&&i},combineAfterAndBefore:function(e){var i=t(e).prev(),r=t(e).next(),n=e&&"P"===e.tagName&&("<br>"===e.innerHTML||""===e.innerHTML),s=1===i.closest("ol, ul",this.core.editor()[0]).length&&1===r.closest("ol, ul",this.core.editor()[0]).length;return!(!n||!s)&&(i.children("li").last().append(this.marker.get()),i.append(r.contents()),this.selection.restore(),!0)},_getBlocks:function(){for(var e=[],i=this.selection.blocks(),r=0;r<i.length;r++){t(i[r]).parent().hasClass("redactor-in")&&e.push(i[r])}return e},_isUnformat:function(t,e){for(var i=0,r=0;r<e.length;r++)if(3!==e[r].nodeType){var n=e[r].tagName.toLowerCase();n!==t&&"figure"!==n||i++}return i===e.length},_uniteBlocks:function(e,i){for(var r=0,n={0:[]},s=!1,o=0;o<e.length;o++){var a=t(e[o]),l=a.closest("th, td");0!==l.length?(l.get(0)!==s&&(r++,n[r]=[]),this.list._isUniteBlock(e[o],i)&&n[r].push(e[o])):this.list._isUniteBlock(e[o],i)?n[r].push(e[o]):(r++,n[r]=[]),s=l.get()}return n},_isUniteBlock:function(t,e){return 3===t.nodeType||-1!==e.indexOf(t.tagName.toLowerCase())},_createList:function(e,i,r){var n=i[i.length-1],s=t(n),o=t("<"+e+">");return s.after(o),o},_createListItem:function(e){var i=t("<li>");if(3===e.nodeType)i.append(e);else{var r=t(e);i.append(r.contents()),r.remove()}return i},_format:function(e,i){var r=["p","div","blockquote","pre","h1","h2","h3","h4","h5","h6","ul","ol"],n=this.list._uniteBlocks(i,r),s=[];for(var o in n){for(var a=n[o],l=this.list._createList(e,n[o]),c=0;c<a.length;c++){var h;3===a[c].nodeType||"UL"!==a[c].tagName&&"OL"!==a[c].tagName?(h=this.list._createListItem(a[c]),l.append(h)):(h=t(a[c]).contents(),l.append(h))}s.push(l.get(0))}return s},_unformat:function(e,i){if(1===i.length){var r=t(i[0]),n=r.find("li"),s=this.selection.blocks(["li"]),o=this.selection.block(),a=t(o).closest("li");if(0===s.length&&0!==a.length&&(s=[a.get(0)]),s.length===n.length)return this.list._unformatEntire(i[0]);var l=this.list._getItemsPosition(n,s);if("Top"===l)return this.list._unformatAtSide("before",s,r);if("Bottom"===l)return s.reverse(),this.list._unformatAtSide("after",s,r);if("Middle"===l){var c=t(s[s.length-1]),h=!1,d=!1,u=t("<"+r.get(0).tagName.toLowerCase()+">");n.each(function(e,i){if(h){var r=t(i);r.children("ul, ol").length;0!==r.closest(".redactor-split-item").length||!1!==d&&0!==r.closest(d).length||r.addClass("redactor-split-item"),d=r}i===c.get(0)&&(h=!0)}),n.filter(".redactor-split-item").each(function(e,i){t(i).removeClass("redactor-split-item"),u.append(i)}),r.after(u),s.reverse();for(var p=0;p<s.length;p++){var f=t(s[p]),g=this.list._createUnformatContainer(f);r.after(g),g.find("ul, ol").remove(),f.remove()}return}}else for(var p=0;p<i.length;p++)3!==i[p].nodeType&&i[p].tagName.toLowerCase()===e&&this.list._unformatEntire(i[p])},_unformatEntire:function(e){var i=t(e);i.find("li").each(function(e,r){var n=t(r),s=this.list._createUnformatContainer(n);n.remove(),i.before(s)}.bind(this)),i.remove()},_unformatAtSide:function(e,i,r){for(var n=0;n<i.length;n++){var s=t(i[n]),o=this.list._createUnformatContainer(s);r[e](o);var a=o.find("ul, ol").first();s.append(a),a.each(function(e,r){var n=t(r),s=n.closest("li");s.get(0)===i[e]&&(n.unwrap(),s.addClass("r-unwrapped"))}),this.utils.isEmpty(s.html())&&s.remove()}r.find(".r-unwrapped").each(function(e){var i=t(e);""===i.html().trim()?i.remove():i.removeClass("r-unwrapped")})},_getItemsPosition:function(t,e){var i="Middle",r=e[0],n=e[e.length-1],s=t.first().get(0),o=t.last().get(0);return s===r&&o!==n?i="Top":s!==r&&o===n&&(i="Bottom"),i},_createUnformatContainer:function(e){var i=t("<p>");return i.append(e.contents()),i}}},marker:function(){return{get:function(t){t=void 0===t?1:t;var e=document.createElement("span");return e.id="selection-marker-"+t,e.className="redactor-selection-marker",e.innerHTML=this.opts.invisibleSpace,e},html:function(t){return this.utils.getOuterHtml(this.marker.get(t))},find:function(t){return t=void 0===t?1:t,this.core.editor().find("span#selection-marker-"+t)},insert:function(){var t=this.selection.get(),e=this.selection.range(t);this.marker.insertNode(e,this.marker.get(1),!0),e&&!1===e.collapsed&&this.marker.insertNode(e,this.marker.get(2),!1)},remove:function(){this.core.editor().find(".redactor-selection-marker").each(this.marker.iterateRemove)},insertNode:function(e,i,r){var n=this.selection.parent();if(null!==e&&0!==t(n).closest(".redactor-in").length){e=e.cloneRange();try{e.collapse(r),e.insertNode(i)}catch(t){this.focus.start()}}},iterateRemove:function(e,i){var r=t(i),n=r.text().replace(/\u200B/g,"");r.parent()[0];""===n?r.remove():r.replaceWith(function(){return t(this).contents()})}}},modal:function(){return{callbacks:{},templates:function(){this.opts.modal={"image-edit":"",image:"",file:"",link:String()+'<div class="redactor-modal-tab" data-title="General"><section><label>URL</label><input type="url" id="redactor-link-url" aria-label="URL" /></section><section><label>'+this.lang.get("text")+'</label><input type="text" id="redactor-link-url-text" aria-label="'+this.lang.get("text")+'" /></section><section><label class="checkbox"><input type="checkbox" id="redactor-link-blank"> '+this.lang.get("link-in-new-tab")+'</label></section><section><button id="redactor-modal-button-action">'+this.lang.get("insert")+'</button><button id="redactor-modal-button-cancel">'+this.lang.get("cancel")+"</button></section></div>"},t.extend(this.opts,this.opts.modal)},addCallback:function(t,e){this.modal.callbacks[t]=e},addTemplate:function(t,e){this.opts.modal[t]=e},getTemplate:function(t){return this.opts.modal[t]},getModal:function(){return this.$modalBody},getActionButton:function(){return this.$modalBody.find("#redactor-modal-button-action")},getCancelButton:function(){return this.$modalBody.find("#redactor-modal-button-cancel")},getDeleteButton:function(){return this.$modalBody.find("#redactor-modal-button-delete")},load:function(){},show:function(){},buildWidth:function(){},buildTabber:function(){},showTab:function(){},setTitle:function(){},setContent:function(){this.$modalBody.html(this.modal.getTemplate(this.modal.templateName)),this.modal.getCancelButton().on("mousedown",t.proxy(this.modal.close,this))},setDraggable:function(){},setEnter:function(){},build:function(){this.modal.buildOverlay(),this.$modalBox=t('<div id="redactor-modal-box"/>').hide(),this.$modal=t('<div id="redactor-modal" role="dialog" />'),this.$modalHeader=t('<div id="redactor-modal-header" />'),this.$modalClose=t('<button type="button" id="redactor-modal-close" aria-label="'+this.lang.get("close")+'" />').html("×"),this.$modalBody=t('<div id="redactor-modal-body" />'),this.$modal.append(this.$modalHeader),this.$modal.append(this.$modalBody),this.$modal.append(this.$modalClose),this.$modalBox.append(this.$modal),this.$modalBox.appendTo(document.body)},buildOverlay:function(){this.$modalOverlay=t('<div id="redactor-modal-overlay">').hide(),t("body").prepend(this.$modalOverlay)},enableEvents:function(){},disableEvents:function(){},closeHandler:function(){},close:function(){}}},observe:function(){return{load:function(){void 0===this.opts.destroyed&&(this.observe.links(),this.observe.images())},isCurrent:function(e,i){return void 0===i&&(i=t(this.selection.current())),i.is(e)||i.parents(e).length>0},toolbar:function(){this.observe.buttons(),this.observe.dropdowns()},buttons:function(e,i){var r=this.selection.current(),n=this.selection.parent();if(!1!==e?this.button.setInactiveAll():this.button.setInactiveAll(i),!1===e&&"html"!==i)return void(-1!==t.inArray(i,this.opts.activeButtons)&&this.button.toggleActive(i));this.utils.isRedactorParent(r)&&("none"!==this.core.editor().css("display")&&(this.utils.isCurrentOrParentHeader()||this.utils.isCurrentOrParent(["table","pre","blockquote","li"])?this.button.disable("horizontalrule"):this.button.enable("horizontalrule")),t.each(this.opts.activeButtonsStates,t.proxy(function(e,i){var s=t(n).closest(e,this.$editor[0]),o=t(r).closest(e,this.$editor[0]);(0===s.length||this.utils.isRedactorParent(s))&&this.utils.isRedactorParent(o)&&(0===s.length&&0===o.closest(e,this.$editor[0]).length||this.button.setActive(i))},this)))},dropdowns:function(){var e=t("<div />").html(this.selection.html()).find("a").length,i=t(this.selection.current()),r=this.utils.isRedactorParent(i);t.each(this.opts.observe.dropdowns,t.proxy(function(t,n){var s=n.observe,o=s.element,a=n.item,l=void 0!==s.in&&s.in,c=void 0!==s.out&&s.out;i.closest(o).length>0&&r||"a"===o&&0!==e?this.observe.setDropdownProperties(a,l,c):this.observe.setDropdownProperties(a,c,l)},this))},setDropdownProperties:function(t,e,i){i&&void 0!==i.attr&&this.observe.setDropdownAttr(t,i.attr,!0),void 0!==e.attr&&this.observe.setDropdownAttr(t,e.attr),void 0!==e.title&&t.find("span").text(e.title)},setDropdownAttr:function(e,i,r){t.each(i,function(t,i){"class"===t?r?e.removeClass(i):e.addClass(i):r?e.removeAttr(t):e.attr(t,i)})},addDropdown:function(t,e,i){void 0!==i.observe&&(i.item=t,this.opts.observe.dropdowns.push(i))},images:function(){this.opts.imageEditable&&(this.core.editor().addClass("redactor-layer-img-edit"),this.core.editor().find("img").each(t.proxy(function(e,i){var r=t(i);r.closest("a",this.$editor[0]).on("click",function(t){t.preventDefault()}),this.image.setEditable(r)},this)))},links:function(){this.opts.linkTooltip&&this.core.editor().find("a").each(t.proxy(function(e,i){var r=t(i);!0!==r.data("cached")&&(r.data("cached",!0),r.on("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.showTooltip,this)))},this))},getTooltipPosition:function(t){return t.offset()},showTooltip:function(e){var i=t(e.target);if("IMG"!==i[0].tagName&&("A"!==i[0].tagName&&(i=i.closest("a",this.$editor[0])),"A"===i[0].tagName)){var r=i,n=this.observe.getTooltipPosition(r),s=t('<span class="redactor-link-tooltip"></span>'),o=r.attr("href");void 0===o&&(o=""),o.length>24&&(o=o.substring(0,24)+"...");var a=t('<a href="'+r.attr("href")+'" target="_blank" />').html(o).addClass("redactor-link-tooltip-action"),l=t('<a href="#" />').html(this.lang.get("edit")).on("click",t.proxy(this.link.show,this)).addClass("redactor-link-tooltip-action"),c=t('<a href="#" />').html(this.lang.get("unlink")).on("click",t.proxy(this.link.unlink,this)).addClass("redactor-link-tooltip-action");s.append(a).append(" | ").append(l).append(" | ").append(c);var h=parseInt(r.css("line-height"),10),d=Math.ceil((e.pageY-n.top)/h),u=n.top+d*h;s.css({top:u+"px",left:n.left+"px"}),t(".redactor-link-tooltip").remove(),t("body").append(s),this.core.editor().on("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this)),t(document).on("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this))}},closeAllTooltip:function(){t(".redactor-link-tooltip").remove()},closeTooltip:function(e){e=e.originalEvent||e;var i=e.target,r=t(i).closest("a",this.$editor[0]);0!==r.length&&"A"===r[0].tagName&&"A"!==i.tagName||"A"===i.tagName&&this.utils.isRedactorParent(i)||t(i).hasClass("redactor-link-tooltip-action")||(this.observe.closeAllTooltip(),this.core.editor().off("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this)),t(document).off("touchstart.redactor."+this.uuid+" click.redactor."+this.uuid,t.proxy(this.observe.closeTooltip,this)))}}},offset:function(){return{get:function(e){var i=this.offset.clone(e);if(!1===i)return 0;var r=document.createElement("div");return r.appendChild(i.cloneContents()),r.innerHTML=r.innerHTML.replace(/<img(.*?[^>])>$/gi,"i"),t.trim(t(r).text()).replace(/[\t\n\r\n]/g,"").replace(/\u200B/g,"").length},clone:function(t){var e=this.selection.get(),i=this.selection.range(e);if(null===i&&void 0===t)return!1;if(!1===(t=void 0===t?this.$editor:t))return!1;t=t[0]||t;var r=i.cloneRange();return r.selectNodeContents(t),r.setEnd(i.endContainer,i.endOffset),r},set:function(t,e){e=void 0===e?t:e,this.focus.is()||this.focus.start();for(var i,r=this.selection.get(),n=this.selection.range(r),s=0,o=document.createTreeWalker(this.$editor[0],NodeFilter.SHOW_TEXT,null,null);null!==(i=o.nextNode());)if(s+=i.nodeValue.length,s>t&&(n.setStart(i,i.nodeValue.length+t-s),t=1/0),s>=e){n.setEnd(i,i.nodeValue.length+e-s);break}n.collapse(!1),this.selection.update(r,n)}}},paragraphize:function(){return{load:function(e){return!1===this.opts.paragraphize||"inline"===this.opts.type||"pre"===this.opts.type?e:""===e||"<p></p>"===e?this.opts.emptyHtml:(e+="\n",this.paragraphize.safes=[],this.paragraphize.z=0,e=e.replace(/(<br\s?\/?>){1,}\n?<\/blockquote>/gi,"</blockquote>"),e=e.replace(/<\/pre>/gi,"</pre>\n\n"),e=e.replace(/<p>\s<br><\/p>/gi,"<p></p>"),e=this.paragraphize.getSafes(e),e=e.replace("<br>","\n"),e=this.paragraphize.convert(e),e=this.paragraphize.clear(e),e=this.paragraphize.restoreSafes(e),e=e.replace(new RegExp("<br\\s?/?>\n?<("+this.opts.paragraphizeBlocks.join("|")+")(.*?[^>])>","gi"),"<p><br /></p>\n<$1$2>"),t.trim(e))},getSafes:function(e){var i=t("<div />").append(e);return i.find("blockquote p").replaceWith(function(){return t(this).append("<br />").contents()}),i.find(this.opts.paragraphizeBlocks.join(", ")).each(t.proxy(function(e,i){return this.paragraphize.z++,this.paragraphize.safes[this.paragraphize.z]=i.outerHTML,t(i).replaceWith("\n#####replace"+this.paragraphize.z+"#####\n\n")},this)),i.find("span.redactor-selection-marker").each(t.proxy(function(e,i){return this.paragraphize.z++,this.paragraphize.safes[this.paragraphize.z]=i.outerHTML,t(i).replaceWith("\n#####replace"+this.paragraphize.z+"#####\n\n")},this)),i.html()},restoreSafes:function(e){return t.each(this.paragraphize.safes,function(t,i){i=void 0!==i?i.replace(/\$/g,"$"):i,e=e.replace("#####replace"+t+"#####",i)}),e},convert:function(e){e=e.replace(/\r\n/g,"xparagraphmarkerz"),e=e.replace(/\n/g,"xparagraphmarkerz"),e=e.replace(/\r/g,"xparagraphmarkerz");var i=/\s+/g;e=e.replace(i," "),e=t.trim(e);var r=/xparagraphmarkerzxparagraphmarkerz/gi;e=e.replace(r,"</p><p>");var n=/xparagraphmarkerz/gi;return e=e.replace(n,"<br>"),e="<p>"+e+"</p>",e=e.replace("<p></p>",""),e=e.replace("\r\n\r\n",""),e=e.replace(/<\/p><p>/g,"</p>\r\n\r\n<p>"),e=e.replace(new RegExp("<br\\s?/?></p>","g"),"</p>"),e=e.replace(new RegExp("<p><br\\s?/?>","g"),"<p>"),e=e.replace(new RegExp("<p><br\\s?/?>","g"),"<p>"),e=e.replace(new RegExp("<br\\s?/?></p>","g"),"</p>"),e=e.replace(/<p> <\/p>/gi,""),e=e.replace(/<p>\s?<br> <\/p>/gi,""),e=e.replace(/<p>\s?<br>/gi,"<p>")},clear:function(t){return t=t.replace(/<p>(.*?)#####replace(.*?)#####\s?<\/p>/gi,"<p>$1</p>#####replace$2#####"),t=t.replace(/(<br\s?\/?>){2,}<\/p>/gi,"</p>"),t=t.replace(new RegExp("</blockquote></p>","gi"),"</blockquote>"),t=t.replace(new RegExp("<p></blockquote>","gi"),"</blockquote>"),t=t.replace(new RegExp("<p><blockquote>","gi"),"<blockquote>"),t=t.replace(new RegExp("<blockquote></p>","gi"),"<blockquote>"),t=t.replace(new RegExp("<p><p ","gi"),"<p "),t=t.replace(new RegExp("<p><p>","gi"),"<p>"),t=t.replace(new RegExp("</p></p>","gi"),"</p>"),t=t.replace(new RegExp("<p>\\s?</p>","gi"),""),t=t.replace(new RegExp("\n</p>","gi"),"</p>"),t=t.replace(new RegExp("<p>\t?\t?\n?<p>","gi"),"<p>"),t=t.replace(new RegExp("<p>\t*</p>","gi"),"")}}},paste:function(){return{init:function(e){this.rtePaste=!0;var i=!("pre"!==this.opts.type&&!this.utils.isCurrentOrParent("pre"));if(this.detect.isDesktop()&&!this.paste.pre&&this.opts.clipboardImageUpload&&this.opts.imageUpload&&this.paste.detectClipboardUpload(e))return void(this.detect.isIe()&&setTimeout(t.proxy(this.paste.clipboardUpload,this),100));this.utils.saveScroll(),this.selection.save(),this.paste.createPasteBox(i),t(window).on("scroll.redactor-freeze",t.proxy(function(){t(window).scrollTop(this.saveBodyScroll)},this)),setTimeout(t.proxy(function(){var e=this.paste.getPasteBoxCode(i);this.buffer.set(),this.selection.restore(),this.utils.restoreScroll();var r=this.clean.getCurrentType(e);e=this.clean.onPaste(e,r);var n=this.core.callback("paste",e);e=void 0===n?e:n,this.paste.insert(e,r),this.rtePaste=!1,i&&this.clean.cleanPre(),t(window).off("scroll.redactor-freeze")},this),1)},getPasteBoxCode:function(t){var e=t?this.$pasteBox.val():this.$pasteBox.html();return this.$pasteBox.remove(),e},createPasteBox:function(e){var i={position:"fixed",width:"1px",top:0,left:"-9999px"};this.$pasteBox=e?t("<textarea>").css(i):t("<div>").attr("contenteditable","true").css(i),this.paste.appendPasteBox(),this.$pasteBox.focus()},appendPasteBox:function(){if(this.detect.isIe())this.core.box().append(this.$pasteBox);else{var e=t(".modal-body:visible");e.length>0?e.append(this.$pasteBox):t("body").prepend(this.$pasteBox)}},detectClipboardUpload:function(e){e=e.originalEvent||e;var i=e.clipboardData;if(this.detect.isIe()||this.detect.isFirefox())return!1;if(-1!==i.types.indexOf("public.tiff"))return e.preventDefault(),!1;if(i.items&&i.items.length){var r=i.items[0].getAsFile();if(null===r)return!1;var n=new FileReader;return n.readAsDataURL(r),n.onload=t.proxy(this.paste.insertFromClipboard,this),!0}},clipboardUpload:function(){var e=this.$editor.find("img");t.each(e,t.proxy(function(e,i){if(-1!==i.src.search(/^data\:image/i)){var r=window.FormData?new FormData:null;if(window.FormData){this.upload.direct=!0,this.upload.type="image",this.upload.url=this.opts.imageUpload,this.upload.callback=t.proxy(function(e){if(this.detect.isIe())t(i).wrap(t("<figure />"));else{var r=t(i).parent();this.utils.replaceToTag(r,"figure")}i.src=e.url,this.core.callback("imageUpload",t(i),e)},this);var n=this.utils.dataURItoBlob(i.src);r.append("clipboard",1),r.append(this.opts.imageUploadParam,n),this.upload.send(r,!1),this.code.sync(),this.rtePaste=!1}}},this))},insertFromClipboard:function(t){var e=window.FormData?new FormData:null;if(window.FormData){this.upload.direct=!0,this.upload.type="image",this.upload.url=this.opts.imageUpload,this.upload.callback=this.image.insert;var i=this.utils.dataURItoBlob(t.target.result);e.append("clipboard",1),e.append(this.opts.imageUploadParam,i),this.upload.send(e,t),this.rtePaste=!1}},insert:function(e,i){i.pre?this.insert.raw(e):i.text?this.insert.text(e):this.insert.html(e,i),this.detect.isFirefox()&&this.opts.imageUpload&&this.opts.clipboardImageUpload&&setTimeout(t.proxy(this.paste.clipboardUpload,this),100)}}},placeholder:function(){return{enable:function(){},show:function(){},update:function(){},hide:function(){},is:function(){},init:function(){},enabled:function(){},enableEvents:function(){},disableEvents:function(){},build:function(){},buildPosition:function(){},getPosition:function(){},isEditorEmpty:function(){},isAttr:function(){},destroy:function(){}}},progress:function(){return{$box:null,$bar:null,target:document.body,show:function(){},hide:function(){},update:function(){},is:function(){},build:function(){},destroy:function(){}}},selection:function(){return{get:function(){return window.getSelection?window.getSelection():document.selection&&"Control"!==document.selection.type?document.selection:null},range:function(t){return void 0===t&&(t=this.selection.get()),t.getRangeAt&&t.rangeCount?t.getRangeAt(0):null},is:function(){return!this.selection.isCollapsed()},isRedactor:function(){var e=this.selection.range();if(null!==e){var i=e.startContainer.parentNode;if(t(i).hasClass("redactor-in")||0!==t(i).parents(".redactor-in").length)return!0}return!1},isCollapsed:function(){var t=this.selection.get();return null!==t&&t.isCollapsed},update:function(t,e){null!==e&&(t.removeAllRanges(),t.addRange(e))},current:function(){var t=this.selection.get();return null!==t&&t.anchorNode},parent:function(){var t=this.selection.current();return null!==t&&t.parentNode},block:function(e){for(e=e||this.selection.current();e;){if(this.utils.isBlockTag(e.tagName))return!t(e).hasClass("redactor-in")&&e;e=e.parentNode}return!1},inline:function(e){for(e=e||this.selection.current();e;){if(this.utils.isInlineTag(e.tagName))return!t(e).hasClass("redactor-in")&&e;e=e.parentNode}return!1},element:function(e){for(e||(e=this.selection.current());e;){if(1===e.nodeType)return!t(e).hasClass("redactor-in")&&e;e=e.parentNode}return!1},prev:function(){return null!==this.selection.current()&&this.selection.current().previousSibling},next:function(){return null!==this.selection.current()&&this.selection.current().nextSibling},blocks:function(e){var i=[],r=this.selection.nodes(e);t.each(r,t.proxy(function(t,e){this.utils.isBlock(e)&&i.push(e)},this));var n=this.selection.block();return 0===i.length&&!1===n?[]:0===i.length&&!1!==n?[n]:i},inlines:function(e){var i=[],r=this.selection.nodes(e);t.each(r,t.proxy(function(t,e){this.utils.isInline(e)&&i.push(e)},this));var n=this.selection.inline();return 0===i.length&&!1===n?[]:0===i.length&&!1!==n?[n]:i},nodes:function(e){var i=void 0===e?[]:t.isArray(e)?e:[e],r=this.selection.get(),n=this.selection.range(r),s=[],o=[];if(this.utils.isCollapsed())s=[this.selection.current()];else{var a=n.startContainer,l=n.endContainer;if(a===l)return[a];for(;a&&a!==l;)s.push(a=this.selection.nextNode(a));for(a=n.startContainer;a&&a!==n.commonAncestorContainer;)s.unshift(a),a=a.parentNode}return t.each(s,function(e,r){if(r){var n=1===r.nodeType&&r.tagName.toLowerCase();if(t(r).hasClass("redactor-script-tag")||t(r).hasClass("redactor-selection-marker"))return;if(n&&0!==i.length&&-1===t.inArray(n,i))return;o.push(r)}}),0===o.length?[]:o},nextNode:function(t){if(t.hasChildNodes())return t.firstChild;for(;t&&!t.nextSibling;)t=t.parentNode;return t?t.nextSibling:null},save:function(){this.marker.insert(),this.savedSel=this.core.editor().html()},restore:function(t){var e=this.marker.find(1),i=this.marker.find(2);this.detect.isFirefox()&&this.core.editor().focus(),0!==e.length&&0!==i.length?this.caret.set(e,i):0!==e.length?this.caret.start(e):this.core.editor().focus(),!1!==t&&(this.marker.remove(),this.savedSel=!1)},saveInstant:function(){var t=this.core.editor()[0],e=t.ownerDocument,i=e.defaultView,r=i.getSelection();if(r.getRangeAt&&r.rangeCount){var n=r.getRangeAt(0),s=n.cloneRange();s.selectNodeContents(t),s.setEnd(n.startContainer,n.startOffset);var o=s.toString().length;return this.saved={start:o,end:o+n.toString().length,node:n.startContainer},this.saved}},restoreInstant:function(t){if(void 0!==t||this.saved){this.saved=void 0!==t?t:this.saved;var e=this.core.editor().find(this.saved.node);if(0===e.length||0!==e.text().trim().replace(/\u200B/g,"").length){var i=this.core.editor()[0],r=i.ownerDocument,n=r.defaultView,s=0,o=r.createRange();o.setStart(i,0),o.collapse(!0);for(var a,l=[i],c=!1,h=!1;!h&&(a=l.pop());)if(3==a.nodeType){var d=s+a.length;!c&&this.saved.start>=s&&this.saved.start<=d&&(o.setStart(a,this.saved.start-s),c=!0),c&&this.saved.end>=s&&this.saved.end<=d&&(o.setEnd(a,this.saved.end-s),h=!0),s=d}else for(var u=a.childNodes.length;u--;)l.push(a.childNodes[u]);var p=n.getSelection();p.removeAllRanges(),p.addRange(o)}else try{var o=document.createRange();o.setStart(e[0],0);var p=window.getSelection();p.removeAllRanges(),p.addRange(o)}catch(t){}}},node:function(e){t(e).prepend(this.marker.get(1)),t(e).append(this.marker.get(2)),this.selection.restore()},all:function(){this.core.editor().focus();var t=this.selection.get(),e=this.selection.range(t);e.selectNodeContents(this.core.editor()[0]),this.selection.update(t,e)},remove:function(){this.selection.get().removeAllRanges()},replace:function(t){this.insert.html(t)},text:function(){return this.selection.get().toString()},html:function(){var t="",e=this.selection.get();if(e.rangeCount){for(var i=document.createElement("div"),r=e.rangeCount,n=0;n<r;++n)i.appendChild(e.getRangeAt(n).cloneContents());t=this.clean.onGet(i.innerHTML)}return t},extractEndOfNode:function(t){var e=this.selection.get(),i=this.selection.range(e),r=i.cloneRange();return r.selectNodeContents(t),r.setStart(i.endContainer,i.endOffset),r.extractContents()},removeMarkers:function(){this.marker.remove()},marker:function(t){return this.marker.get(t)},markerHtml:function(t){return this.marker.html(t)}}},shortcuts:function(){return{hotkeysSpecialKeys:{8:"backspace",9:"tab",10:"return",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",59:";",61:"=",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},hotkeysShiftNums:{"`":"~",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(",0:")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"},init:function(e,i){if(!1===this.opts.shortcuts)return!e.ctrlKey&&!e.metaKey||66!==i&&73!==i||e.preventDefault(),!1;t.each(this.opts.shortcuts,t.proxy(function(t,i){this.shortcuts.build(e,t,i)},this))},build:function(e,i,r){for(var n=t.proxy(function(){this.shortcuts.buildHandler(r)},this),s=i.split(","),o=s.length,a=0;a<o;a++)"string"==typeof s[a]&&this.shortcuts.handler(e,t.trim(s[a]),n)},buildHandler:function(t){var e;"-1"!==t.func.search(/\./)?(e=t.func.split("."),void 0!==this[e[0]]&&this[e[0]][e[1]].apply(this,t.params)):this[t.func].apply(this,t.params)},handler:function(e,i,r){i=i.toLowerCase().split(" ");var n=this.shortcuts.hotkeysSpecialKeys[e.keyCode],s=String.fromCharCode(e.which).toLowerCase(),o="",a={};t.each(["alt","ctrl","meta","shift"],function(t,i){e[i+"Key"]&&n!==i&&(o+=i+"+")}),n&&(a[o+n]=!0),s&&(a[o+s]=!0,a[o+this.shortcuts.hotkeysShiftNums[s]]=!0,"shift+"===o&&(a[this.shortcuts.hotkeysShiftNums[s]]=!0));for(var l=i.length,c=0;c<l;c++)if(a[i[c]])return e.preventDefault(),r.apply(this,arguments)}}},storage:function(){return{data:[],add:function(){},status:function(){},observe:function(){},changes:function(){}}},toolbar:function(){return{build:function(){this.button.hideButtons(),this.button.hideButtonsOnMobile(),this.$toolbarBox=t('<div class="redactor-toolbar-box" />'),
+this.$toolbarBox[0].innerHTML='<ul class="redactor-toolbar" id="redactor-toolbar-'+this.uuid+'" role="toolbar"></ul>',this.$toolbar=t(this.$toolbarBox[0].children[0]),this.$box[0].insertBefore(this.$toolbarBox[0],this.$box[0].firstChild),this.button.$toolbar=this.$toolbar,this.button.setFormatting(),this.button.load(this.$toolbar),require(["Core"],function(t){this.$toolbar[0].addEventListener("keydown",this.toolbar.keydown.bind(this,t))}.bind(this))},createContainer:function(){},append:function(){},setOverflow:function(){},setFixed:function(){},setUnfixed:function(){},getBoxTop:function(){},observeScroll:function(){},observeScrollResize:function(){},observeScrollEnable:function(){},observeScrollDisable:function(){},setDropdownsFixed:function(){},unsetDropdownsFixed:function(){},setDropdownPosition:function(){},keydown:function(e,i){var r=document.activeElement;if(r.classList.contains("re-button")){if(-1!==[13,32,35,36,37,39,40].indexOf(i.which)){if(13===i.which||32===i.which)return i.preventDefault(),void require(["Core"],function(t){t.triggerEvent(r,"mousedown")});if(40===i.which){if("true"!==elAttr(r,"aria-haspopup"))return;i.preventDefault(),e.triggerEvent(r,"mousedown");var n=t(r).data("dropdown"),s=elBySel("li",n[0]);return void(s&&s.focus())}i.preventDefault();var o=Array.prototype.slice.call(elBySelAll(".re-button",this.$toolbar[0])),a=null;if(35===i.which)a=o[o.length-1];else if(36===i.which)a=o[0];else{var l=o.indexOf(r);37===i.which?-1===--l&&(l=o.length-1):39===i.which&&++l===o.length&&(l=0),a=o[l]}null!==a&&a.focus()}}}}},upload:function(){return{init:function(){},directUpload:function(){},onDrop:function(){},traverseFile:function(){},setConfig:function(){},getType:function(){},getHiddenFields:function(){},send:function(){},onDrag:function(){},onDragLeave:function(){},clearImageFields:function(){},addImageFields:function(){},removeImageFields:function(){},clearFileFields:function(){},addFileFields:function(){},removeFileFields:function(){}}},uploads3:function(){return{send:function(){},executeOnSignedUrl:function(){},createCORSRequest:function(){},sendToS3:function(){}}},utils:function(){return{isEmpty:function(e){return e=void 0===e?this.core.editor().html():e,e=e.replace(/[\u200B-\u200D\uFEFF]/g,""),e=e.replace(/ /gi,""),e=e.replace(/<\/?br\s?\/?>/g,""),e=e.replace(/\s/g,""),e=e.replace(/^<p>[^\W\w\D\d]*?<\/p>$/i,""),e=e.replace(/<iframe(.*?[^>])>$/i,"iframe"),e=e.replace(/<source(.*?[^>])>$/i,"source"),e=e.replace(/<[^\/>][^>]*><\/[^>]+>/gi,""),e=e.replace(/<[^\/>][^>]*><\/[^>]+>/gi,""),""===(e=t.trim(e))},isElement:function(t){try{return t instanceof HTMLElement}catch(e){return"object"==typeof t&&1===t.nodeType&&"object"==typeof t.style&&"object"==typeof t.ownerDocument}},strpos:function(t,e,i){var r=t.indexOf(e,i);return r>=0&&r},dataURItoBlob:function(t){var e;e=t.split(",")[0].indexOf("base64")>=0?atob(t.split(",")[1]):unescape(t.split(",")[1]);for(var i=t.split(",")[0].split(":")[1].split(";")[0],r=new Uint8Array(e.length),n=0;n<e.length;n++)r[n]=e.charCodeAt(n);return new Blob([r],{type:i})},getOuterHtml:function(e){return t("<div>").append(t(e).eq(0).clone()).html()},cloneAttributes:function(e,i){e=e[0]||e,i=t(i);for(var r=e.attributes,n=r.length;n--;){var s=r[n];i.attr(s.name,s.value)}return i},breakBlockTag:function(){var e=this.selection.block();if(!e)return!1;var i=this.utils.isEmpty(e.innerHTML),r=e.tagName.toLowerCase();if("pre"===r||"li"===r||"td"===r||"th"===r)return!1;if(!i&&this.utils.isStartOfElement(e))return{$block:t(e),$next:t(e).next(),type:"start"};if(!i&&this.utils.isEndOfElement(e))return{$block:t(e),$next:t(e).next(),type:"end"};var n=this.selection.extractEndOfNode(e),s=t("<"+r+" />").append(n);return s=this.utils.cloneAttributes(e,s),t(e).after(s),{$block:t(e),$next:s,type:"break"}},inBlocks:function(e){e=t.isArray(e)?e:[e];for(var i=this.selection.blocks(),r=i.length,n=!1,s=0;s<r;s++)if(!1!==i[s]){var o=i[s].tagName.toLowerCase();-1!==t.inArray(o,e)&&(n=!0)}return n},inInlines:function(e){e=t.isArray(e)?e:[e];for(var i=this.selection.inlines(),r=i.length,n=!1,s=0;s<r;s++){var o=i[s].tagName.toLowerCase();-1!==t.inArray(o,e)&&(n=!0)}return n},isTag:function(e,i){var r=t(e).closest(i,this.core.editor()[0]);return 1===r.length&&r[0]},isBlock:function(t){return null!==t&&((t=t[0]||t)&&this.utils.isBlockTag(t.tagName))},isBlockTag:function(t){return void 0!==t&&this.reIsBlock.test(t)},isInline:function(t){return(t=t[0]||t)&&this.utils.isInlineTag(t.tagName)},isInlineTag:function(t){return void 0!==t&&this.reIsInline.test(t)},isRedactorParent:function(e){return!!e&&(0!==t(e).parents(".redactor-in").length&&!t(e).hasClass("redactor-in")&&e)},isCurrentOrParentHeader:function(){return this.utils.isCurrentOrParent(["H1","H2","H3","H4","H5","H6"])},isCurrentOrParent:function(e){var i=this.selection.parent(),r=this.selection.current();if(t.isArray(e)){var n=0;return t.each(e,t.proxy(function(t,e){this.utils.isCurrentOrParentOne(r,i,e)&&n++},this)),0!==n}return this.utils.isCurrentOrParentOne(r,i,e)},isCurrentOrParentOne:function(t,e,i){return i=i.toUpperCase(),e&&e.tagName===i?e:!(!t||t.tagName!==i)&&t},isEditorRelative:function(){var e=this.core.editor().css("position"),i=["absolute","fixed","relative"];return-1!==t.inArray(i,e)},setEditorRelative:function(){this.core.editor().addClass("redactor-relative")},getScrollTarget:function(){var e=t(this.opts.scrollTarget);return 0!==e.length?e:t(document)},freezeScroll:function(){this.freezeScrollTop=this.utils.getScrollTarget().scrollTop(),this.utils.getScrollTarget().scrollTop(this.freezeScrollTop)},unfreezeScroll:function(){void 0!==this.freezeScrollTop&&this.utils.getScrollTarget().scrollTop(this.freezeScrollTop)},saveScroll:function(){this.tmpScrollTop=this.utils.getScrollTarget().scrollTop()},restoreScroll:function(){void 0!==this.tmpScrollTop&&this.utils.getScrollTarget().scrollTop(this.tmpScrollTop)},isStartOfElement:function(t){return!(void 0===t&&!(t=this.selection.block()))&&0===this.offset.get(t)},isEndOfElement:function(e){if(void 0===e&&!(e=this.selection.block()))return!1;var i=t.trim(t(e).text()).replace(/[\t\n\r\n]/g,"").replace(/\u200B/g,"");return this.offset.get(e)===i.length},removeEmptyAttr:function(e,i){var r=t(e);return void 0===r.attr(i)||""===r.attr(i)&&(r.removeAttr(i),!0)},replaceToTag:function(e,i){var r;return t(e).replaceWith(function(){r=t("<"+i+" />").append(t(this).contents());for(var e=0;e<this.attributes.length;e++)r.attr(this.attributes[e].name,this.attributes[e].value);return r}),r},isSelectAll:function(){return this.selectAll},enableSelectAll:function(){this.selectAll=!0},disableSelectAll:function(){this.selectAll=!1},disableBodyScroll:function(){},measureScrollbar:function(){var e=t("body"),i=document.createElement("div");i.className="redactor-scrollbar-measure",e.append(i);var r=i.offsetWidth-i.clientWidth;return e[0].removeChild(i),r},enableBodyScroll:function(){},appendFields:function(e,i){if(!e)return i;if("object"==typeof e)return t.each(e,function(e,r){null!==r&&0===r.toString().indexOf("#")&&(r=t(r).val()),i.append(e,r)}),i;var r=t(e);if(0===r.length)return i;return r.each(function(){i.append(t(this).attr("name"),t(this).val())}),i},appendForms:function(e,i){if(!e)return i;var r=t(e);if(0===r.length)return i;var n=r.serializeArray();return t.each(n,function(t,e){i.append(e.name,e.value)}),i},isRgb:function(t){return 0===t.search(/^rgb/i)},rgb2hex:function(t){return t=t.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i),t&&4===t.length?"#"+("0"+parseInt(t[1],10).toString(16)).slice(-2)+("0"+parseInt(t[2],10).toString(16)).slice(-2)+("0"+parseInt(t[3],10).toString(16)).slice(-2):""},isCollapsed:function(){return this.selection.isCollapsed()},isMobile:function(){return this.detect.isMobile()},isDesktop:function(){return this.detect.isDesktop()},isPad:function(){return this.detect.isIpad()}}},browser:function(){return{webkit:function(){return this.detect.isWebkit()},ff:function(){return this.detect.isFirefox()},ie:function(){return this.detect.isIe()}}}},t(window).on("load.tools.redactor",function(){t('[data-tools="redactor"]').redactor()}),e.prototype.init.prototype=e.prototype}(jQuery),function(t){function e(e,i,r,n){var s={duration:.5,iterate:1,delay:0,prefix:"redactor-",timing:"linear"};this.animation=i,this.slide="slideDown"===this.animation||"slideUp"===this.animation,this.$element=t(e),this.prefixes=["","-moz-","-o-animation-","-webkit-"],this.queue=[],"function"==typeof r?(n=r,this.opts=s):this.opts=t.extend(s,r),this.slide&&this.$element.height(this.$element.height()),this.init(n)}t.fn.redactorAnimation=function(t,i,r){return this.each(function(){new e(this,t,i,r)})},e.prototype={init:function(t){this.queue.push(this.animation),this.clean(),"show"===this.animation?(this.opts.timing="linear",this.$element.removeClass("hide").show(),"function"==typeof t&&t(this)):"hide"===this.animation?(this.opts.timing="linear",this.$element.hide(),"function"==typeof t&&t(this)):this.animate(t)},animate:function(e){this.$element.addClass("redactor-animated").css("display","").removeClass("hide"),this.$element.addClass(this.opts.prefix+this.queue[0]),this.set(this.opts.duration+"s",this.opts.delay+"s",this.opts.iterate,this.opts.timing);var i=this.queue.length>1?null:e;this.complete("AnimationEnd",t.proxy(function(){this.$element.hasClass(this.opts.prefix+this.queue[0])&&(this.clean(),this.queue.shift(),this.queue.length&&this.animate(e))},this),i)},set:function(t,e,i,r){for(var n=this.prefixes.length;n--;)this.$element.css(this.prefixes[n]+"animation-duration",t),this.$element.css(this.prefixes[n]+"animation-delay",e),this.$element.css(this.prefixes[n]+"animation-iteration-count",i),this.$element.css(this.prefixes[n]+"animation-timing-function",r)},clean:function(){this.$element.removeClass("redactor-animated"),this.$element.removeClass(this.opts.prefix+this.queue[0]),this.set("","","","")},complete:function(e,i,r){this.$element.one(e.toLowerCase()+" webkit"+e+" o"+e+" MS"+e,t.proxy(function(){"function"==typeof i&&i(),"function"==typeof r&&r(this);var e=["fadeOut","slideUp","zoomOut","slideOutUp","slideOutRight","slideOutLeft"];-1!==t.inArray(this.animation,e)&&this.$element.css("display","none"),this.slide&&this.$element.css("height","")},this))}}}(jQuery); })(this);
+
+// plugins/WoltLabArticle.js
+(function (window, undefined) { $.Redactor.prototype.WoltLabArticle=function(){"use strict";return{init:function(){var t=this.button.add("woltlabArticle","");require(["WoltLabSuite/Core/Ui/Redactor/Article"],function(i){new i(this,t[0])}.bind(this))}}}; })(this);
// plugins/WoltLabAttachment.js
(function (window, undefined) { $.Redactor.prototype.WoltLabAttachment=function(){"use strict";return{init:function(){this.opts.woltlab.attachments&&require(["EventHandler"],function(t){t.add("com.woltlab.wcf.redactor2","insertAttachment_"+this.$element[0].id,this.WoltLabAttachment._insert.bind(this)),t.add("com.woltlab.wcf.redactor2","deleteAttachment_"+this.$element[0].id,this.WoltLabAttachment._delete.bind(this)),t.add("com.woltlab.wcf.redactor2","replaceAttachment_"+this.$element[0].id,this.WoltLabAttachment._replaceAttachment.bind(this))}.bind(this))},_insert:function(t){if(!this.WoltLabSource.isActive()){var e=t.attachmentId;if(this.buffer.set(),t.url){var a="wcfImgAttachment"+this.uuid,n=elById(a);n&&n.removeAttribute("id"),this.insert.html('<img src="'+t.url+'" class="woltlabAttachment" data-attachment-id="'+e+'" id="'+a+'">'),n=elById(a);for(var i=!0,r=n;r=r.nextSibling;)if(r.nodeType!==Node.TEXT_NODE||""!==r.textContent.replace(/\u200B/g,"").trim()){i=!1;break}i?this.caret.after(n.parentNode):window.setTimeout(function(){var t=elById(a);if(t){t.removeAttribute("id");var e=t.nextSibling;e&&e.nodeType===Node.TEXT_NODE&&""===e.textContent||(e=document.createTextNode(""),t.parentNode.insertBefore(e,t.nextSibling));var n=document.createRange();n.selectNode(e),n.collapse(!1);var i=window.getSelection();i.removeAllRanges(),i.addRange(n)}}.bind(this),10)}else this.insert.text("[attach="+e+"][/attach]");this.buffer.set()}},_replaceAttachment:function(t){var e=elCreate("img");e.className="woltlabAttachment",e.src=t.src,elData(e,"attachment-id",t.attachmentId),t.img.parentNode.insertBefore(e,t.img),elRemove(t.img)},_delete:function(t){var e=t.attachmentId,a=this.core.editor()[0];elBySelAll('.woltlabAttachment[data-attachment-id="'+e+'"]',a,function(t){elRemove(t)});var n="[attach="+e+"][/attach]";if(!1!==a.textContent.indexOf(n)){for(var i,r=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,null,!1),c=[];i=r.nextNode();)-1!==i.textContent.indexOf(n)&&c.push(i);for(var o=0,l=c.length;o<l;o++)c[o].textContent=c[o].textContent.replace(new RegExp("\\[attach="+e+"\\]\\[\\/attach\\]","g"),"")}}}}; })(this);
(function (window, undefined) { $.Redactor.prototype.WoltLabAutosave=function(){"use strict";return{init:function(){this.opts.woltlab.autosave&&(this.opts.woltlab.autosave.watch(this),this.opts.woltlab.autosave.createOverlay(),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","autosaveDestroy_"+this.$element[0].id,this.WoltLabAutosave.destroy.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","autosaveReset_"+this.$element[0].id,this.WoltLabAutosave.reset.bind(this)))},destroy:function(){this.opts.woltlab.autosave&&this.opts.woltlab.autosave.destroy()},reset:function(){this.opts.woltlab.autosave&&this.opts.woltlab.autosave.clear()}}}; })(this);
// plugins/WoltLabBlock.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabBlock=function(){"use strict";return{init:function(){this.block.tags=["p","blockquote","pre","h1","h2","h3","h4","h5","h6","div","figure"],this.block.format=function(e,t,i,o){if(e="quote"===e?"blockquote":e,-1!==$.inArray(e,this.block.tags))return"p"===e&&void 0===t&&(t="class"),this.placeholder.hide(),this.buffer.set(),this.utils.isCollapsed()?this.block.formatCollapsed(e,t,i,o):this.block.formatUncollapsed(e,t,i,o)}.bind(this);var e=function(e,t){return!(document.activeElement!==e||!1===t||!this.utils.isRedactorParent(t))}.bind(this),t=this.block.formatCollapsed;this.block.formatCollapsed=function(i,o,s,r){var l=this.selection.block();if(!l||"LI"!==l.nodeName&&"TD"!==l.nodeName){var a=this.core.editor()[0];e(a,l)||(this.selection.restore(),document.activeElement!==a&&a.focus()),e(a,l)||(this.focus.end(),this.selection.save());var n=t.call(this,i,o,s,r),h=n.length;if(1===h&&n[0].nodeName.match(/^H[1-6]$/)){var c=n[0];1===c.childElementCount&&"BR"===c.children[0].nodeName&&this.utils.isEmpty(c.innerHTML)&&(c.innerHTML="")}else for(var d=0;d<h;d++)this.WoltLabBlock._paragraphize(n[d]);return this.caret.end(n),n}}.bind(this);var i=this.block.formatUncollapsed;this.block.formatUncollapsed=function(e,t,o,s){this.selection.save(),this.selection.blocks().forEach(function(e){if("OL"===e.nodeName||"UL"===e.nodeName){e.parentNode.nodeName.toLowerCase();var t=elCreate("div");e.parentNode.insertBefore(t,e),t.appendChild(e)}}),this.selection.restore();for(var r,l=i.call(this,e,t,o,s),a=null,n=0,h=l.length;n<h;n++)if(r=l[n][0],this.WoltLabBlock._paragraphize(r),0===n)a=r;else{for(;r.childNodes.length;)a.appendChild(r.childNodes[0]);elRemove(r)}return $(a)}.bind(this),this.block.removeAllAttr=function(e){e=this.block.getBlocks(e);var t=[];return $.each(e,function(e,i){for(void 0===i.attributes&&t.push(i);i.attributes.length;)i.removeAttribute(i.attributes[0].name);t.push(i)}),t}.bind(this),this.block.getBlocks=function(e){if(e=void 0===e?this.selection.blocks():e,$(e).hasClass("redactor-box")||$(e).hasClass("redactor-layer")){var t=[],i=this.core.editor().children();return $.each(i,$.proxy(function(e,i){this.utils.isBlock(i)&&t.push(i)},this)),t}return e}.bind(this)},register:function(e,t){-1===this.block.tags.indexOf(e)&&(this.block.tags.push(e),this.opts.paragraphizeBlocks.push(e),-1===this.opts.blockTags.indexOf(e)&&(this.opts.blockTags.push(e),this.reIsBlock=new RegExp("^("+this.opts.blockTags.join("|").toUpperCase()+")$","i")),t&&this.WoltLabKeydown.register(e))},_paragraphize:function(e){if(-1===["p","pre","h1","h2","h3","h4","h5","h6","div","figure"].indexOf(e.nodeName.toLowerCase())){for(var t=elCreate("p");e.childNodes.length;)t.appendChild(e.childNodes[0]);e.appendChild(t)}}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabBlock=function(){"use strict";return{preserveBlocks:["pre","woltlab-quote","woltlab-spoiler"],init:function(){this.block.tags=["p","blockquote","pre","h1","h2","h3","h4","h5","h6","div","figure"],this.block.format=function(e,t,o,i){if(e="quote"===e?"blockquote":e,-1!==$.inArray(e,this.block.tags))return"p"===e&&void 0===t&&(t="class"),this.placeholder.hide(),this.buffer.set(),this.utils.isCollapsed()?this.block.formatCollapsed(e,t,o,i):this.block.formatUncollapsed(e,t,o,i)}.bind(this);var e=function(e,t){return!(document.activeElement!==e||!1===t||!this.utils.isRedactorParent(t))}.bind(this),t=this.block.formatCollapsed;this.block.formatCollapsed=function(o,i,s,r){var l=this.selection.block();if(!l||"LI"!==l.nodeName&&"TD"!==l.nodeName){var a=this.core.editor()[0];e(a,l)||(this.selection.restore(),document.activeElement!==a&&a.focus()),e(a,l)||(this.focus.end(),this.selection.save());var n=t.call(this,o,i,s,r),h=n.length;if(1===h&&n[0].nodeName.match(/^H[1-6]$/)){var c=n[0];1===c.childElementCount&&"BR"===c.children[0].nodeName&&this.utils.isEmpty(c.innerHTML)&&(c.innerHTML="")}else for(var d=0;d<h;d++)this.WoltLabBlock._paragraphize(n[d]);return this.caret.end(n),n}}.bind(this),this.block.formatUncollapsed=function(e,t,o,i){var s,r;this.selection.save(),this.selection.blocks().forEach(function(e){if("OL"===e.nodeName||"UL"===e.nodeName){e.parentNode.nodeName.toLowerCase();var t=elCreate("div");e.parentNode.insertBefore(t,e),t.appendChild(e)}}),this.selection.restore(),this.selection.save();var l=[],a=this.selection.blocks();a[0]&&($(a[0]).hasClass("redactor-in")||$(a[0]).hasClass("redactor-box"))&&(a=this.core.editor().find(this.opts.blockTags.join(", ")));var n=a.length;for(r=0;r<n;r++){var h=a[r].tagName.toLowerCase();if(-1!==$.inArray(h,this.block.tags)&&"figure"!==h){if("pre"!==e&&-1!==this.WoltLabBlock.preserveBlocks.indexOf(a[r].nodeName.toLowerCase())?(s=elCreate(e),a[r].parentNode.insertBefore(s,a[r]),s.appendChild(a[r]),s=$(s)):s=this.utils.replaceToTag(a[r],e),"object"==typeof t){i=o;for(var c in t)s=this.block.setAttr(s,c,t[c],i)}else s=this.block.setAttr(s,t,o,i);l.push(s),this.block.removeInlineTags(s)}}if(this.selection.restore(),"pre"===e&&0!==l.length){var d=l[0];$.each(l,function(e,t){0!==e&&($(d).append("\n"+$.trim(t.html())),$(t).remove())}),l=[],l.push(d)}var p=null;for(r=0,length=l.length;r<length;r++)if(s=l[r][0],this.WoltLabBlock._paragraphize(s),0===r)p=s;else{for(;s.childNodes.length;)p.appendChild(s.childNodes[0]);elRemove(s)}return $(p)}.bind(this),this.block.removeAllAttr=function(e){e=this.block.getBlocks(e);var t=[];return $.each(e,function(e,o){for(void 0===o.attributes&&t.push(o);o.attributes.length;)o.removeAttribute(o.attributes[0].name);t.push(o)}),t}.bind(this),this.block.getBlocks=function(e){if(e=void 0===e?this.selection.blocks():e,$(e).hasClass("redactor-box")||$(e).hasClass("redactor-layer")){var t=[],o=this.core.editor().children();return $.each(o,$.proxy(function(e,o){this.utils.isBlock(o)&&t.push(o)},this)),t}return e}.bind(this)},register:function(e,t){-1===this.block.tags.indexOf(e)&&(this.block.tags.push(e),this.opts.paragraphizeBlocks.push(e),-1===this.opts.blockTags.indexOf(e)&&(this.opts.blockTags.push(e),this.reIsBlock=new RegExp("^("+this.opts.blockTags.join("|").toUpperCase()+")$","i")),t&&this.WoltLabKeydown.register(e))},_paragraphize:function(e){if(-1===["p","pre","h1","h2","h3","h4","h5","h6","div","figure"].indexOf(e.nodeName.toLowerCase())){for(var t,o=!1,i=0,s=e.childNodes.length;i<s;i++){if(t=e.childNodes[i],t.nodeType===Node.TEXT_NODE){o=!0;break}if(t.nodeType===Node.ELEMENT_NODE&&-1===this.block.tags.indexOf(t.nodeName.toLowerCase())){o=!0;break}}if(o)for(var r,l=e.childNodes[0],a=null;l;)r=l.nextSibling,l.nodeType!==Node.ELEMENT_NODE||-1===this.block.tags.indexOf(l.nodeName.toLowerCase())?(null===a&&(a=elCreate("p"),e.insertBefore(a,l)),a.appendChild(l)):null!==a&&(a=null),l=r}}}}; })(this);
// plugins/WoltLabButton.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabButton=function(){"use strict";var t;return{init:function(){this.button.buildButtonTooltip=function(){};var t=this.button.clickCallback;this.button.clickCallback=function(e,o,n,i){"function"==typeof e.preventDefault&&e.preventDefault(),t.call(this,e,o,n,i)}.bind(this);var e,o,n,i;for(n=0,i=this.opts.woltlab.customButtons.length;n<i;n++)o=this.opts.woltlab.customButtons[n],e=this.button.add(o,""),this.button.addCallback(e,this.WoltLabButton._handleCustomButton);var s,a,l;for(n=0,i=this.opts.buttons.length;n<i;n++)if("wcfSeparator"!==(o=this.opts.buttons[n])){if(!this.opts.woltlab.buttons.hasOwnProperty(o))throw new Error("Missing button definition for '"+o+"'.");switch(s=this.opts.woltlab.buttons[o],"underline"===o&&(this.opts.activeButtonsStates.u="underline"),o){case"subscript":case"superscript":e=this.button.addAfter(this.opts.buttons[n-1],o,""),this.button.setEvent(e,o,{func:"inline.format"}),this.opts.activeButtonsStates["subscript"===o?"sub":"sup"]=o;break;case"redo":case"undo":e=this.button.addAfter(this.opts.buttons[n-1],o,""),this.button.addCallback(e,this.buffer[o]);break;default:e=this.button.get(o)}if(a=s.icon,l=!1,!a.match(/^fa-/)&&a.match(/\.(gif|jpe?g|png|svg)$/)&&(l=!0),this.button.setIcon(e,'<span class="icon icon16 '+(l?"redactorButtonImage":a)+'"'+(l?" style=\"background-image: url('"+WCF_PATH+"icon/"+a+"')\"":"")+"></span>"),!e[0])throw new Error("Missing button element for '"+o+"'.");if(elAttr(e[0],"title",s.title),e[0].classList.add("jsTooltip"),"lists"===o){var r=e.data("dropdown");elBySel(".redactor-dropdown-outdent span",r[0]).textContent=WCF.Language.get("wcf.editor.list.outdent"),elBySel(".redactor-dropdown-indent span",r[0]).textContent=WCF.Language.get("wcf.editor.list.indent")}}var c=this.core.toolbar()[0];elBySelAll(".re-button-tooltip",c.parentNode,elRemove);for(var d,u={},h=[];c.childElementCount;)d=c.removeChild(c.children[0]),o=elAttr(d.children[0],"rel"),u[o]=d,h.push(o);var b=!1;for(n=0,i=this.opts.buttons.length;n<i;n++)o=this.opts.buttons[n],"wcfSeparator"!==o?(d=u[o],c.appendChild(d),h.splice(h.indexOf(o),1),b&&(d.classList.add("redactor-toolbar-separator"),b=!1)):b=!0;for(n=0,i=c.childElementCount;n<i;n++)d=c.children[n],e=d.children[0],elData(d,"show-on-mobile",-1!==this.opts.woltlab.buttonMobile.indexOf(e.rel));h.forEach(function(t){c.appendChild(u[t])}),WCF.DOMNodeInsertedHandler.execute(),require(["Ui/Screen"],function(t){t.on("screen-xs",{match:this.WoltLabButton._enableToggleButton.bind(this),unmatch:this.WoltLabButton._disableToggleButton.bind(this),setup:this.WoltLabButton._setupToggleButton.bind(this)})}.bind(this)),this.$toolbar[0].addEventListener("dragstart",function(t){t.preventDefault()})},_handleCustomButton:function(t){var e={cancel:!1};if(WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","bbcode_"+t+"_"+this.$element[0].id,e),!0!==e.cancel){this.buffer.set();var o=this.marker.get();o.classList.add("woltlab-bbcode-marker");var n="["+t+"]"+this.selection.html()+o.outerHTML+"[/"+t+"]";this.insert.html(n),this.selection.restore()}window.setTimeout(function(){document.activeElement!==this.$editor[0]&&this.$editor[0].focus()}.bind(this),10)},_enableToggleButton:function(){null===t.parentNode&&this.$toolbar[0].appendChild(t)},_disableToggleButton:function(){null!==t.parentNode&&this.$toolbar[0].removeChild(t)},_setupToggleButton:function(){t=elCreate("li"),t.className="redactorToolbarToggle",t.innerHTML='<a href="#"><span class="icon icon16 fa-caret-down"></span></a>',elData(t,"show-on-mobile",!0);var e=t.children[0].children[0],o=function(t){t instanceof Event&&t.preventDefault(),this.$toolbar[0].classList.toggle("redactorToolbarOverride")&&document.activeElement&&document.activeElement!==this.$editor[0]&&document.activeElement.blur(),e.classList.toggle("fa-caret-down"),e.classList.toggle("fa-caret-up")}.bind(this);t.children[0].addEventListener("mousedown",o),this.$toolbar[0].appendChild(t),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+this.$element[0].id,function(){this.$toolbar[0].classList.contains("redactorToolbarOverride")&&o()}.bind(this))}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabButton=function(){"use strict";var t;return{init:function(){var t,e,o,n;for(o=0,n=this.opts.woltlab.customButtons.length;o<n;o++)e=this.opts.woltlab.customButtons[o],t=this.button.add(e,""),this.button.addCallback(t,this.WoltLabButton._handleCustomButton);var s,i,a,r=this.core.toolbar()[0];for(o=0,n=this.opts.buttons.length;o<n;o++)if("wcfSeparator"!==(e=this.opts.buttons[o])){if(!this.opts.woltlab.buttons.hasOwnProperty(e))throw new Error("Missing button definition for '"+e+"'.");switch(s=this.opts.woltlab.buttons[e],"underline"===e&&(this.opts.activeButtonsStates.u="underline"),e){case"subscript":case"superscript":t=this.button.addAfter(this.opts.buttons[o-1],e,""),this.button.setEvent(t,e,{func:"inline.format"}),this.opts.activeButtonsStates["subscript"===e?"sub":"sup"]=e;break;case"redo":case"undo":t=this.button.addAfter(this.opts.buttons[o-1],e,""),this.button.addCallback(t,this.buffer[e]);break;default:t=this.button.get(e)}if(i=s.icon,a=!i.match(/^fa-/)&&i.match(/\.(gif|jpe?g|png|svg)$/),this.button.setIcon(t,'<span class="icon icon16 '+(a?"redactorButtonImage":i)+'"'+(a?" style=\"background-image: url('"+WCF_PATH+"icon/"+i+"')\"":"")+"></span>"),!t[0])throw new Error("Missing button element for '"+e+"'.");if(t[0].title=s.title,t[0].classList.add("jsTooltip"),"lists"===e){var l=t.data("dropdown");elBySel(".redactor-dropdown-outdent span",l[0]).textContent=WCF.Language.get("wcf.editor.list.outdent"),elBySel(".redactor-dropdown-indent span",l[0]).textContent=WCF.Language.get("wcf.editor.list.indent")}}for(var d,c={},u=[];r.childElementCount;)d=r.removeChild(r.children[0]),e=elAttr(d.children[0],"rel"),c[e]=d,u.push(e);var h=!1;for(o=0,n=this.opts.buttons.length;o<n;o++)e=this.opts.buttons[o],"wcfSeparator"!==e?(d=c[e],r.appendChild(d),u.splice(u.indexOf(e),1),h&&(d.classList.add("redactor-toolbar-separator"),h=!1)):h=!0;for(o=0,n=r.childElementCount;o<n;o++)d=r.children[o],t=d.children[0],elData(d,"show-on-mobile",-1!==this.opts.woltlab.buttonMobile.indexOf(t.rel));u.forEach(function(t){r.appendChild(c[t])}),WCF.DOMNodeInsertedHandler.execute(),require(["Ui/Screen"],function(t){t.on("screen-xs",{match:this.WoltLabButton._enableToggleButton.bind(this),unmatch:this.WoltLabButton._disableToggleButton.bind(this),setup:this.WoltLabButton._setupToggleButton.bind(this)})}.bind(this)),r.addEventListener("dragstart",function(t){t.preventDefault()}),elAttr(elBySel(".re-html",r),"tabindex",0)},_handleCustomButton:function(t){var e={cancel:!1};if(WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","bbcode_"+t+"_"+this.$element[0].id,e),!0!==e.cancel){this.buffer.set();var o=this.marker.get();o.classList.add("woltlab-bbcode-marker");var n="["+t+"]"+this.selection.html()+o.outerHTML+"[/"+t+"]";this.insert.html(n),this.selection.restore()}window.setTimeout(function(){document.activeElement!==this.$editor[0]&&this.$editor[0].focus()}.bind(this),10)},_enableToggleButton:function(){null===t.parentNode&&this.$toolbar[0].appendChild(t)},_disableToggleButton:function(){null!==t.parentNode&&this.$toolbar[0].removeChild(t)},_setupToggleButton:function(){t=elCreate("li"),t.className="redactorToolbarToggle",t.innerHTML='<a href="#"><span class="icon icon16 fa-caret-down"></span></a>',elData(t,"show-on-mobile",!0);var e=t.children[0].children[0],o=function(t){t instanceof Event&&t.preventDefault(),this.$toolbar[0].classList.toggle("redactorToolbarOverride")&&document.activeElement&&document.activeElement!==this.$editor[0]&&document.activeElement.blur(),e.classList.toggle("fa-caret-down"),e.classList.toggle("fa-caret-up")}.bind(this);t.children[0].addEventListener("mousedown",o),this.$toolbar[0].appendChild(t),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+this.$element[0].id,function(){this.$toolbar[0].classList.contains("redactorToolbarOverride")&&o()}.bind(this))}}}; })(this);
// plugins/WoltLabCaret.js
(function (window, undefined) { $.Redactor.prototype.WoltLabCaret=function(){"use strict";var e,t=!1,n=!1;return{init:function(){var i=this.caret.after;this.caret.after=function(e){e=this.caret.prepare(e),this.utils.isBlockTag(e.tagName)&&this.WoltLabCaret._addParagraphAfterBlock(e),i.call(this,e)}.bind(this);var r=this.core.editor()[0];require(["Environment"],function(i){t="ios"===i.platform(),(n="safari"===i.browser())&&r.classList.add("jsSafariMarginClickTarget");var o=this.WoltLabCaret._handleEditorClick.bind(this),a=this.WoltLabCaret._handleEditorMouseUp.bind(this);n&&t?(r.addEventListener("touchstart",function(t){e=t.target},{passive:!0}),r.addEventListener("touchend",function(e){o(e),a(e)}.bind(this))):(r.addEventListener(WCF_CLICK_EVENT,o),r.addEventListener("mouseup",a))}.bind(this));var o=this.caret.end;this.caret.end=function(e){e=this.caret.prepare(e),"OL"!==e.nodeName&&"UL"!==e.nodeName||null===(e=e.lastElementChild)&&(e=e.parentNode);var n=!1;if(e.nodeType===Node.ELEMENT_NODE&&e.lastChild&&"P"===e.lastChild.nodeName)n=!0;else if(t){var i=this.core.editor()[0];e.parentNode===i&&"<p><br></p>"===i.innerHTML&&(n=!0)}else"P"===e.nodeName&&0===e.childNodes.length&&(e.innerHTML="",n=!0);if(n){var r=window.getSelection(),a=document.createRange();return a.selectNodeContents(e.lastChild),a.collapse(!1),r.removeAllRanges(),void r.addRange(a)}return"P"===e.nodeName&&1===e.childNodes.length&&"BR"===e.childNodes[0].nodeName?this.caret.before(e.childNodes[0]):o.call(this,e)}.bind(this);var a=this.selection.nodes;this.selection.nodes=function(e){var t=a.call(this,e);if(1===t.length&&t[0]===this.$editor[0]){var n=this.selection.range(this.selection.get());if(n.startContainer===n.endContainer)return[n.startContainer]}return t}.bind(this),this.WoltLabCaret._initInternalRange();var s=this.selection.saveInstant;this.selection.saveInstant=function(){var e=s.call(this);if(e){e.isAtNodeStart=!1;var t=window.getSelection();if(t.rangeCount&&!t.isCollapsed){var n=t.getRangeAt(0);n.startContainer.nodeType===Node.TEXT_NODE&&0===n.startOffset&&(e.isAtNodeStart=!0)}}return e}.bind(this);var l=this.selection.restoreInstant;this.selection.restoreInstant=function(e){if(void 0!==e||this.saved){var t=void 0!==e?e:this.saved;l.call(this,e);var n=window.getSelection();if(n.rangeCount)if(!0===t.isAtNodeStart){if(!n.isCollapsed){var i=n.getRangeAt(0),r=i.startContainer;if(t.node===r)return;for(;null!==r&&"P"!==r.nodeName;)r=r.parentNode;if(null!==r&&null!==(r=r.nextElementSibling)&&"P"===r.nodeName&&0===r.textContent.replace(/\u200B/g,"").length){r=r.nextElementSibling;for(var o=t.node;null!==o&&o!==r;)o=o.parentNode;o===r&&(i=i.cloneRange(),i.setStart(t.node,0),n.removeAllRanges(),n.addRange(i))}}}else if(n.isCollapsed){var a=n.anchorNode,s=this.core.editor()[0];if(a.nodeType===Node.TEXT_NODE&&a.parentNode===s&&n.anchorOffset===a.textContent.length){var d=a.nextElementSibling;d&&"P"===d.nodeName&&this.caret.start(d)}}}}.bind(this),this.selection.nodes=function(e){var t=void 0===e?[]:$.isArray(e)?e:[e],n=this.selection.get(),i=this.selection.range(n),r=[],o=[];if(this.utils.isCollapsed())r=[this.selection.current()];else{var a=i.startContainer,s=i.endContainer;if(a===s)return[a];for(var l=i.commonAncestorContainer;a&&a!==s;)r.push(a=this.selection.nextNode(a,l));for(a=i.startContainer;a&&a!==l;)r.unshift(a),a=a.parentNode}return $.each(r,function(e,n){if(n){var i=1===n.nodeType&&n.tagName.toLowerCase();if($(n).hasClass("redactor-script-tag")||$(n).hasClass("redactor-selection-marker"))return;if(i&&0!==t.length&&-1===$.inArray(i,t))return;o.push(n)}}),0===o.length?[]:o}.bind(this),this.selection.nextNode=function(e,t){if(e.hasChildNodes())return e.firstChild;for(;e&&!e.nextSibling;)if(e=e.parentNode,t&&e===t)return null;return e?e.nextSibling:null}},paragraphAfterBlock:function(e){var t=e.nextElementSibling;t&&"P"!==t.nodeName&&(t=elCreate("p"),t.textContent="",e.parentNode.insertBefore(t,e.nextSibling)),this.caret.after(e)},endOfEditor:function(){var e=this.core.editor()[0];document.activeElement!==e&&e.focus();var t=e.lastElementChild;"P"===t.nodeName?this.caret.end(t):this.caret.after(t)},_initInternalRange:function(){var e=this.core.editor()[0],t=null,n=window.getSelection(),i=function(){t=n.rangeCount?n.getRangeAt(0).cloneRange():null};this.WoltLabCaret.forceSelectionSave=i;var r=function(){if(null!==t){if(document.activeElement===e){var i=n.getRangeAt(0);if(0!==i.startOffset)return;for(var r=i.startContainer;r;){if(r.parentNode===e){if(r.previousSibling)return;break}if(r.previousSibling)return;r=r.parentNode}if(!r)return}var o=e.scrollLeft,a=e.scrollTop;e.focus(),e.scrollLeft=o,e.scrollTop=a,n.removeAllRanges(),n.addRange(t),t=null}};e.addEventListener("keyup",i),e.addEventListener("mouseup",function(){n.rangeCount&&i()});var o=this.selection.save;this.selection.save=function(){t=null,o.call(this)}.bind(this);var a=this.selection.restore;this.selection.restore=function(){t&&null===elBySel(".redactor-selection-marker",this.$editor[0])&&(r(),n.rangeCount&&this.utils.isRedactorParent(n.getRangeAt(0).commonAncestorContainer))||a.call(this)}.bind(this);var s=this.buffer.set;this.buffer.set=function(t){if(document.activeElement!==e){var n=window.getSelection();n.rangeCount&&!1!==this.utils.isRedactorParent(n.anchorNode)?e.focus():r()}s.call(this,t),i()}.bind(this);var l=this.insert.html;this.insert.html=function(e,t){var n=elBySel(".redactor-selection-marker",this.$editor[0]);l.call(this,e,t),(n||null===elBySel(".redactor-selection-marker",this.$editor[0]))&&i()}.bind(this),require(["Environment"],function(t){"ios"===t.platform()&&(e.addEventListener("focus",function(){document.addEventListener("selectionchange",i)}),e.addEventListener("blur",function(){document.removeEventListener("selectionchange",i)}))}.bind(this))},_handleEditorClick:function(i){var r=i.clientY;if(!this.selection.get().isCollapsed){if(!(n&&t&&e===i.target&&this.utils.isBlockTag(e.nodeName)))return;r=i.changedTouches[0].clientY}var o=this.selection.block();if(!1===o){if(this.selection.current()===this.$editor[0]){var a=this.$editor[0].childNodes[this.selection.get().anchorOffset];a.nodeType===Node.ELEMENT_NODE&&"TABLE"===a.nodeName&&(o=a)}if(!1===o)return}var s=!1;n&&this.utils.isBlockTag(i.target.nodeName)&&r>i.target.getBoundingClientRect().bottom&&(o=i.target,s=!0);for(var l=i.target;l&&!this.utils.isBlockTag(l.nodeName);)l=l.parentNode;if(l&&(s||l!==o)&&("P"!==o.nodeName||(o=o.parentNode)!==this.$editor[0]&&this.utils.isBlockTag(o.nodeName))){if("TD"===o.nodeName)for(;"TABLE"!==o.nodeName;)o=o.parentNode;if(!o.nodeName.match(/^H\d$/)&&!$(o).closest("ol, ul",this.$editor[0]).length){for(var d,c,h=o;h;){if(c=h.getBoundingClientRect(),r<c.top)d=!0,o=h;else{if(!(r>c.bottom))break;d=!1,o=h}if(!h.parentNode||h.parentNode===this.$editor[0])break;h=h.parentNode}if(void 0!==d){var f=o[(d?"previous":"next")+"ElementSibling"];if(f&&"P"===f.nodeName)return void this.caret.end(f);this.buffer.set();var u=elCreate("p");u.textContent="",o.parentNode.insertBefore(u,d?o:o.nextSibling),this.caret.end(u)}}}},_handleEditorMouseUp:function(i){var r,o,a=window.getSelection();if(a.isCollapsed||n&&t&&e===i.target&&this.utils.isBlockTag(e.nodeName))if(i.target===this.$editor[0])r=a.anchorNode,r.nodeType===Node.TEXT_NODE&&(r=r.parentNode),"KBD"===r.nodeName&&(o=r.previousSibling,null!==o&&""===o.textContent||(o=document.createTextNode(""),r.parentNode.insertBefore(o,r)),this.caret.before(o));else if("KBD"===i.target.nodeName){var s=i.target;if(r=a.anchorNode,r.nodeType===Node.TEXT_NODE){for(o=r;(o=o.nextSibling)&&o.nodeType===Node.TEXT_NODE&&(""===o.textContent||""===o.textContent););if(o===s){if(0===s.childNodes.length||""!==s.childNodes[0].textContent){var l=document.createTextNode("");s.insertBefore(l,s.firstChild)}var d=document.createRange();d.setStartAfter(s.childNodes[0]),d.setEndAfter(s.childNodes[0]),a.removeAllRanges(),a.addRange(d)}}}},_addParagraphAfterBlock:function(e){var t=e.nextElementSibling;t&&("P"===t.nodeName||this.utils.isBlockTag(t.nodeName))||(t=elCreate("p"),t.textContent="",e.parentNode.insertBefore(t,e.nextSibling))}}}; })(this);
// plugins/WoltLabClean.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabClean=function(){"use strict";return{init:function(){var e=this.clean.onSet;this.clean.onSet=function(t){t=t.replace(/\u200B/g,""),t=t.replace(/&amp;/g,"@@@WCF_LITERAL_AMP@@@"),t=t.replace(/&/g,"&WCF_AMPERSAND&"),t=e.call(this,t),t=t.replace(/&WCF_AMPERSAND&(amp;)?/g,"&"),t=t.replace(/@@@WCF_LITERAL_AMP@@@/,"&amp;");var n=elCreate("div");return n.innerHTML=t,elBySelAll("iframe",n,elRemove),elBySelAll("pre",n,function(e){e.classList.contains("redactor-script-tag")&&elRemove(e)}),elBySelAll("td",n,function(e){0===e.childNodes.length&&(e.innerHTML="")}),elBySelAll("pre, woltlab-quote, woltlab-spoiler",n,function(e){0!==e.childElementCount||0!==e.textContent.length&&!e.textContent.match(/^\r?\n$/)||(e.textContent="")}),t=n.innerHTML}.bind(this);var t=this.clean.onSync;this.clean.onSync=function(e){var n=elCreate("div");n.innerHTML=e;var l={};return elBySelAll("pre",n,function(e){var t=WCF.getUUID();l[t]=e.textContent,e.textContent=t}),elBySelAll("p",n,function(e){var t=e.lastElementChild;if(t&&"BR"===t.nodeName)if(t.nextSibling){if(t.nextSibling.textContent.replace(/[\r\n\t]/g,"").match(/^\u200B+$/)){var n=elCreate("p");n.innerHTML="<br>",e.parentNode.insertBefore(n,e.nextSibling),e.removeChild(t.nextSibling),e.removeChild(t)}}else(t.previousElementSibling||t.previousSibling&&""!==t.previousSibling.textContent.replace(/\u200B/g,"").trim())&&e.removeChild(t)}),elBySelAll("span",n,function(e){if(e.childNodes.length>0){var t=e.childNodes[e.childNodes.length-1];t.nodeType===Node.TEXT_NODE&&t.textContent.match(/\n$/)&&(t.textContent=t.textContent.replace(/\n+$/,e.parentNode.lastChild===e?"":" "))}}),e=n.innerHTML,e=e.replace(/<p>\u200B<\/p>/g,"<p><br></p>"),e=e.replace(/&/g,"&WCF_AMPERSAND&"),e=t.call(this,e),e=e.replace(/&WCF_AMPERSAND&/g,"&"),n.innerHTML=e,elBySelAll("pre",n,function(e){l.hasOwnProperty(e.textContent)&&(e.textContent=l[e.textContent])}),e=n.innerHTML}.bind(this);var n=this.clean.savePreFormatting;this.clean.savePreFormatting=function(e){var t=this.clean.encodeEntities;return this.clean.encodeEntities=function(e){return WCF.String.escapeHTML(e)},e=n.call(this,e),this.clean.encodeEntities=t,e}.bind(this);var l=this.clean.onPaste;this.clean.onPaste=function(e,t,n){if(t.pre||this.utils.isCurrentOrParent("kbd"))return t.pre&&this.opts.preSpaces&&(e=e.replace(/\t/g,new Array(this.opts.preSpaces+1).join(" "))),WCF.String.escapeHTML(e);var r=elCreate("div");r.innerHTML=e.replace(/@@@WOLTLAB-P-ALIGN-(?:left|right|center|justify)@@@/g,"");var i,o,a,s=!0;for(o=0,a=r.childElementCount;o<a;o++){if(i=r.children[o],"DIV"!==i.nodeName||0===i.childNodes.length){s=!1;break}if(1===i.childNodes.length&&1===i.childElementCount){var c=i.children[0];if(0===c.childNodes.length&&"BR"!==c.nodeName){s=!1;break}}}if(s){var h=[];for(o=0,a=r.childElementCount;o<a;o++)h.push(r.children[o]);h.forEach(function(e){var t=elCreate("p");for(r.insertBefore(t,e);e.childNodes.length>0;)t.appendChild(e.childNodes[0]);r.removeChild(e)})}var d,p,f,u,m=null!==elBySel(".MsoNormal",r),g=elBySelAll("[style]",r);for(o=0,a=g.length;o<a;o++){i=g[o],p=[];for(var v=0,y=i.style.length;v<y;v++)if(d=i.style[v],-1===this.opts.woltlab.allowedInlineStyles.indexOf(d)){if("font-weight"===d&&"STRONG"!==i.nodeName)u=i.style.getPropertyValue(d),"bold"!==u&&"bolder"!==u||(u=600),(u=~~u)>500&&(f=elCreate("strong"),i.parentNode.insertBefore(f,i),f.appendChild(i));else if(m&&"margin-bottom"===d&&"P"===i.nodeName&&(u=i.style.getPropertyValue(d),u.match(/^12(?:\.0)?pt$/))){var b=elCreate("p");b.innerHTML="<br>",i.parentNode.insertBefore(b,i.nextSibling)}p.push(d)}p.forEach(function(e){i.style.removeProperty(e)})}return elBySelAll("span",r,function(e){if(!e.classList.contains("redactor-selection-marker"))if(e.hasAttribute("style")&&e.style.length)for(var t=e.style.getPropertyValue("color"),n=e.style.getPropertyValue("font-family"),l=e.style.getPropertyValue("font-size"),r=(t?1:0)+(n?1:0)+(l?1:0);r>1;){var i=elCreate("span");t?(i.style.setProperty("color",t,""),e.style.removeProperty("color"),t="",r--):n?(i.style.setProperty("font-family",n,""),e.style.removeProperty("font-family"),n="",r--):l&&(i.style.setProperty("font-size",l,""),e.style.removeProperty("font-size"),l="",r--),e.parentNode.insertBefore(i,e),i.appendChild(e)}else{for(;e.childNodes.length;)e.parentNode.insertBefore(e.childNodes[0],e);elRemove(e)}}),elBySelAll("p",r,function(e){e.classList.contains("MsoNormal")?1===e.childElementCount&&"O:P"===e.children[0].nodeName&&" "===e.textContent&&(e.innerHTML="<br>"):e.className.match(/\btext-(left|right|center|justify)\b/)&&e.insertBefore(document.createTextNode("@@@WOLTLAB-P-ALIGN-"+RegExp.$1+"@@@"),e.firstChild),e.removeAttribute("class"),e.removeAttribute("style")}),elBySelAll("img",r,function(e){e.removeAttribute("style")}),elBySelAll("br",r,function(e){e.parentNode.insertBefore(document.createTextNode("@@@WOLTLAB-BR-MARKER@@@"),e.nextSibling)}),elBySelAll("kbd",r,function(e){for(e.insertBefore(document.createTextNode("[tt]"),e.firstChild),e.appendChild(document.createTextNode("[/tt]"));e.childNodes.length;)e.parentNode.insertBefore(e.childNodes[0],e);elRemove(e)}),e=l.call(this,r.innerHTML,t,n),e=e.replace(/\n*@@@WOLTLAB-BR-MARKER@@@\n*/g,"<woltlab-br-marker></woltlab-br-marker>"),e=e.replace(/(<p>)?\s*@@@WOLTLAB-P-ALIGN-(left|right|center|justify)@@@/g,function(e,t,n){return t?'<p class="text-'+n+'">':""}),r.innerHTML=e.replace(/&quot;/g,"""),elBySelAll("woltlab-br-marker",r,function(e){var t=e.parentNode;if(null!==t){if("P"===t.nodeName){var n=elCreate("p");n.innerHTML="<br>";var l=!1,r=e.nextSibling;r&&"WOLTLAB-BR-MARKER"===r.nodeName&&(l=!0);for(var i=!l;e.nextSibling;)i&&0!==e.nextSibling.textContent.replace(/\u200B/g,"").trim().length&&(i=!1),n.appendChild(e.nextSibling);i||elRemove(n.firstElementChild);var o=e.previousSibling;o&&"BR"===o.nodeName&&elRemove(o),t.parentNode.insertBefore(n,t.nextSibling),l&&(n=elCreate("p"),n.innerHTML="<br>",t.parentNode.insertBefore(n,t.nextSibling))}else t.insertBefore(elCreate("br"),e);elRemove(e)}}),elBySelAll("p",r,function(e){var t=!1;0===e.childNodes.length?t=!0:""===e.textContent?(t=!0,elBySelAll("*",e,function(e){"SPAN"!==e.nodeName&&(t=!1)})):0===e.textContent.trim().length&&(elBySelAll("span",e,function(e){if(!e.hasAttribute("style")||!e.style.length){for(;e.childNodes.length;)e.parentNode.insertBefore(e.childNodes[0],e);elRemove(e)}}),0===e.children.length&&(e.innerHTML="<br>")),t&&elRemove(e)}),r.innerHTML}.bind(this);var r=[],i=function(e,t){for(var n,l,i={},o=0,a=t.length;o<a;o++)n=t[o],l=elAttr(e,n),"style"===n&&0===e.style.length&&0===l.indexOf("font-family")&&(l=l.replace(/"/g,"")),i[n]=l;r.push({element:e,attributes:i})},o=this.clean.convertTags;this.clean.convertTags=function(e,t){var n=elCreate("div");n.innerHTML=e,r=[],WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","convertTags_"+this.$element[0].id,{addToStorage:i,div:n}),elBySelAll("span",n,function(e){i(e,["style"])}),r.forEach(function(e,t){var n=e.element,l=n.parentNode;for(l.insertBefore(document.createTextNode("###custom"+t+"###"),n),l.insertBefore(document.createTextNode("###/custom"+t+"###"),n.nextSibling);n.childNodes.length;)l.insertBefore(n.childNodes[0],n);l.removeChild(n)});var l=!1;t.links&&this.opts.pasteLinks&&(elBySelAll("a",n,function(e){e.href&&(e.outerHTML='#####[a href="'+e.href+'"]#####'+e.innerHTML+"#####[/a]#####")}),l=!0,t.links=!1);var a=!1;return t.images&&this.opts.pasteImages&&(elBySelAll("img",n,function(e){if(e.src){for(var t,n='#####[img src="'+e.src+'"',l=0,r=e.attributes.length;l<r;l++)t=e.attributes.item(l),"src"!==t.name&&(n+=" "+t.name+'="'+t.value+'"');e.outerHTML=n+"]#####"}}),a=!0,t.images=!1),e=o.call(this,n.innerHTML,t),a&&(t.images=!0),l&&(t.links=!0),e}.bind(this);var a=this.clean.reconvertTags;this.clean.reconvertTags=function(e,t){if(r.length){e=e.replace(/###(\/?)custom(\d+)###/g,'<$1woltlab-custom-tag data-index="$2">');var n=elCreate("div");n.innerHTML=e,elBySelAll("woltlab-custom-tag",n,function(e){var t=~~elData(e,"index");if(r[t]){var n=r[t],l=elCreate(n.element.nodeName);for(var i in n.attributes)n.attributes.hasOwnProperty(i)&&elAttr(l,i,n.attributes[i]);for(e.parentNode.insertBefore(l,e);e.childNodes.length;)l.appendChild(e.childNodes[0])}elRemove(e)}),e=n.innerHTML}return a.call(this,e,t)}.bind(this),this.clean.removeSpans=function(e){return e};var s=this.clean.getCurrentType;this.clean.getCurrentType=function(e,t){var n=s.call(this,e,t);return this.utils.isCurrentOrParent(["kbd"])&&(n.inline=!1,n.block=!1,n.encode=!0,n.pre=!0,n.paragraphize=!1,n.images=!1,n.links=!1),n}.bind(this);this.clean.removeEmptyInlineTags;this.clean.removeEmptyInlineTags=function(e){var t=this.opts.inlineTags,n=$("<div/>").html($.parseHTML(e,document,!0)),l=this,r=n.find("span"),i=n.find(t.join(","));return i.filter(":not(span)").removeAttr("style"),i.each(function(){var e=$(this).html();0===this.attributes.length&&l.utils.isEmpty(e)&&$(this).replaceWith(function(){return $(this).contents()})}),r.each(function(){$(this).html();0===this.attributes.length&&$(this).replaceWith(function(){return $(this).contents()})}),e=n.html(),e=e.replace("\x3c!--?php","<?php"),e=e.replace("\x3c!--?","<?"),e=e.replace("?--\x3e","?>"),n.remove(),e}.bind(this)},removeRedundantStyles:function(){var e=[],t=["del","em","strong","sub","sup","u"];elBySelAll(t.join(","),this.$editor[0],function(t){null!==elBySel(t.nodeName,t)&&e.push(t)}),elBySelAll("span[style]",this.$editor[0],function(t){["color","font-family","font-size"].forEach(function(n){var l=t.style.getPropertyValue(n);l&&window.getComputedStyle(t.parentNode).getPropertyValue(n)===l&&e.push(t)})});var n;e.forEach(function(e){for(n=e.parentNode;e.childNodes.length;)n.insertBefore(e.childNodes[0],e);n.removeChild(e)})}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabClean=function(){"use strict";return{init:function(){var e=this.clean.onSet;this.clean.onSet=function(t){t=t.replace(/\u200B/g,""),t=t.replace(/&amp;/g,"@@@WCF_LITERAL_AMP@@@"),t=t.replace(/&/g,"&WCF_AMPERSAND&"),t=e.call(this,t),t=t.replace(/&WCF_AMPERSAND&(amp;)?/g,"&"),t=t.replace(/@@@WCF_LITERAL_AMP@@@/,"&amp;");var n=elCreate("div");return n.innerHTML=t,elBySelAll("iframe",n,elRemove),elBySelAll("pre",n,function(e){e.classList.contains("redactor-script-tag")&&elRemove(e)}),elBySelAll("td",n,function(e){0===e.childNodes.length&&(e.innerHTML="")}),elBySelAll("pre, woltlab-quote, woltlab-spoiler",n,function(e){0!==e.childElementCount||0!==e.textContent.length&&!e.textContent.match(/^\r?\n$/)||(e.textContent="")}),t=n.innerHTML}.bind(this);var t=this.clean.onSync;this.clean.onSync=function(e){var n=elCreate("div");n.innerHTML=e;var l={};return elBySelAll("pre",n,function(e){var t=WCF.getUUID();l[t]=e.textContent,e.textContent=t}),elBySelAll("p",n,function(e){var t=e.lastElementChild;if(t&&"BR"===t.nodeName)if(t.nextSibling){if(t.nextSibling.textContent.replace(/[\r\n\t]/g,"").match(/^\u200B+$/)){var n=elCreate("p");n.innerHTML="<br>",e.parentNode.insertBefore(n,e.nextSibling),e.removeChild(t.nextSibling),e.removeChild(t)}}else(t.previousElementSibling||t.previousSibling&&""!==t.previousSibling.textContent.replace(/\u200B/g,"").trim())&&e.removeChild(t)}),elBySelAll("span",n,function(e){if(e.childNodes.length>0){var t=e.childNodes[e.childNodes.length-1];t.nodeType===Node.TEXT_NODE&&t.textContent.match(/\n$/)&&(t.textContent=t.textContent.replace(/\n+$/,e.parentNode.lastChild===e?"":" "))}}),e=n.innerHTML,e=e.replace(/<p>\u200B<\/p>/g,"<p><br></p>"),e=e.replace(/&/g,"&WCF_AMPERSAND&"),e=t.call(this,e),e=e.replace(/&WCF_AMPERSAND&/g,"&"),n.innerHTML=e,elBySelAll("pre",n,function(e){l.hasOwnProperty(e.textContent)&&(e.textContent=l[e.textContent])}),e=n.innerHTML}.bind(this);var n=this.clean.savePreFormatting;this.clean.savePreFormatting=function(e){var t=this.clean.encodeEntities;return this.clean.encodeEntities=function(e){return WCF.String.escapeHTML(e)},e=n.call(this,e),this.clean.encodeEntities=t,e}.bind(this);var l=this.clean.onPaste;this.clean.onPaste=function(e,t,n){if(t.pre||this.utils.isCurrentOrParent("kbd"))return t.pre&&this.opts.preSpaces&&(e=e.replace(/\t/g,new Array(this.opts.preSpaces+1).join(" "))),WCF.String.escapeHTML(e);var r=elCreate("div");r.innerHTML=e.replace(/@@@WOLTLAB-P-ALIGN-(?:left|right|center|justify)@@@/g,"");var i,o,a,s=!0;for(o=0,a=r.childElementCount;o<a;o++){if(i=r.children[o],"DIV"!==i.nodeName||0===i.childNodes.length){s=!1;break}if(1===i.childNodes.length&&1===i.childElementCount){var c=i.children[0];if(0===c.childNodes.length&&"BR"!==c.nodeName){s=!1;break}}}if(s){var h=[];for(o=0,a=r.childElementCount;o<a;o++)h.push(r.children[o]);h.forEach(function(e){var t=elCreate("p");for(r.insertBefore(t,e);e.childNodes.length>0;)t.appendChild(e.childNodes[0]);r.removeChild(e)})}var d,p,f,u,m=null!==elBySel(".MsoNormal",r),g=elBySelAll("[style]",r);for(o=0,a=g.length;o<a;o++){i=g[o],p=[];for(var v=0,y=i.style.length;v<y;v++)if(d=i.style[v],-1===this.opts.woltlab.allowedInlineStyles.indexOf(d)){if("font-weight"===d&&"STRONG"!==i.nodeName)u=i.style.getPropertyValue(d),"bold"!==u&&"bolder"!==u||(u=600),(u=~~u)>500&&(f=elCreate("strong"),i.parentNode.insertBefore(f,i),f.appendChild(i));else if(m&&"margin-bottom"===d&&"P"===i.nodeName&&(u=i.style.getPropertyValue(d),u.match(/^12(?:\.0)?pt$/))){var b=elCreate("p");b.innerHTML="<br>",i.parentNode.insertBefore(b,i.nextSibling)}p.push(d)}p.forEach(function(e){i.style.removeProperty(e)})}return elBySelAll("span",r,function(e){if(!e.classList.contains("redactor-selection-marker"))if(e.hasAttribute("style")&&e.style.length)for(var t=e.style.getPropertyValue("color"),n=e.style.getPropertyValue("font-family"),l=e.style.getPropertyValue("font-size"),r=(t?1:0)+(n?1:0)+(l?1:0);r>1;){var i=elCreate("span");t?(i.style.setProperty("color",t,""),e.style.removeProperty("color"),t="",r--):n?(i.style.setProperty("font-family",n,""),e.style.removeProperty("font-family"),n="",r--):l&&(i.style.setProperty("font-size",l,""),e.style.removeProperty("font-size"),l="",r--),e.parentNode.insertBefore(i,e),i.appendChild(e)}else{for(;e.childNodes.length;)e.parentNode.insertBefore(e.childNodes[0],e);elRemove(e)}}),elBySelAll("p",r,function(e){e.classList.contains("MsoNormal")?1===e.childElementCount&&"O:P"===e.children[0].nodeName&&" "===e.textContent&&(e.innerHTML="<br>"):e.className.match(/\btext-(left|right|center|justify)\b/)&&e.insertBefore(document.createTextNode("@@@WOLTLAB-P-ALIGN-"+RegExp.$1+"@@@"),e.firstChild),e.removeAttribute("class"),e.removeAttribute("style")}),elBySelAll("img",r,function(e){e.removeAttribute("style")}),elBySelAll("br",r,function(e){e.parentNode.insertBefore(document.createTextNode("@@@WOLTLAB-BR-MARKER@@@"),e.nextSibling)}),elBySelAll("kbd",r,function(e){for(e.insertBefore(document.createTextNode("[tt]"),e.firstChild),e.appendChild(document.createTextNode("[/tt]"));e.childNodes.length;)e.parentNode.insertBefore(e.childNodes[0],e);elRemove(e)}),e=l.call(this,r.innerHTML,t,n),e=e.replace(/\n*@@@WOLTLAB-BR-MARKER@@@\n*/g,"<woltlab-br-marker></woltlab-br-marker>"),e=e.replace(/(<p>)?\s*@@@WOLTLAB-P-ALIGN-(left|right|center|justify)@@@/g,function(e,t,n){return t?'<p class="text-'+n+'">':""}),r.innerHTML=e.replace(/&quot;/g,"""),elBySelAll("woltlab-br-marker",r,function(e){var t=e.parentNode;if(null!==t){if("P"===t.nodeName){var n=elCreate("p");n.innerHTML="<br>";var l=!1,r=e.nextSibling;r&&"WOLTLAB-BR-MARKER"===r.nodeName&&(l=!0);for(var i=!l;e.nextSibling;)i&&0!==e.nextSibling.textContent.replace(/\u200B/g,"").trim().length&&(i=!1),n.appendChild(e.nextSibling);i||elRemove(n.firstElementChild);var o=e.previousSibling;o&&"BR"===o.nodeName&&elRemove(o),t.parentNode.insertBefore(n,t.nextSibling),l&&(n=elCreate("p"),n.innerHTML="<br>",t.parentNode.insertBefore(n,t.nextSibling))}else t.insertBefore(elCreate("br"),e);elRemove(e)}}),elBySelAll("p",r,function(e){var t=!1;0===e.childNodes.length?t=!0:""===e.textContent?(t=!0,elBySelAll("*",e,function(e){"SPAN"!==e.nodeName&&(t=!1)})):0===e.textContent.trim().length&&(elBySelAll("span",e,function(e){if(!e.hasAttribute("style")||!e.style.length){for(;e.childNodes.length;)e.parentNode.insertBefore(e.childNodes[0],e);elRemove(e)}}),0===e.children.length&&(e.innerHTML="<br>")),t&&elRemove(e)}),r.innerHTML}.bind(this);var r=[],i=function(e,t){for(var n,l,i={},o=0,a=t.length;o<a;o++)n=t[o],l=elAttr(e,n),"style"===n&&0===e.style.length&&0===l.indexOf("font-family")&&(l=l.replace(/"/g,"")),i[n]=l;r.push({element:e,attributes:i})},o=this.clean.convertTags;this.clean.convertTags=function(e,t){var n=elCreate("div");n.innerHTML=e,r=[],WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","convertTags_"+this.$element[0].id,{addToStorage:i,div:n}),elBySelAll("span",n,function(e){i(e,["style"])}),r.forEach(function(e,t){var n=e.element,l=n.parentNode;for(l.insertBefore(document.createTextNode("###custom"+t+"###"),n),l.insertBefore(document.createTextNode("###/custom"+t+"###"),n.nextSibling);n.childNodes.length;)l.insertBefore(n.childNodes[0],n);l.removeChild(n)});var l=!1;t.links&&this.opts.pasteLinks&&(elBySelAll("a",n,function(e){e.href&&(e.outerHTML='#@###[a href="'+e.href+'"]###@#'+e.innerHTML+"#@###[/a]###@#")}),l=!0,t.links=!1);var a=!1;return t.images&&this.opts.pasteImages&&(elBySelAll("img",n,function(e){if(e.src){for(var t,n='#####[img src="'+e.src+'"',l=0,r=e.attributes.length;l<r;l++)t=e.attributes.item(l),"src"!==t.name&&(n+=" "+t.name+'="'+t.value+'"');e.outerHTML=n+"]#####"}}),a=!0,t.images=!1),e=o.call(this,n.innerHTML,t),a&&(t.images=!0),l&&(t.links=!0),e}.bind(this);var a=this.clean.reconvertTags;this.clean.reconvertTags=function(e,t){if(r.length){e=e.replace(/###(\/?)custom(\d+)###/g,'<$1woltlab-custom-tag data-index="$2">');var n=elCreate("div");n.innerHTML=e,elBySelAll("woltlab-custom-tag",n,function(e){var t=~~elData(e,"index");if(r[t]){var n=r[t],l=elCreate(n.element.nodeName);for(var i in n.attributes)n.attributes.hasOwnProperty(i)&&elAttr(l,i,n.attributes[i]);for(e.parentNode.insertBefore(l,e);e.childNodes.length;)l.appendChild(e.childNodes[0])}elRemove(e)}),e=n.innerHTML}return(t.links&&this.opts.pasteLinks||t.images&&this.opts.pasteImages)&&(e=e.replace(new RegExp("#@###\\[","gi"),"<"),e=e.replace(new RegExp("\\]###@#","gi"),">")),a.call(this,e,t)}.bind(this),this.clean.removeSpans=function(e){return e};var s=this.clean.getCurrentType;this.clean.getCurrentType=function(e,t){var n=s.call(this,e,t);return this.utils.isCurrentOrParent(["kbd"])&&(n.inline=!1,n.block=!1,n.encode=!0,n.pre=!0,n.paragraphize=!1,n.images=!1,n.links=!1),n}.bind(this);this.clean.removeEmptyInlineTags;this.clean.removeEmptyInlineTags=function(e){var t=this.opts.inlineTags,n=$("<div/>").html($.parseHTML(e,document,!0)),l=this,r=n.find("span"),i=n.find(t.join(","));return i.filter(":not(span)").removeAttr("style"),i.each(function(){var e=$(this).html();0===this.attributes.length&&l.utils.isEmpty(e)&&$(this).replaceWith(function(){return $(this).contents()})}),r.each(function(){$(this).html();0===this.attributes.length&&$(this).replaceWith(function(){return $(this).contents()})}),e=n.html(),e=e.replace("\x3c!--?php","<?php"),e=e.replace("\x3c!--?","<?"),e=e.replace("?--\x3e","?>"),n.remove(),e}.bind(this)},removeRedundantStyles:function(){var e=[],t=["del","em","strong","sub","sup","u"];elBySelAll(t.join(","),this.$editor[0],function(t){null!==elBySel(t.nodeName,t)&&e.push(t)}),elBySelAll("span[style]",this.$editor[0],function(t){["color","font-family","font-size"].forEach(function(n){var l=t.style.getPropertyValue(n);l&&window.getComputedStyle(t.parentNode).getPropertyValue(n)===l&&e.push(t)})});var n;e.forEach(function(e){for(n=e.parentNode;e.childNodes.length;)n.insertBefore(e.childNodes[0],e);n.removeChild(e)})}}}; })(this);
// plugins/WoltLabCode.js
(function (window, undefined) { $.Redactor.prototype.WoltLabCode=function(){"use strict";return{init:function(){require(["WoltLabSuite/Core/Ui/Redactor/Code"],function(t){new t(this)}.bind(this));var t=this.code.start;this.code.start=function(e){t.call(this,e),WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","codeStart_"+this.$element[0].id),elBySelAll("kbd",this.$editor[0],function(t){var e=t.nextSibling;if(!e||e.nodeType!==Node.TEXT_NODE||""!==e.textContent.substr(0,1)){var i=document.createTextNode("");t.parentNode.insertBefore(i,e)}})}.bind(this);var e=this.code.set;this.code.set=function(t,i){e.call(this,t,i),this.utils.isEmpty()&&this.observe.toolbar()}.bind(this);var i=this.code.get;this.code.get=function(){return this.code.html=!1,this.code.startSync(this.core.editor().html()),i.call(this)}.bind(this)}}}; })(this);
// plugins/WoltLabDragAndDrop.js
(function (window, undefined) { $.Redactor.prototype.WoltLabDragAndDrop=function(){"use strict";return{init:function(){(this.opts.woltlab.attachments||this.opts.woltlab.media)&&require(["WoltLabSuite/Core/Ui/Redactor/DragAndDrop"],function(t){t.init(this)}.bind(this))}}}; })(this);
-// plugins/WoltLabDropdown.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabDropdown=function(){"use strict";return{init:function(){this.utils.disableBodyScroll=function(){},this.utils.enableBodyScroll=function(){},this.WoltLabDropdown._hideAll(),this.WoltLabDropdown._show(),this.dropdown.build=function(o,d,t){t=this.dropdown.buildFormatting(o,t);var i,n=document.createDocumentFragment();for(var e in t)if(t.hasOwnProperty(e)){i=t[e];var r=this.dropdown.buildItem(e,i);this.observe.addDropdown($(r),e,i),n.appendChild(r)}for(var a=!1,l=0,s=n.childNodes.length;l<s;l++)if(n.childNodes[l].nodeType===Node.ELEMENT_NODE){a=!0;break}a&&(d[0].rel=o,d[0].appendChild(n))}.bind(this),this.dropdown.buildItem=function(o,d){var t=elCreate("li");return void 0!==d.classname&&t.classList.add(d.classname),0===o.toLowerCase().indexOf("divider")?(t.classList.add("redactor-dropdown-divider"),t):(t.innerHTML='<a href="#" class="redactor-dropdown-'+o+'" role="button"><span>'+d.title+"</span></a>",t.children[0].addEventListener("mousedown",function(t){t.preventDefault(),this.dropdown.buildClick(t,o,d)}.bind(this)),t)}.bind(this)},_hideAll:function(){var o=this.dropdown.hideAll;this.dropdown.hideAll=function(d,t){o.call(this,d,t),$(".redactor-dropdown-"+this.uuid).stop(!0,!0).hide()}.bind(this)},_show:function(){var o=this.dropdown.show;this.dropdown.show=function(d,t){var i=this.button.get(t),n=i.data("dropdown");if(!elDataBool(n[0],"woltlab")){var e=elCreate("ul");for(e.className="dropdownMenu";n[0].childElementCount;)e.appendChild(n[0].children[0]);n[0].appendChild(e),elData(n[0],"woltlab",!0)}var r=i.hasClass("dropact");o.call(this,d,t),r||n.stop(!0).show()}.bind(this)}}}; })(this);
-
// plugins/WoltLabEvent.js
(function (window, undefined) { $.Redactor.prototype.WoltLabEvent=function(){"use strict";var t=0;return{init:function(){this._callbacks=[],this._elementId=this.$element[0].id,elData(this.$editor[0],"element-id",this._elementId),require(["EventHandler"],function(t){this.WoltLabEvent._setEvents(t),t.add("com.woltlab.wcf.redactor2","destroy_"+this._elementId,function(){t.removeAllBySuffix("com.woltlab.wcf.redactor2",this._elementId)}.bind(this))}.bind(this));var e=window.navigator.userAgent.toLowerCase();-1===e.indexOf("windows phone")&&-1===e.indexOf("edge/")&&(this.$editor[0].addEventListener("focus",function(){t++,document.documentElement.classList.add("redactorActive")}),this.$editor[0].addEventListener("blur",function(){t--,window.setTimeout(function(){0===t&&document.documentElement.classList.remove("redactorActive")},100)})),this.events.iterateObserver=function(t){var e=!1;(("textarea"===this.opts.type||"div"===this.opts.type)&&!this.detect.isFirefox()&&t.target===this.core.editor()[0]&&"childList"===t.type&&!t.addedNodes.length&&!t.removedNodes.length||"class"===t.attributeName&&t.target===this.core.editor()[0]||"data-vivaldi-spatnav-clickable"===t.attributeName||"attributes"===t.type&&null===t.attributeName)&&(e=!0),e||(this.observe.load(),this.events.changeHandler())}.bind(this),this.events.observer.disconnect(),this.events.createObserver(),this.events.setupObserver()},_setEvents:function(t){var e=this.$element[0].id,i=this.observe.load;this.observe.load=function(){i.call(this),t.fire("com.woltlab.wcf.redactor2","observe_load_"+e,{editor:this.$editor[0]})}.bind(this),this.opts.callbacks.keyup=function(i){var n={cancel:!1,event:i};return t.fire("com.woltlab.wcf.redactor","keyup_"+e,n),!1===n.cancel},t.add("com.woltlab.wcf.redactor2","getText_"+e,function(t){t.message=this.code.get()}.bind(this)),t.add("com.woltlab.wcf.redactor2","reset_"+e,function(){this.code.set("")}.bind(this))},register:function(t,e){require(["EventHandler"],function(i){var n=this.uuid;-1===this._callbacks.indexOf(t)&&(this.opts.callbacks[t]=function(e){var s={cancel:!1,event:e,redactor:this};return i.fire("com.woltlab.wcf.redactor2",t+"_"+n+"_"+this.WoltLabEvent._elementId,s),!1===s.cancel}.bind(this),this._callbacks.push(t)),require(["EventHandler"],function(i){i.add("com.woltlab.wcf.redactor2",t+"_"+n+"_"+this.WoltLabEvent._elementId,e)}.bind(this))}.bind(this))}}}; })(this);
(function (window, undefined) { $.Redactor.prototype.WoltLabFont=function(){"use strict";return{_fonts:["Arial, Helvetica, sans-serif","Comic Sans MS, Marker Felt, cursive","Consolas, Courier New, Courier, monospace","Georgia, serif","Lucida Sans Unicode, Lucida Grande, sans-serif","Tahoma, Geneva, sans-serif","Times New Roman, Times, serif",'Trebuchet MS", Helvetica, sans-serif',"Verdana, Geneva, sans-serif"],init:function(){var t=this.WoltLabFont.setFont.bind(this),e={};this.WoltLabFont._fonts.forEach(function(o,i){e["fontFamily_"+i]={title:o.split(",")[0].replace(/['"]/g,""),func:t}}),e.removeFont={title:this.lang.get("remove-font"),func:this.WoltLabFont.removeFont.bind(this)};var o=this.button.add("woltlabFont","");this.button.addDropdown(o,e);var i=o.data("dropdown");i.find("a").each(function(t,e){e.className.match(/^redactor-dropdown-fontFamily_(\d+)$/)&&e.style.setProperty("font-family",this.WoltLabFont._fonts[RegExp.$1],"")}.bind(this)),$('<li class="dropdownDivider"></li>').insertBefore(i.children("li").last())},setFont:function(t){t=t.replace(/^fontFamily_/,""),this.selection.save(),require(["WoltLabSuite/Core/Ui/Redactor/Format"],function(e){this.buffer.set(),e.format(this.$editor[0],"font-family",this.WoltLabFont._fonts[t]),this.buffer.set()}.bind(this)),this.selection.restore()},removeFont:function(){this.selection.save(),require(["WoltLabSuite/Core/Ui/Redactor/Format"],function(t){this.buffer.set(),t.removeFormat(this.$editor[0],"font-family"),this.buffer.set()}.bind(this)),this.selection.restore()}}}; })(this);
// plugins/WoltLabFullscreen.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabFullscreen=function(){"use strict";var e,t=!1;return{init:function(){var o=this.button.add("woltlabFullscreen","");this.button.addCallback(o,this.WoltLabFullscreen._toggle.bind(this)),e=o[0],elHide(e.parentNode),require(["Ui/Screen"],function(o){o.on("screen-sm-up",{match:function(){elShow(e.parentNode)},unmatch:function(){elHide(e.parentNode),t&&this.WoltLabFullscreen._toggle()}.bind(this),setup:function(){elShow(e.parentNode)}})}.bind(this))},_toggle:function(){e.children[0].classList.toggle("fa-compress"),e.children[0].classList.toggle("fa-expand"),this.core.box()[0].classList.toggle("redactorBoxFullscreen")?(WCF.System.DisableScrolling.disable(),this.core.editor()[0].style.setProperty("height","calc(100% - "+~~this.core.toolbar()[0].clientHeight+"px)",""),t=!0):(WCF.System.DisableScrolling.enable(),this.core.editor()[0].style.removeProperty("height"),t=!1)}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabFullscreen=function(){"use strict";var e,t=!1;return{init:function(){var l=this.button.add("woltlabFullscreen","");this.button.addCallback(l,this.WoltLabFullscreen._toggle.bind(this)),e=l[0],elHide(e.parentNode),require(["Ui/Screen"],function(l){l.on("screen-sm-up",{match:function(){elShow(e.parentNode)},unmatch:function(){elHide(e.parentNode),t&&this.WoltLabFullscreen._toggle()}.bind(this),setup:function(){elShow(e.parentNode)}})}.bind(this))},_toggle:function(){e.children[0].classList.toggle("fa-compress"),e.children[0].classList.toggle("fa-expand"),this.core.box()[0].classList.toggle("redactorBoxFullscreen")?(WCF.System.DisableScrolling.disable(),t=!0):(WCF.System.DisableScrolling.enable(),t=!1)}}}; })(this);
// plugins/WoltLabHtml.js
(function (window, undefined) { $.Redactor.prototype.WoltLabHtml=function(){"use strict";return{init:function(){require(["WoltLabSuite/Core/Ui/Redactor/Html"],function(t){new t(this)}.bind(this))}}}; })(this);
// plugins/WoltLabImage.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabImage=function(){"use strict";return{init:function(){if(this.opts.woltlab.allowImages){var t=this.button.add("woltlabImage","");this.button.addCallback(t,this.WoltLabImage.add)}var e=this.image.showEdit;this.image.showEdit=function(t){var a=t[0];if(!a.classList.contains("smiley")){e(t),this.modal.setTitle(WCF.Language.get("wcf.editor.image.edit")),this.modal.getActionButton().text(WCF.Language.get("wcf.global.button.save")),this.modal.getDeleteButton().text(WCF.Language.get("wcf.global.button.delete")),elById("redactor-image-source").value=a.src;var i=elById("redactor-image-float");a.classList.contains("messageFloatObjectLeft")?i.value="left":a.classList.contains("messageFloatObjectRight")&&(i.value="right"),a.classList.contains("woltlabAttachment")&&elRemove(elById("redactor-image-source-container"))}}.bind(this);var a=this.image.update;this.image.update=function(){var t=this.observe.image[0],e=elById("redactor-image-source"),i=function(t,e){$('<small class="innerError" />').text(e).insertAfter(t)};if(!t.classList.contains("woltlabAttachment")){var r=e.value.trim();if(""===r)return i(e,WCF.Language.get("wcf.global.form.error.empty"));if(!r.match(this.opts.regexps.url))return i(e,WCF.Language.get("wcf.editor.image.source.error.invalid"));if(this.opts.woltlab.forceSecureImages&&0===r.indexOf("http://"))return i(e,WCF.Language.get("wcf.editor.image.source.error.insecure"));t.src=r}t.classList.remove("messageFloatObjectLeft"),t.classList.remove("messageFloatObjectRight");var o=elById("redactor-image-float").value;"left"!==o&&"right"!==o||t.classList.add("messageFloatObject"+WCF.String.ucfirst(o)),a.call(this),t.removeAttribute("alt"),t.removeAttribute("title"),this.caret.after(t)}.bind(this),this.opts.modal["image-edit"]='<div class="section"><dl id="redactor-image-source-container"><dt><label for="redactor-image-source">'+WCF.Language.get("wcf.editor.image.source")+'</label></dt><dd><input type="text" id="redactor-image-source" class="long"></dd></dl><dl><dt><label for="redactor-image-link">'+WCF.Language.get("wcf.editor.image.link")+'</label></dt><dd><input type="text" id="redactor-image-link" class="long"></dd></dl><dl><dt><label for="redactor-image-float">'+WCF.Language.get("wcf.editor.image.float")+'</label></dt><dd><select id="redactor-image-float"><option value="none">'+WCF.Language.get("wcf.global.noSelection")+'</option><option value="left">'+WCF.Language.get("wcf.editor.image.float.left")+'</option><option value="right">'+WCF.Language.get("wcf.editor.image.float.right")+'</option></select></dd></dl><input id="redactor-image-title" style="display: none"><input id="redactor-image-caption" style="display: none"><div class="formSubmit"><button id="redactor-modal-button-action" class="buttonPrimary">Insert</button><button id="redactor-modal-button-delete" class="redactor-modal-button-offset">Delete</button></div></div>'},add:function(){this.modal.load("image-edit",WCF.Language.get("wcf.editor.image.insert")),this.modal.show(),this.modal.getDeleteButton().hide();var t=this.modal.getActionButton()[0];t.addEventListener(WCF_CLICK_EVENT,this.WoltLabImage.insert),t.textContent=WCF.Language.get("wcf.global.button.insert"),this.WoltLabModal.rebuild()},insert:function(t){t.preventDefault(),this.modal.getModal().find(".innerError").remove();var e=elById("redactor-image-source"),a=function(t,e){$('<small class="innerError" />').text(e).insertAfter(t)},i=e.value.trim();if(""===i)return a(e,WCF.Language.get("wcf.global.form.error.empty"));if(!i.match(this.opts.regexps.url))return a(e,WCF.Language.get("wcf.editor.image.source.error.invalid"));if(this.opts.woltlab.forceSecureImages&&0===i.indexOf("http://"))return a(e,WCF.Language.get("wcf.editor.image.source.error.insecure"));var r=elById("redactor-image-link"),o=r.value.trim();if(""!==o&&!o.match(this.opts.regexps.url))return a(r,WCF.Language.get("wcf.editor.image.link.error.invalid"));var l=elById("redactor-image-float").value,s="";"left"!==l&&"right"!==l||(s="messageFloatObject"+WCF.String.ucfirst(l));var n,d='<img src="'+WCF.String.escapeHTML(i)+'"'+(s?' class="'+s+'"':"")+">";o&&(n=WCF.getUUID(),d='<a href="'+WCF.String.escapeHTML(o)+'" data-uuid="'+n+'">'+d+"</a>"),this.modal.close(),this.buffer.set(),this.insert.html(d),n&&window.setTimeout(function(){var t=elBySel('a[data-uuid="'+n+'"]',this.core.editor()[0]);t&&(t.removeAttribute("data-uuid"),this.caret.after(t))}.bind(this),1)}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabImage=function(){"use strict";return{init:function(){if(this.opts.woltlab.allowImages){var t=this.button.add("woltlabImage","");this.button.addCallback(t,this.WoltLabImage.add)}var e=this.image.showEdit;this.image.showEdit=function(t){var a=t[0];if(!a.classList.contains("smiley")){e(t),this.modal.setTitle(WCF.Language.get("wcf.editor.image.edit")),this.modal.getActionButton().text(WCF.Language.get("wcf.global.button.save")),this.modal.getDeleteButton().text(WCF.Language.get("wcf.global.button.delete")),elById("redactor-image-source").value=a.src;var i=elById("redactor-image-float");a.classList.contains("messageFloatObjectLeft")?i.value="left":a.classList.contains("messageFloatObjectRight")&&(i.value="right"),a.classList.contains("woltlabAttachment")&&elRemove(elById("redactor-image-source-container"))}}.bind(this);var a=this.image.update;this.image.update=function(){var t=this.observe.image[0],e=elById("redactor-image-source"),i=function(t,e){$('<small class="innerError" />').text(e).insertAfter(t)};if(!t.classList.contains("woltlabAttachment")){var r=e.value.trim();if(""===r)return i(e,WCF.Language.get("wcf.global.form.error.empty"));if(!r.match(this.opts.regexps.url))return i(e,WCF.Language.get("wcf.editor.image.source.error.invalid"));if(this.opts.woltlab.forceSecureImages&&0===r.indexOf("http://"))return i(e,WCF.Language.get("wcf.editor.image.source.error.insecure"));if(!this.WoltLabImage.isValidSource(r))return i(e,WCF.Language.get("wcf.editor.image.source.error.blocked"));t.src=r}t.classList.remove("messageFloatObjectLeft"),t.classList.remove("messageFloatObjectRight");var o=elById("redactor-image-float").value;"left"!==o&&"right"!==o||t.classList.add("messageFloatObject"+WCF.String.ucfirst(o)),a.call(this),t.removeAttribute("alt"),t.removeAttribute("title"),this.caret.after(t)}.bind(this),this.opts.modal["image-edit"]='<div class="section"><dl id="redactor-image-source-container"><dt><label for="redactor-image-source">'+WCF.Language.get("wcf.editor.image.source")+'</label></dt><dd><input type="text" id="redactor-image-source" class="long"></dd></dl><dl><dt><label for="redactor-image-link">'+WCF.Language.get("wcf.editor.image.link")+'</label></dt><dd><input type="text" id="redactor-image-link" class="long"></dd></dl><dl><dt><label for="redactor-image-float">'+WCF.Language.get("wcf.editor.image.float")+'</label></dt><dd><select id="redactor-image-float"><option value="none">'+WCF.Language.get("wcf.global.noSelection")+'</option><option value="left">'+WCF.Language.get("wcf.editor.image.float.left")+'</option><option value="right">'+WCF.Language.get("wcf.editor.image.float.right")+'</option></select></dd></dl><input id="redactor-image-title" style="display: none"><input id="redactor-image-caption" style="display: none"><div class="formSubmit"><button id="redactor-modal-button-action" class="buttonPrimary">Insert</button><button id="redactor-modal-button-delete" class="redactor-modal-button-offset">Delete</button></div></div>'},add:function(){this.modal.load("image-edit",WCF.Language.get("wcf.editor.image.insert")),this.modal.show(),this.modal.getDeleteButton().hide();var t=this.modal.getActionButton()[0];t.addEventListener(WCF_CLICK_EVENT,this.WoltLabImage.insert),t.textContent=WCF.Language.get("wcf.global.button.insert"),this.WoltLabModal.rebuild()},insert:function(t){t.preventDefault(),this.modal.getModal().find(".innerError").remove();var e=elById("redactor-image-source"),a=function(t,e){$('<small class="innerError" />').text(e).insertAfter(t)},i=e.value.trim();if(""===i)return a(e,WCF.Language.get("wcf.global.form.error.empty"));if(!i.match(this.opts.regexps.url))return a(e,WCF.Language.get("wcf.editor.image.source.error.invalid"));if(this.opts.woltlab.forceSecureImages&&0===i.indexOf("http://"))return a(e,WCF.Language.get("wcf.editor.image.source.error.insecure"));if(!this.WoltLabImage.isValidSource(i))return a(e,WCF.Language.get("wcf.editor.image.source.error.blocked"));var r=elById("redactor-image-link"),o=r.value.trim();if(""!==o&&!o.match(this.opts.regexps.url))return a(r,WCF.Language.get("wcf.editor.image.link.error.invalid"));var l=elById("redactor-image-float").value,s="";"left"!==l&&"right"!==l||(s="messageFloatObject"+WCF.String.ucfirst(l));var n,d='<img src="'+WCF.String.escapeHTML(i)+'"'+(s?' class="'+s+'"':"")+">";o&&(n=WCF.getUUID(),d='<a href="'+WCF.String.escapeHTML(o)+'" data-uuid="'+n+'">'+d+"</a>"),this.modal.close(),this.buffer.set(),this.insert.html(d),n&&window.setTimeout(function(){var t=elBySel('a[data-uuid="'+n+'"]',this.core.editor()[0]);t&&(t.removeAttribute("data-uuid"),this.caret.after(t))}.bind(this),1)},isValidSource:function(t){var e=elCreate("a");if(e.href=t,""===e.hostname)return!0;if(this.opts.woltlab.images.secureOnly&&"https:"!==e.protocol)return!1;if(!this.opts.woltlab.images.external){var a=!1,i=[];if(this.opts.woltlab.images.whitelist.forEach(function(t){0===t.indexOf("*.")?i.push(t.replace(/^\*\./,"")):e.hostname===t&&(a=!0)}),a||i.forEach(function(t){e.hostname.substr(-1*t.length)===t&&(a=!0)}),!a)return!1}return!0},validateImages:function(){elBySelAll("img:not(.smiley):not(.woltlabAttachment)",this.core.editor()[0],function(t){this.WoltLabImage.isValidSource(t.src)||t.classList.add("editorImageBlocked")}.bind(this))}}}; })(this);
// plugins/WoltLabIndent.js
(function (window, undefined) { $.Redactor.prototype.WoltLabIndent=function(){"use strict";return{init:function(){if(this.detect.isFirefox()){var e,t,i=this.indent.decrease;this.indent.decrease=function(){if(this.list.get()){var n=$(this.selection.current()).closest("li",this.core.editor()[0]);if(0===n.closest("ul, ol",this.core.editor()[0]).parent().closest("ul, ol",this.core.editor()[0]).length){var r=n[0];if(null!==elBySel("ul, ol",r)){this.buffer.set(),this.selection.save();var l,o,s=r.previousElementSibling;if(null!==s){for(l=elCreate(r.parentNode.nodeName.toLowerCase());s.nextSibling;)l.appendChild(s.nextSibling);o=s.parentNode,o.parentNode.insertBefore(l,o.nextSibling)}if(null!==r.nextElementSibling){for(l=elCreate(r.parentNode.nodeName.toLowerCase());r.nextSibling;)l.appendChild(r.nextSibling);o=r.parentNode,o.parentNode.insertBefore(l,o.nextSibling)}for(o=r.parentNode;r.childNodes.length;)o.parentNode.insertBefore(r.childNodes[0],o);return elRemove(o),void this.selection.restore()}}else{elBySelAll("woltlab-list-marker",this.core.editor()[0],elRemove),this.selection.save(),e=elCreate("woltlab-list-marker"),n[0].insertBefore(e,n[0].firstChild);var a=n[0].lastElementChild;if("BR"===a.nodeName){for(var d="",s=a;s=s.nextSibling;)d+=s.textContent;""===d.replace(/\u200B/g,"").trim()&&elRemove(a)}t=elCreate("woltlab-list-marker"),n[0].appendChild(t),this.selection.restore()}i.call(this)}}.bind(this);var n=this.indent.removeEmpty;this.indent.removeEmpty=function(){if(e&&e.parentNode){for(var i,r=elCreate("li");(i=e.nextSibling)&&i!==t;)r.appendChild(i);e.parentNode.insertBefore(r,e),elBySelAll("woltlab-list-marker",this.core.editor()[0],elRemove),e=void 0,t=void 0}n.call(this)}.bind(this)}this.indent.repositionItem=function(e){var t=e.next();0===t.length||"UL"===t[0].tagName&&"OL"===t[0].tagName||e.append(t);var i=e.prev();if(0!==i.length&&"LI"!==i[0].tagName){this.selection.save();e.closest("li",this.core.editor()[0]).after(e),this.selection.restore()}}.bind(this),this.indent.normalize=function(){if(this.detect.isFirefox()){var e=this.selection.block();if(e&&"P"===e.nodeName){var t=e.previousElementSibling;t&&"BR"===t.nodeName&&elRemove(t)}}this.core.editor().find("li").each($.proxy(function(e,t){var i=$(t),n="";0!==this.opts.keepStyleAttr.length&&(n=","+this.opts.keepStyleAttr.join(",")),i.find(this.opts.inlineTags.join(",")).not("img"+n).not("span").removeAttr("style");var r=i.parent();if(0!==r.length&&"LI"===r[0].tagName){for(;t.nextSibling;)t.appendChild(t.nextSibling);return void r.after(i)}var l=i.next();0===l.length||"UL"!==l[0].tagName&&"OL"!==l[0].tagName||i.append(l)},this))}.bind(this)}}}; })(this);
(function (window, undefined) { $.Redactor.prototype.WoltLabInsert=function(){"use strict";return{init:function(){var e=this.opts.woltlab.placeholderCallback,t=this.insert.html;this.insert.html=function(o,r){e&&(e=e());var n=window.getSelection();if(n.rangeCount&&"IMG"===n.anchorNode.nodeName&&this.caret.after(n.anchorNode),this.placeholder.hide(),this.core.editor().focus(),this.detect.isFirefox()){var i=n.anchorNode.nodeType===Node.TEXT_NODE?n.anchorNode.parentNode:n.anchorNode;null===i.closest(".redactor-layer")&&(this.selection.restore(),i=n.anchorNode.nodeType===Node.TEXT_NODE?n.anchorNode.parentNode:n.anchorNode,null===i.closest(".redactor-layer")&&(this.WoltLabCaret.endOfEditor(),this.selection.save()))}var a=this.selection.block(),s=""===this.$editor[0].innerHTML.replace(/<\/?p>/g,"").replace(/<br>/g,"").replace(/\u200B/g,"").trim();if(t.call(this,o,r),s&&(a=this.$editor[0].firstElementChild),a&&"P"===a.nodeName&&a.nextElementSibling){var c=!1;0===a.childElementCount&&""===a.textContent.replace(/\u200B/g,"").trim()?c=!0:1===a.childElementCount&&"<br>"===a.innerHTML&&(c=!0),c&&elRemove(a)}n.rangeCount&&"IMG"===n.anchorNode.nodeName&&this.caret.after(n.anchorNode)}.bind(this);var o=this.insert.text;this.insert.text=function(t){e&&(e=e()),this.core.editor().focus(),this.selection.restore(),elClosest(window.getSelection().anchorNode,".redactor-layer")!==this.core.editor()[0]&&this.WoltLabCaret.endOfEditor(),o.call(this,t),this.selection.saveInstant()}.bind(this),this.insert.placeHtml=function(e){var t=!1;e.forEach(function(e){e instanceof Element&&e.classList.contains("woltlab-bbcode-marker")&&(t=!0)});var o=document.createElement("span");o.id="redactor-insert-marker",o=this.insert.node(o),$(o).before(e),t||(this.selection.restore(),this.caret.after(o)),$(o).remove()}.bind(this)}}}; })(this);
// plugins/WoltLabKeydown.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabKeydown=function(){"use strict";var e=[];return{init:function(){var e=window.getSelection(),t=this.keydown.init;this.keydown.init=function(i){var n;if(this.detect.isFirefox()&&e.isCollapsed&&i.which===this.keyCode.BACKSPACE&&(n=e.anchorNode,n.nodeType===Node.ELEMENT_NODE&&e.anchorOffset>0&&(n=n.childNodes[e.anchorOffset-1]),n.nodeType===Node.TEXT_NODE&&""===n.textContent)){for(var o=[],r=n;r=r.previousSibling;){if(r.nodeType===Node.ELEMENT_NODE){"IMG"!==r.nodeName&&(o=[]);break}if(r.nodeType===Node.TEXT_NODE){var l=r.textContent;if(""!==l&&""!==l){o=[];break}o.push(r)}}o.length&&o.forEach(elRemove)}if((i.originalEvent.which===this.keyCode.BACKSPACE||i.originalEvent.which===this.keyCode.DELETE)&&e.isCollapsed){var s=this.selection.block();if("P"===s.nodeName){if(this.list.combineAfterAndBefore(s))return void i.originalEvent.preventDefault();if(this.utils.isEmpty(s.innerHTML)&&s.previousElementSibling!==s.nextElementSibling){var a=null,d=null;return i.originalEvent.which===this.keyCode.BACKSPACE?null===s.previousElementSibling?d=s.nextElementSibling:a=s.previousElementSibling:null===s.nextElementSibling?a=s.previousElementSibling:d=s.nextElementSibling,elRemove(s),null===d?("OL"!==a.nodeName&&"UL"!==a.nodeName||(a=a.lastElementChild),this.caret.end(a)):("OL"!==d.nodeName&&"UL"!==d.nodeName||(d=d.firstElementChild),this.caret.start(d)),void i.originalEvent.preventDefault()}}else if(this.detect.isWebkit()&&"LI"===s.nodeName&&i.which===this.keyCode.DELETE){var h=e.anchorNode;if(h.nodeType===Node.TEXT_NODE&&h.textContent.length===e.anchorOffset&&h.parentNode.lastChild===h){var c=s.nextElementSibling;if(c&&"LI"===c.nodeName){for(this.buffer.set(),this.selection.save();c.childNodes.length;)s.appendChild(c.childNodes[0]);return elRemove(c),this.selection.restore(),void i.preventDefault()}}}}var f=null;if(i.which===this.keyCode.BACKSPACE&&this.detect.isFirefox()){var u=this.selection.block();if(u&&"P"===u.tagName&&this.utils.isStartOfElement(u)){var m=u.previousElementSibling;if(m&&("OL"===m.nodeName||"UL"===m.nodeName)){this.buffer.set(),this.selection.save();for(var v=m.lastElementChild;u.childNodes.length;)v.appendChild(u.childNodes[0]);return elRemove(u),this.selection.restore(),void i.preventDefault()}m&&"P"===m.nodeName&&null!==(f=m.lastElementChild)&&"BR"!==f.nodeName&&(f=null)}}if(!1===t.call(this,i)||i.originalEvent.defaultPrevented){if(null!==f&&this.detect.isFirefox()){var p=e.getRangeAt(0);1===p.startOffset&&p.startContainer.firstElementChild===f&&elRemove(f)}}else if(i=i.originalEvent,!(39!==i.which||i.ctrlKey||i.shiftKey||i.metaKey||i.altKey)){if(!e.isCollapsed)return;var g=e.anchorNode;if(g.nodeType!==Node.TEXT_NODE||e.getRangeAt(0).startOffset!==g.textContent.length)return;var y=g.parentNode;if("KBD"!==y.nodeName)return;var N=!0;for(n=y;n&&n!==this.core.editor()[0];){if(null!==n.nextSibling){for(;n.nextSibling&&n.nextSibling.nodeType===Node.TEXT_NODE&&0===n.nextSibling.textContent.length;)n.parentNode.removeChild(n.nextSibling);if(n.nextSibling&&"BR"!==n.nextSibling.nodeName||null!==n.nextSibling.nextSibling){N=!1;break}}n=n.parentNode}N&&y.parentNode.insertBefore(document.createTextNode(""),y.nextSibling)}}.bind(this);var i=window.navigator.userAgent.toLowerCase();-1!==i.indexOf("linux")&&-1!==i.indexOf("android")&&-1!==i.indexOf("chrome")&&(this.keydown.checkEvents=function(){this.core.addEvent(!1)}.bind(this)),this.core.editor().off("keydown.redactor"),this.core.editor().on("keydown.redactor",this.keydown.init.bind(this)),this.keydown.onArrowDown=function(){for(var e,t,i=this.WoltLabKeydown._getBlocks(),n=0;n<i.length;n++)if(t=i[n]){if(!this.utils.isEndOfElement(t))continue;if(null!==(e=t.nextElementSibling)&&"P"===e.nodeName)break;return void this.keydown.insertAfterLastElement(t)}}.bind(this),this.keydown.onArrowUp=function(){for(var e,t,i=this.WoltLabKeydown._getBlocks(),n=0;n<i.length;n++)if(t=i[n]){if(!this.utils.isStartOfElement())break;if(null!==(e=t.previousElementSibling)&&"P"!==e.nodeName){var o=$(this.opts.emptyHtml)[0];t.parentNode.insertBefore(o,t),this.caret.end(o);break}return void this.keydown.insertBeforeFirstElement(t)}}.bind(this);var n=function(e){if(!1===this.core.callback("enter",e))return e.preventDefault(),!1;if(this.keydown.blockquote&&!0===this.keydown.exitFromBlockquote(e))return!1;if(this.keydown.pre)return this.keydown.insertNewLine(e);if(this.keydown.blockquote||this.keydown.figcaption)return this.keydown.insertBreakLine(e);if(this.keydown.figure)setTimeout($.proxy(function(){this.keydown.replaceToParagraph("FIGURE")},this),1);else if(this.keydown.block){if(setTimeout($.proxy(function(){this.keydown.replaceToParagraph("DIV")},this),1),"LI"===this.keydown.block.tagName){var t=this.selection.current(),i=elClosest(t,"li");this.utils.isRedactorParent(i)&&this.utils.isEmpty(i.innerHTML)&&null===i.nextElementSibling&&(i.innerHTML="")}}else if(!this.keydown.block)return this.keydown.insertParagraph(e);return this.detect.isFirefox()&&this.utils.isInline(this.keydown.parent)?(this.keydown.insertBreakLine(e),void setTimeout(function(){for(var e=this.selection.block(),t=this.selection.inline();t&&t!==e;){if("A"===t.nodeName){var i=!1;if(0===t.childNodes.length?i=!0:""===t.textContent.replace(/\u200B/g,"").trim()&&(i=!0,elBySelAll("*",t,function(e){"SPAN"!==e.nodeName&&(i=!1)})),i){for(;t.childNodes.length;)t.parentNode.insertBefore(t.childNodes[0],t);elRemove(t);break}}t=t.parentNode}}.bind(this),1)):void 0}.bind(this);this.keydown.onEnter=function(e){var t=this.keydown.blockquote;t&&(this.keydown.blockquote=!1);var i=n.call(this,e);return t&&(this.keydown.blockquote=t),i}.bind(this),this.keydown.replaceToParagraph=function(e){var t=this.selection.block(),i=t.innerHTML.replace(/<br\s?\/?>/gi,"");if(t.tagName===e&&this.utils.isEmpty(i)&&!$(t).hasClass("redactor-in")){var n=document.createElement("p");$(t).replaceWith(n);var o=document.createRange();o.setStart(n,0);var r=document.createTextNode("");o.insertNode(r),o.setStartAfter(r),o.collapse(!0);var l=window.getSelection();return l.removeAllRanges(),l.addRange(o),!1}"P"===t.tagName&&$(t).removeAttr("style")}.bind(this),this.keydown.onShiftEnter=function(e){return this.buffer.set(),this.keydown.pre?this.keydown.insertNewLine(e):this.insert.raw("<br>")}.bind(this);var o=this.keydown.onTab;this.keydown.onTab=function(t,i){if(!this.keydown.pre){var n=$(this.selection.current()).closest("ul, ol, td",this.core.editor()[0]);if(0===n.length)return!0;if(n=n[0],"TD"===n.nodeName){var r=null;if(t.originalEvent.shiftKey?null===(r=n.previousElementSibling)&&null!==(r=n.parentNode.previousElementSibling)&&(r=r.lastElementChild):null===(r=n.nextElementSibling)&&(r=n.parentNode.nextElementSibling,null===r&&(this.table.addRowBelow(),r=n.parentNode.nextElementSibling),r=r.firstElementChild),null!==r)if(this.utils.isEmpty(r.innerHTML))this.caret.end(r);else{var l=document.createRange();l.selectNodeContents(r),e.removeAllRanges(),e.addRange(l)}return t.originalEvent.preventDefault(),!1}}return o.call(this,t,i)}.bind(this);var r=this.keydown.formatEmpty;this.keydown.formatEmpty=function(e){for(var t,i=this.$editor[0],n=0,o=i.childElementCount;n<o;n++)if(t=i.children[n],"P"!==t.nodeName&&this.utils.isBlockTag(t.nodeName))return;return r.call(this,e)}.bind(this);var l=this.keydown.removeInvisibleSpace;this.keydown.removeInvisibleSpace=function(){this.keydown.current!==this.$editor[0]&&l.call(this)}.bind(this),require(["Core","Environment"],function(e,t){if("desktop"===t.platform()){var i=this.$editor[0].closest("form, .message");if(null!==i){var n=elBySel(".formSubmit",i);if(null!==n){var o=elBySel('input[type="submit"], button[data-type="save"], button[accesskey="s"]',n);o&&(o.removeAttribute("accesskey"),this.WoltLabEvent.register("keydown",function(t){if(83===t.event.which){var i=!1;window.navigator.platform.match(/^Mac/)?t.event.ctrlKey&&t.event.altKey&&(i=!0):t.event.altKey&&!t.event.ctrlKey&&(i=!0),i&&(t.cancel=!0,"function"==typeof o.click?o.click():e.triggerEvent(o,WCF_CLICK_EVENT))}}.bind(this)))}}}}.bind(this)),this.WoltLabKeydown._handleBackspaceAndDelete()},register:function(t){-1===e.indexOf(t)&&e.push(t)},_getBlocks:function(){for(var t=[this.keydown.blockquote,this.keydown.pre,this.keydown.figcaption],i=0,n=e.length;i<n;i++)t.push(this.utils.isTag(this.keydown.current,e[i]));return t},_handleBackspaceAndDelete:function(){var e=function(e){return null===elBySel("img",e)&&""===e.textContent.replace(/\u200B/g,"").trim()},t=function(t){var i,n=this.selection.block();if(n)if("TD"===n.nodeName){var o=n.innerHTML;""===o?t.preventDefault():""===o&&(t.preventDefault(),n.innerHTML="")}else if(-1!==n.nodeName.indexOf("-")&&e(n))i=n.parentNode,i.insertBefore(this.marker.get(),n.nextSibling),elRemove(n),this.selection.restore();else if((i=n&&"P"===n.nodeName?n.parentNode:null)&&-1!==i.nodeName.indexOf("-")){var r=window.getSelection().getRangeAt(0),l=document.createRange();l.setStartBefore(n),l.setEnd(r.startContainer,r.startOffset);var s=l.cloneContents(),a=elCreate("div");if(a.appendChild(s),e(a)){t.preventDefault();var d=n.previousElementSibling,h=null;if(d)h=e(d);else for(i=n;(i=i.parentNode)&&i!==this.$editor[0];)if(d=i.previousElementSibling){h=!1;break}if(h)elRemove(d);else if(null!==h){var c=n.parentNode;if("P"===d.nodeName){d.appendChild(this.marker.get());for(var f;n.childNodes.length;){if(f=n.childNodes[0],"BR"===f.nodeName){elRemove(f);break}d.appendChild(f)}0===n.childNodes.length&&elRemove(n),this.selection.restore()}else d.appendChild(n),n.insertBefore(this.marker.get(),n.firstChild),this.selection.restore();e(c)&&elRemove(c)}else null===h&&(i=n.parentNode,e(i)&&elRemove(i))}}}.bind(this),i=function(t){var i,n=this.selection.block();if(-1!==n.nodeName.indexOf("-")&&e(n))i=n.parentNode,i.insertBefore(this.marker.get(),n.nextSibling),elRemove(n),this.selection.restore();else if((i=n&&"P"===n.nodeName?n.parentNode:null)&&-1!==i.nodeName.indexOf("-")){var o=window.getSelection().getRangeAt(0),r=document.createRange();r.setStart(o.startContainer,o.startOffset),r.setEndAfter(n);var l=r.cloneContents(),s=elCreate("div");if(s.appendChild(l),e(s)){t.preventDefault();var a=n.nextElementSibling,d=null;if(a)d=e(a);else for(i=n;(i=i.parentNode)&&i!==this.$editor[0];)if(a=i.nextElementSibling){d=!1;break}if(d)elRemove(a);else if(null!==d){var h=a.parentNode;if("P"===a.nodeName){for(;a.childNodes.length;)n.appendChild(a.childNodes[0]);elRemove(a)}else{if(n.appendChild(this.marker.get()),i=n.parentNode,-1!==a.nodeName.indexOf("-")){var c=a.firstElementChild;if(c&&"P"===c.nodeName){for(;c.childNodes.length;)n.appendChild(c.childNodes[0]);a.removeChild(c),e(a)&&elRemove(a)}}else i.insertBefore(a,n.nextSibling);this.selection.restore()}e(h)&&elRemove(h)}else null===d&&(i=n.parentNode,e(i)&&elRemove(i))}}}.bind(this),n=function(){return this.opts.blockTags.filter(function(e){return-1!==e.indexOf("-")}).join(",")}.bind(this),o=[],r=function(){elBySelAll(n(),this.core.editor()[0],function(e){null!==e.parentNode&&-1!==o.indexOf(e)&&["nextElementSibling","previousElementSibling"].forEach(function(t){for(var i,n=e[t];null!==n&&n.nodeName===e.nodeName&&-1===o.indexOf(n);){if("previousElementSibling"===t)for(var r=n.childNodes.length-1;r>=0;r--)e.insertBefore(n.childNodes[r],e.firstChild);else for(;n.childNodes.length>0;)e.appendChild(n.childNodes[0]);i=n[t],elRemove(n),n=i}})}),o=[]}.bind(this);this.keydown.onBackspaceAndDeleteAfter=function(e){this.detect.isFirefox()&&(this.selection.isCollapsed()?e.which===this.keyCode.BACKSPACE?t(e):e.which===this.keyCode.DELETE&&i(e):e.which!==this.keyCode.BACKSPACE&&e.which!==this.keyCode.DELETE||elBySelAll(n(),this.core.editor()[0],function(e){o.push(e)}));var l=!1;if(e.which===this.keyCode.BACKSPACE&&this.selection.isCollapsed()&&this.detect.isWebkit()){var s=this.selection.block();!1!==s&&"PRE"===s.nodeName&&(l=!0)}setTimeout($.proxy(function(){var t;if(o.length>0&&r(),l){var i=this.selection.block();if((!1===i||"PRE"!==i.nodeName)&&(t=this.selection.current(),t.nodeType===Node.TEXT_NODE&&t.nextSibling&&"SPAN"===t.nextSibling.nodeName)){var n=t.nextSibling;if(-1!==n.style.getPropertyValue("font-family").indexOf("monospace")&&"pre-wrap"===n.style.getPropertyValue("white-space")){for(;n.childNodes.length;)n.parentNode.insertBefore(n.childNodes[0],n);elRemove(n)}}}this.code.syncFire=!1,this.keydown.removeEmptyLists();var s="";0!==this.opts.keepStyleAttr.length&&(s=","+this.opts.keepStyleAttr.join(",")),this.core.editor().find("*[style]").not("span, img, figure, iframe, #redactor-image-box, #redactor-image-editter, [data-redactor-style-cache], [data-redactor-span]"+s).removeAttr("style"),this.keydown.formatEmpty(e),t=this.selection.current(),"KBD"===t.nodeName&&0===t.innerHTML.length&&elRemove(t),this.code.syncFire=!0},this),1)}.bind(this)}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabKeydown=function(){"use strict";var e=[];return{init:function(){var e=window.getSelection(),t=this.keydown.init;this.keydown.init=function(i){var n;if(this.detect.isFirefox()&&e.isCollapsed&&i.which===this.keyCode.BACKSPACE&&(n=e.anchorNode,n.nodeType===Node.ELEMENT_NODE&&e.anchorOffset>0&&(n=n.childNodes[e.anchorOffset-1]),n.nodeType===Node.TEXT_NODE&&""===n.textContent)){for(var o=[],r=n;r=r.previousSibling;){if(r.nodeType===Node.ELEMENT_NODE){"IMG"!==r.nodeName&&(o=[]);break}if(r.nodeType===Node.TEXT_NODE){var l=r.textContent;if(""!==l&&""!==l){o=[];break}o.push(r)}}o.length&&o.forEach(elRemove)}if((i.originalEvent.which===this.keyCode.BACKSPACE||i.originalEvent.which===this.keyCode.DELETE)&&e.isCollapsed){var s=this.selection.block();if("P"===s.nodeName){if(this.list.combineAfterAndBefore(s))return void i.originalEvent.preventDefault();if(this.utils.isEmpty(s.innerHTML)&&s.previousElementSibling!==s.nextElementSibling){var a=null,d=null;return i.originalEvent.which===this.keyCode.BACKSPACE?null===s.previousElementSibling?d=s.nextElementSibling:a=s.previousElementSibling:null===s.nextElementSibling?a=s.previousElementSibling:d=s.nextElementSibling,elRemove(s),null===d?("OL"!==a.nodeName&&"UL"!==a.nodeName||(a=a.lastElementChild),this.caret.end(a)):("OL"!==d.nodeName&&"UL"!==d.nodeName||(d=d.firstElementChild),this.caret.start(d)),void i.originalEvent.preventDefault()}}else{if(i.originalEvent.which===this.keyCode.DELETE&&-1!==["H1","H2","H3","H4","H5","H6"].indexOf(s.nodeName)&&this.utils.isEmpty(s.innerHTML)){var h=s.nextElementSibling;return h?h=s.nextElementSibling:(h=elCreate("p"),h.innerHTML=this.opts.invisibleSpace,s.parentNode.appendChild(h)),s.parentNode.removeChild(s),this.caret.start(h),void i.originalEvent.preventDefault()}if(this.detect.isWebkit()&&"LI"===s.nodeName&&i.which===this.keyCode.DELETE){var c=e.anchorNode;if(c.nodeType===Node.TEXT_NODE&&c.textContent.length===e.anchorOffset&&c.parentNode.lastChild===c){var f=s.nextElementSibling;if(f&&"LI"===f.nodeName){for(this.buffer.set(),this.selection.save();f.childNodes.length;)s.appendChild(f.childNodes[0]);return elRemove(f),this.selection.restore(),void i.preventDefault()}}}}}var u=null;if(i.which===this.keyCode.BACKSPACE&&this.detect.isFirefox()){var m=this.selection.block();if(m&&"P"===m.tagName&&this.utils.isStartOfElement(m)){var v=m.previousElementSibling;if(v&&("OL"===v.nodeName||"UL"===v.nodeName)){this.buffer.set(),this.selection.save();for(var p=v.lastElementChild;m.childNodes.length;)p.appendChild(m.childNodes[0]);return elRemove(m),this.selection.restore(),void i.preventDefault()}v&&"P"===v.nodeName&&null!==(u=v.lastElementChild)&&"BR"!==u.nodeName&&(u=null)}}if(!1===t.call(this,i)||i.originalEvent.defaultPrevented){if(null!==u&&this.detect.isFirefox()){var g=e.getRangeAt(0);1===g.startOffset&&g.startContainer.firstElementChild===u&&elRemove(u)}}else if(i=i.originalEvent,!(39!==i.which||i.ctrlKey||i.shiftKey||i.metaKey||i.altKey)){if(!e.isCollapsed)return;var y=e.anchorNode;if(y.nodeType!==Node.TEXT_NODE||e.getRangeAt(0).startOffset!==y.textContent.length)return;var E=y.parentNode;if("KBD"!==E.nodeName)return;var N=!0;for(n=E;n&&n!==this.core.editor()[0];){if(null!==n.nextSibling){for(;n.nextSibling&&n.nextSibling.nodeType===Node.TEXT_NODE&&0===n.nextSibling.textContent.length;)n.parentNode.removeChild(n.nextSibling);if(n.nextSibling&&"BR"!==n.nextSibling.nodeName||null!==n.nextSibling.nextSibling){N=!1;break}}n=n.parentNode}N&&E.parentNode.insertBefore(document.createTextNode(""),E.nextSibling)}}.bind(this);var i=window.navigator.userAgent.toLowerCase();-1!==i.indexOf("linux")&&-1!==i.indexOf("android")&&-1!==i.indexOf("chrome")&&(this.keydown.checkEvents=function(){this.core.addEvent(!1)}.bind(this)),this.core.editor().off("keydown.redactor"),this.core.editor().on("keydown.redactor",this.keydown.init.bind(this)),this.keydown.onArrowDown=function(){for(var e,t,i=this.WoltLabKeydown._getBlocks(),n=0;n<i.length;n++)if(t=i[n]){if(!this.utils.isEndOfElement(t))continue;if(null!==(e=t.nextElementSibling)&&"P"===e.nodeName)break;return void this.keydown.insertAfterLastElement(t)}}.bind(this),this.keydown.onArrowUp=function(){for(var e,t,i=this.WoltLabKeydown._getBlocks(),n=0;n<i.length;n++)if(t=i[n]){if(!this.utils.isStartOfElement())break;if(null!==(e=t.previousElementSibling)&&"P"!==e.nodeName){var o=$(this.opts.emptyHtml)[0];t.parentNode.insertBefore(o,t),this.caret.end(o);break}return void this.keydown.insertBeforeFirstElement(t)}}.bind(this);var n=function(e){if(!1===this.core.callback("enter",e))return e.preventDefault(),!1;if(this.keydown.blockquote&&!0===this.keydown.exitFromBlockquote(e))return!1;if(this.keydown.pre)return this.keydown.insertNewLine(e);if(this.keydown.blockquote||this.keydown.figcaption)return this.keydown.insertBreakLine(e);if(this.keydown.figure)setTimeout($.proxy(function(){this.keydown.replaceToParagraph("FIGURE")},this),1);else if(this.keydown.block){if(setTimeout($.proxy(function(){this.keydown.replaceToParagraph("DIV")},this),1),"LI"===this.keydown.block.tagName){var t=this.selection.current(),i=elClosest(t,"li");this.utils.isRedactorParent(i)&&this.utils.isEmpty(i.innerHTML)&&null===i.nextElementSibling&&(i.innerHTML="")}}else if(!this.keydown.block)return this.keydown.insertParagraph(e);return this.detect.isFirefox()&&this.utils.isInline(this.keydown.parent)?(this.keydown.insertBreakLine(e),void setTimeout(function(){for(var e=this.selection.block(),t=this.selection.inline();t&&t!==e;){if("A"===t.nodeName){var i=!1;if(0===t.childNodes.length?i=!0:""===t.textContent.replace(/\u200B/g,"").trim()&&(i=!0,elBySelAll("*",t,function(e){"SPAN"!==e.nodeName&&(i=!1)})),i){for(;t.childNodes.length;)t.parentNode.insertBefore(t.childNodes[0],t);elRemove(t);break}}t=t.parentNode}}.bind(this),1)):void 0}.bind(this);this.keydown.onEnter=function(e){var t=this.keydown.blockquote;t&&(this.keydown.blockquote=!1);var i=n.call(this,e);return t&&(this.keydown.blockquote=t),i}.bind(this),this.keydown.replaceToParagraph=function(e){var t=this.selection.block(),i=t.innerHTML.replace(/<br\s?\/?>/gi,"");if(t.tagName===e&&this.utils.isEmpty(i)&&!$(t).hasClass("redactor-in")){var n=document.createElement("p");$(t).replaceWith(n);var o=document.createRange();o.setStart(n,0);var r=document.createTextNode("");o.insertNode(r),o.setStartAfter(r),o.collapse(!0);var l=window.getSelection();return l.removeAllRanges(),l.addRange(o),!1}"P"===t.tagName&&$(t).removeAttr("style")}.bind(this),this.keydown.onShiftEnter=function(e){return this.buffer.set(),this.keydown.pre?this.keydown.insertNewLine(e):this.insert.raw("<br>")}.bind(this);var o=this.keydown.onTab;this.keydown.onTab=function(t,i){if(!this.keydown.pre){var n=$(this.selection.current()).closest("ul, ol, td",this.core.editor()[0]);if(0===n.length)return!0;if(n=n[0],"TD"===n.nodeName){var r=null;if(t.originalEvent.shiftKey?null===(r=n.previousElementSibling)&&null!==(r=n.parentNode.previousElementSibling)&&(r=r.lastElementChild):null===(r=n.nextElementSibling)&&(r=n.parentNode.nextElementSibling,null===r&&(this.table.addRowBelow(),r=n.parentNode.nextElementSibling),r=r.firstElementChild),null!==r)if(this.utils.isEmpty(r.innerHTML))this.caret.end(r);else{var l=document.createRange();l.selectNodeContents(r),e.removeAllRanges(),e.addRange(l)}return t.originalEvent.preventDefault(),!1}}return o.call(this,t,i)}.bind(this);var r=this.keydown.formatEmpty;this.keydown.formatEmpty=function(e){for(var t,i=this.$editor[0],n=0,o=i.childElementCount;n<o;n++)if(t=i.children[n],"P"!==t.nodeName&&this.utils.isBlockTag(t.nodeName))return;return r.call(this,e)}.bind(this);var l=this.keydown.removeInvisibleSpace;this.keydown.removeInvisibleSpace=function(){this.keydown.current!==this.$editor[0]&&l.call(this)}.bind(this),require(["Core","Environment"],function(e,t){if("desktop"===t.platform()){var i=this.$editor[0].closest("form, .message");if(null!==i){var n=elBySel(".formSubmit",i);if(null!==n){var o=elBySel('input[type="submit"], button[data-type="save"], button[accesskey="s"]',n);o&&(o.removeAttribute("accesskey"),this.WoltLabEvent.register("keydown",function(t){if(83===t.event.which){var i=!1;window.navigator.platform.match(/^Mac/)?t.event.ctrlKey&&t.event.altKey&&(i=!0):t.event.altKey&&!t.event.ctrlKey&&(i=!0),i&&(t.cancel=!0,"function"==typeof o.click?o.click():e.triggerEvent(o,WCF_CLICK_EVENT))}}.bind(this)))}}}}.bind(this)),this.WoltLabKeydown._handleBackspaceAndDelete()},register:function(t){-1===e.indexOf(t)&&e.push(t)},_getBlocks:function(){for(var t=[this.keydown.blockquote,this.keydown.pre,this.keydown.figcaption],i=0,n=e.length;i<n;i++)t.push(this.utils.isTag(this.keydown.current,e[i]));return t},_handleBackspaceAndDelete:function(){var e=function(e){return null===elBySel("img",e)&&""===e.textContent.replace(/\u200B/g,"").trim()},t=function(t){var i,n=this.selection.block();if(n)if("TD"===n.nodeName){var o=n.innerHTML;""===o?t.preventDefault():""===o&&(t.preventDefault(),n.innerHTML="")}else if(-1!==n.nodeName.indexOf("-")&&e(n))i=n.parentNode,i.insertBefore(this.marker.get(),n.nextSibling),elRemove(n),this.selection.restore();else if((i=n&&"P"===n.nodeName?n.parentNode:null)&&-1!==i.nodeName.indexOf("-")){var r=window.getSelection().getRangeAt(0),l=document.createRange();l.setStartBefore(n),l.setEnd(r.startContainer,r.startOffset);var s=l.cloneContents(),a=elCreate("div");if(a.appendChild(s),e(a)){t.preventDefault();var d=n.previousElementSibling,h=null;if(d)h=e(d);else for(i=n;(i=i.parentNode)&&i!==this.$editor[0];)if(d=i.previousElementSibling){h=!1;break}if(h)elRemove(d);else if(null!==h){var c=n.parentNode;if("P"===d.nodeName){d.appendChild(this.marker.get());for(var f;n.childNodes.length;){if(f=n.childNodes[0],"BR"===f.nodeName){elRemove(f);break}d.appendChild(f)}0===n.childNodes.length&&elRemove(n),this.selection.restore()}else d.appendChild(n),n.insertBefore(this.marker.get(),n.firstChild),this.selection.restore();e(c)&&elRemove(c)}else null===h&&(i=n.parentNode,e(i)&&elRemove(i))}}}.bind(this),i=function(t){var i,n=this.selection.block();if(-1!==n.nodeName.indexOf("-")&&e(n))i=n.parentNode,i.insertBefore(this.marker.get(),n.nextSibling),elRemove(n),this.selection.restore();else if((i=n&&"P"===n.nodeName?n.parentNode:null)&&-1!==i.nodeName.indexOf("-")){var o=window.getSelection().getRangeAt(0),r=document.createRange();r.setStart(o.startContainer,o.startOffset),r.setEndAfter(n);var l=r.cloneContents(),s=elCreate("div");if(s.appendChild(l),e(s)){t.preventDefault();var a=n.nextElementSibling,d=null;if(a)d=e(a);else for(i=n;(i=i.parentNode)&&i!==this.$editor[0];)if(a=i.nextElementSibling){d=!1;break}if(d)elRemove(a);else if(null!==d){var h=a.parentNode;if("P"===a.nodeName){for(;a.childNodes.length;)n.appendChild(a.childNodes[0]);elRemove(a)}else{if(n.appendChild(this.marker.get()),i=n.parentNode,-1!==a.nodeName.indexOf("-")){var c=a.firstElementChild;if(c&&"P"===c.nodeName){for(;c.childNodes.length;)n.appendChild(c.childNodes[0]);a.removeChild(c),e(a)&&elRemove(a)}}else i.insertBefore(a,n.nextSibling);this.selection.restore()}e(h)&&elRemove(h)}else null===d&&(i=n.parentNode,e(i)&&elRemove(i))}}}.bind(this),n=function(){return this.opts.blockTags.filter(function(e){return-1!==e.indexOf("-")}).join(",")}.bind(this),o=[],r=function(){elBySelAll(n(),this.core.editor()[0],function(e){null!==e.parentNode&&-1!==o.indexOf(e)&&["nextElementSibling","previousElementSibling"].forEach(function(t){for(var i,n=e[t];null!==n&&n.nodeName===e.nodeName&&-1===o.indexOf(n);){if("previousElementSibling"===t)for(var r=n.childNodes.length-1;r>=0;r--)e.insertBefore(n.childNodes[r],e.firstChild);else for(;n.childNodes.length>0;)e.appendChild(n.childNodes[0]);i=n[t],elRemove(n),n=i}})}),o=[]}.bind(this);this.keydown.onBackspaceAndDeleteAfter=function(e){this.detect.isFirefox()&&(this.selection.isCollapsed()?e.which===this.keyCode.BACKSPACE?t(e):e.which===this.keyCode.DELETE&&i(e):e.which!==this.keyCode.BACKSPACE&&e.which!==this.keyCode.DELETE||elBySelAll(n(),this.core.editor()[0],function(e){o.push(e)}));var l=!1;if(e.which===this.keyCode.BACKSPACE&&this.selection.isCollapsed()&&this.detect.isWebkit()){var s=this.selection.block();!1!==s&&"PRE"===s.nodeName&&(l=!0)}setTimeout($.proxy(function(){var t;if(o.length>0&&r(),l){var i=this.selection.block();if((!1===i||"PRE"!==i.nodeName)&&(t=this.selection.current(),t.nodeType===Node.TEXT_NODE&&t.nextSibling&&"SPAN"===t.nextSibling.nodeName)){var n=t.nextSibling;if(-1!==n.style.getPropertyValue("font-family").indexOf("monospace")&&"pre-wrap"===n.style.getPropertyValue("white-space")){for(;n.childNodes.length;)n.parentNode.insertBefore(n.childNodes[0],n);elRemove(n)}}}this.code.syncFire=!1,this.keydown.removeEmptyLists();var s="";0!==this.opts.keepStyleAttr.length&&(s=","+this.opts.keepStyleAttr.join(",")),this.core.editor().find("*[style]").not("span, img, figure, iframe, #redactor-image-box, #redactor-image-editter, [data-redactor-style-cache], [data-redactor-span]"+s).removeAttr("style"),this.keydown.formatEmpty(e),t=this.selection.current(),"KBD"===t.nodeName&&0===t.innerHTML.length&&elRemove(t),this.code.syncFire=!0},this),1)}.bind(this)}}}; })(this);
// plugins/WoltLabKeyup.js
(function (window, undefined) { $.Redactor.prototype.WoltLabKeyup=function(){"use strict";return{init:function(){this.WoltLabEvent.register("keyup",function(e){e.event.originalEvent.which===this.keyCode.ENTER&&this.WoltLabKeyup._keyupEnter()}.bind(this))},_keyupEnter:function(){var e=this.$editor[0],t=window.getSelection(),r=null;if(this.detect.isFirefox()){var n=t.anchorNode;if(n.nodeType===Node.TEXT_NODE&&0===t.anchorOffset&&(r=n.parentNode,r.childNodes[0]===n&&(n=n.parentNode)),"LI"===n.nodeName){var i=n.parentNode;if(r=i.parentNode,"LI"===r.nodeName&&null===i.previousSibling)return r.insertBefore(this.marker.get(),i),r.insertBefore(elCreate("br"),i),void this.selection.restore()}}for(var o=t.anchorNode;o.parentNode;){if(o.parentNode===e){r=o;break}o=o.parentNode}null!==r&&"P"===r.nodeName&&(this.WoltLabKeyup._rebuildEmptyParagraph(r,!1),null!==(r=r.previousElementSibling)&&"P"===r.nodeName&&this.WoltLabKeyup._rebuildEmptyParagraph(r,!0))},_rebuildEmptyParagraph:function(e,t){if(!(e.textContent.replace(/\u200B/g,"").trim().length>0)){for(var r=e;r.nodeType===Node.ELEMENT_NODE&&0!==r.childNodes.length;){if(r.children.length>1)return;r=1===r.children.length?r.children[0]:r.childNodes[0]}if(r.nodeType===Node.TEXT_NODE){var n=elCreate("br");r.parentNode.appendChild(n),t&&elRemove(r)}}}}}; })(this);
(function (window, undefined) { $.Redactor.prototype.WoltLabLink=function(){"use strict";var i=null;return{init:function(){this.link.isUrl=function(i){var t="((xn--)?[\\W\\w\\D\\d]+(-(?!-[\\W\\w\\D\\d])+)*\\.)+[\\W\\w]{2,}",e=new RegExp("^(http|ftp|https|steam|ts3server)://"+t,"i"),s=new RegExp("^"+t,"i"),r=new RegExp(".(html|php)$","i"),l=new RegExp("^/","i"),n=new RegExp("^tel:(.*?)","i");return-1===i.search(e)&&-1!==i.search(s)&&-1===i.search(r)&&"/"!==i.substring(0,1)&&(i="http://"+i),(-1!==i.search(e)||-1!==i.search(r)||-1!==i.search(l)||-1!==i.search(n))&&i}.bind(this),this.link.show=this.WoltLabLink.show.bind(this),this.link.parse=function(i){if(this.link.isMailto(i.url))i.url="mailto:"+i.url.replace("mailto:","");else if(0!==i.url.search("#")&&this.opts.linkValidation){var t=this.link.isUrl(i.url);!1===t&&(t="http://"+i.url),i.url=t}return!this.link.isEmpty(i)&&!1!==i.url&&i}.bind(this),require(["WoltLabSuite/Core/Ui/Redactor/Link"],function(t){i=t})},show:function(t){void 0!==t&&t.preventDefault&&t.preventDefault();var e=this.selection.is();this.selection.save(),this.observe.closeAllTooltip();var s=this.link.is();i.showDialog({insert:!1===s,submitCallback:function(){var i=this.link.buildLinkFromModal();return!1!==i&&(this.selection.restore(),this.link.insert(i,!0),!0)}.bind(this)}),e&&this.selection.restore();var r=this.link.buildLinkFromElement(s);e&&this.selection.save(),r.url=this.link.removeSelfHostFromUrl(r.url),this.link.setModalValues(r),this.detect.isDesktop()&&$("#redactor-link-url").focus()}}}; })(this);
// plugins/WoltLabList.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabList=function(){"use strict";return{init:function(){this.list.combineAfterAndBefore=function(e){var t=$(e).prev(),i=$(e).next(),n=e&&"P"===e.tagName&&("<br>"===e.innerHTML||""===e.innerHTML),s=1===t.closest("ol, ul",this.core.editor()[0]).length&&1===i.closest("ol, ul",this.core.editor()[0]).length,o=!1;if(s&&!n&&0===e.textContent.replace(/\u200b/g,"").trim().length){var r=["A","B","BR","EM","I","STRONG","U"],l=!0;elBySelAll("*",e,function(e){-1===r.indexOf(e.nodeName)&&("SPAN"===e.nodeName&&""===e.className.trim()||(l=!1))}),l&&(o=!0,n=!0)}if(n&&s){if("LI"===e.nodeName&&o)return t.append(this.marker.get()),elRemove(e),this.selection.restore(),!0;t.children("li").last().append(this.marker.get()),t.append(i.contents());var a=e.nextElementSibling;return"OL"!==a.nodeName&&"UL"!==a.nodeName||0!==a.childElementCount||elRemove(a),o&&elRemove(e),this.selection.restore(),!0}return!1}.bind(this),this.list.toggle=function(e){if(!this.utils.inBlocks(["table","td","th","tr"])){e="orderedlist"===e?"ol":e,e="unorderedlist"===e?"ul":e,e=e.toLowerCase(),this.buffer.set(),this.selection.save();var t=this.list._getBlocks(),i=this.selection.block(),n=$(i).parent().closest("ol, ul",this.core.editor()[0]);return 0===t.length&&0!==n.length&&(t=[n.get(0)]),t=this.list._isUnformat(e,t)?this.list._unformat(e,t):this.list._format(e,t),this.selection.restore(),t}}.bind(this)}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabList=function(){"use strict";return{parentsQualifiedForLists:["woltlab-quote","woltlab-spoiler"],init:function(){this.list.combineAfterAndBefore=function(t){var e=$(t).prev(),i=$(t).next(),s=t&&"P"===t.tagName&&("<br>"===t.innerHTML||""===t.innerHTML),n=1===e.closest("ol, ul",this.core.editor()[0]).length&&1===i.closest("ol, ul",this.core.editor()[0]).length,o=!1;if(n&&!s&&0===t.textContent.replace(/\u200b/g,"").trim().length){var r=["A","B","BR","EM","I","STRONG","U"],l=!0;elBySelAll("*",t,function(t){-1===r.indexOf(t.nodeName)&&("SPAN"===t.nodeName&&""===t.className.trim()||(l=!1))}),l&&(o=!0,s=!0)}if(s&&n){if("LI"===t.nodeName&&o)return e.append(this.marker.get()),elRemove(t),this.selection.restore(),!0;e.children("li").last().append(this.marker.get()),e.append(i.contents());var a=t.nextElementSibling;return"OL"!==a.nodeName&&"UL"!==a.nodeName||0!==a.childElementCount||elRemove(a),o&&elRemove(t),this.selection.restore(),!0}return!1}.bind(this),this.list.toggle=function(t){if(!this.utils.inBlocks(["table","td","th","tr"])){t="orderedlist"===t?"ol":t,t="unorderedlist"===t?"ul":t,t=t.toLowerCase(),this.buffer.set(),this.selection.save();var e=this.list._getBlocks(),i=this.selection.block(),s=$(i).parent().closest("ol, ul",this.core.editor()[0]);return 0===e.length&&0!==s.length&&(e=[s.get(0)]),e=this.list._isUnformat(t,e)?this.list._unformat(t,e):this.list._format(t,e),this.selection.restore(),e}}.bind(this),this.list._getBlocks=function(){return this.selection.blocks().filter(function(t){var e=t.parentNode;return!!e.classList.contains("redactor-in")||-1!==this.WoltLabList.parentsQualifiedForLists.indexOf(e.nodeName.toLowerCase())}.bind(this))}.bind(this)}}}; })(this);
// plugins/WoltLabMedia.js
(function (window, undefined) { $.Redactor.prototype.WoltLabMedia=function(){"use strict";return{init:function(){var t=this.button.add("woltlabMedia","");$(t).addClass("jsMediaEditorButton");var e=WCF.System.Event.addListener("com.woltlab.wcf.redactor2","metacode_wsm_"+this.$element[0].id,function(t){if(1!==t.attributes.length){var e="";3===t.attributes.length&&("left"===t.attributes[2]?e=" messageFloatObjectLeft":"right"===t.attributes[2]&&(e=" messageFloatObjectRight"));var a=elCreate("img");a.className="woltlabSuiteMedia"+e,a.src=this.opts.woltlab.mediaUrl.replace(/&/,"&").replace("-123456789",t.attributes[0]).replace("thumbnail=void",function(){return t.attributes[1]?"thumbnail="+t.attributes[1]:""}),elData(a,"media-id",t.attributes[0]),elData(a,"media-size",t.attributes[1]);var i=t.metacode;i.parentNode.insertBefore(a,i),elRemove(i),t.cancel=!0}}.bind(this));WCF.System.Event.addListener("com.woltlab.wcf.redactor2","destroy_"+this.$element[0].id,function(){WCF.System.Event.removeListener("com.woltlab.wcf.redactor2","metacode_wsm_"+this.$element[0].id,e)}.bind(this)),require(["WoltLabSuite/Core/Media/Manager/Editor"],function(t){new t({editor:this})}.bind(this))}}}; })(this);
(function (window, undefined) { $.Redactor.prototype.WoltLabModal=function(){"use strict";var t=null,i="",e=null,o={close:function(){this.selection.restore(),e.getDialog("redactorOverlay-"+this.uuid)&&e.close("redactorOverlay-"+this.uuid)},load:function(e,o){t.innerHTML=this.modal.getTemplate(e),i=o},setTitle:function(t){e.setTitle(this,t)},show:function(){this.selection.save(),e.open(this),e.setTitle(this,i)}};return{init:function(){t=elCreate("div"),t.className="redactorModalWrapper",t.id="redactorOverlay-"+this.uuid,elHide(t),document.body.appendChild(t),this.$modalBody=$(t),require(["Ui/Dialog"],function(t){e=t;for(var i in o)o.hasOwnProperty(i)&&(this.modal[i]=o[i].bind(this))}.bind(this)),this._dialogSetup=function(){return{id:"redactorOverlay-"+this.uuid,options:{onClose:function(t){var i=e.getDialog(t);elBySelAll("[id]",i.content,function(t){t.removeAttribute("id")})}}}}},rebuild:function(){e&&e.rebuild("redactorOverlay-"+this.uuid)}}}; })(this);
// plugins/WoltLabObserve.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabObserve=function(){"use strict";return{init:function(){var t=elByClass("re-button",this.button.toolbar()[0]);this.button.setInactiveAll=function(e){for(var o,s=0,i=t.length;s<i;s++)o=t[s],o.classList.contains("re-"+e)||o.classList.remove("redactor-act")}.bind(this),this.observe.buttons=function(t,e){var o=this.selection.current();if(!1!==t?this.button.setInactiveAll():this.button.setInactiveAll(e),!1===t&&"html"!==e)return void(-1!==$.inArray(e,this.opts.activeButtons)&&this.button.toggleActive(e));if(this.utils.isRedactorParent(o)){var s=this.WoltLabSource.isActive();this.utils.isCurrentOrParentHeader()||this.utils.isCurrentOrParent(["table","pre","blockquote","li"])?this.button.disable("horizontalrule"):s||this.button.enable("horizontalrule"),this.utils.isCurrentOrParent(["table","li"])?(this.button.disable("code"),this.button.disable("spoiler"),this.button.disable("woltlabHtml"),this.button.disable("woltlabQuote")):s||(this.button.enable("code"),this.button.enable("spoiler"),this.button.enable("woltlabHtml"),this.button.enable("woltlabQuote"));var i=this.$editor[0];if(o.nodeType!==Node.ELEMENT_NODE&&(o=o.parentNode),o.closest(".redactor-layer")===i)for(var r,n,l=[];o!==i;)n=o.nodeName.toLowerCase(),-1===l.indexOf(n)&&(r=n,"pre"===n&&o.classList.contains("woltlabHtml")&&(r="woltlab-html"),this.opts.activeButtonsStates.hasOwnProperty(r)&&this.button.setActive(this.opts.activeButtonsStates[r]),"pre"!==n&&l.push(n)),o=o.parentNode}}.bind(this),this.observe.dropdowns=function(){var t=this.selection.current();t&&t.nodeType!==Node.ELEMENT_NODE&&(t=t.parentNode);var e,o=this.$editor[0],s=t&&t.closest(".redactor-layer")===o,i=[];if(s)for(;t!==o;)e=t.nodeName.toLowerCase(),-1===i.indexOf(e)&&i.push(e),t=t.parentNode;for(var r,n=null,l=0,a=this.opts.observe.dropdowns.length;l<a;l++){r=this.opts.observe.dropdowns[l];var b=r.observe,h=b.element,u=r.item,d=!!b.in&&b.in,c=!!b.out&&b.out;"a"===h&&null===n&&(n=$("<div />").html(this.selection.html()).find("a").length),-1!==i.indexOf(h)&&s||"a"===h&&0!==n?this.observe.setDropdownProperties(u,d,c):this.observe.setDropdownProperties(u,c,d)}}.bind(this)}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabObserve=function(){"use strict";return{init:function(){var t=elByClass("re-button",this.button.toolbar()[0]);this.button.setInactiveAll=function(e){for(var s,o=0,i=t.length;o<i;o++)s=t[o],s.classList.contains("re-"+e)||(s.classList.remove("redactor-act"),"html"!==s.rel&&elAttr(s,"tabindex",-1),elAttr(s,"aria-pressed",!1))}.bind(this),this.observe.buttons=function(t,e){var s=this.selection.current();if(!1!==t?this.button.setInactiveAll():this.button.setInactiveAll(e),!1===t&&"html"!==e)return void(-1!==$.inArray(e,this.opts.activeButtons)&&this.button.toggleActive(e));if(this.utils.isRedactorParent(s)){var o=this.WoltLabSource.isActive();this.utils.isCurrentOrParentHeader()||this.utils.isCurrentOrParent(["table","pre","blockquote","li"])?this.button.disable("horizontalrule"):o||this.button.enable("horizontalrule"),this.utils.isCurrentOrParent(["table","li"])?(this.button.disable("code"),this.button.disable("spoiler"),this.button.disable("woltlabHtml"),this.button.disable("woltlabQuote")):o||(this.button.enable("code"),this.button.enable("spoiler"),this.button.enable("woltlabHtml"),this.button.enable("woltlabQuote")),o&&this.button.setActive("html"),this.core.box()[0].classList.contains("redactorBoxFullscreen")&&this.button.setActive("woltlabFullscreen");var i=this.$editor[0];if(s.nodeType!==Node.ELEMENT_NODE&&(s=s.parentNode),s.closest(".redactor-layer")===i)for(var r,n,l=[];s!==i;)n=s.nodeName.toLowerCase(),-1===l.indexOf(n)&&(r=n,"pre"===n&&s.classList.contains("woltlabHtml")&&(r="woltlab-html"),this.opts.activeButtonsStates.hasOwnProperty(r)&&this.button.setActive(this.opts.activeButtonsStates[r]),"pre"!==n&&l.push(n)),s=s.parentNode}}.bind(this),this.observe.dropdowns=function(){var t=this.selection.current();t&&t.nodeType!==Node.ELEMENT_NODE&&(t=t.parentNode);var e,s=this.$editor[0],o=t&&t.closest(".redactor-layer")===s,i=[];if(o)for(;t!==s;)e=t.nodeName.toLowerCase(),-1===i.indexOf(e)&&i.push(e),t=t.parentNode;for(var r,n=null,l=0,a=this.opts.observe.dropdowns.length;l<a;l++){r=this.opts.observe.dropdowns[l];var b=r.observe,h=b.element,u=r.item,c=!!b.in&&b.in,d=!!b.out&&b.out;"a"===h&&null===n&&(n=$("<div />").html(this.selection.html()).find("a").length),-1!==i.indexOf(h)&&o||"a"===h&&0!==n?this.observe.setDropdownProperties(u,c,d):this.observe.setDropdownProperties(u,d,c)}}.bind(this)}}}; })(this);
// plugins/WoltLabPage.js
(function (window, undefined) { $.Redactor.prototype.WoltLabPage=function(){"use strict";return{init:function(){var t=this.button.add("woltlabPage","");require(["WoltLabSuite/Core/Ui/Redactor/Page"],function(e){new e(this,t[0])}.bind(this))}}}; })(this);
// plugins/WoltLabPaste.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabPaste=function(){"use strict";var t=null;return{init:function(){var e=null,i=!1,r=document.documentMode&&"object"==typeof window.clipboardData,n=null,a=null,s=function(t){this.rtePaste=!0;var e=!("pre"!==this.opts.type&&!this.utils.isCurrentOrParent("pre"));this.utils.saveScroll(),this.selection.save(),this.paste.createPasteBox(e);var i=this.paste.getPasteBoxCode(e);this.buffer.set(),this.selection.restore(),this.utils.restoreScroll();var r=this.clean.getCurrentType(i);i=this.clean.onPaste(i,r);var n=this.core.callback("paste",i);i=void 0===n?i:n,this.paste.insert(i,r),this.rtePaste=!1,e&&this.clean.cleanPre()}.bind(this),l=this.paste.init;this.paste.init=function(o){a=n=null;var d="pre"===this.opts.type||this.utils.isCurrentOrParent("pre");if(i=!d&&this.utils.isCurrentOrParent("kbd"),d||i){e=r?window.clipboardData.getData("Text"):o.originalEvent.clipboardData.getData("text/plain");var h=this.clean.encodeEntities;this.clean.encodeEntities=function(t){return this.clean.encodeEntities=h,WCF.String.escapeHTML(t)}.bind(this)}else if(!r){var c=o.originalEvent.clipboardData.types,p=!1;if(-1!==c.indexOf("text/html")&&(n=o.originalEvent.clipboardData.getData("text/html"),n.trim().match(/^<html[^>]*>[\s\S]*?<body[^>]*>([\s\S]+)<\/body>[\s\S]*?<\/html>$/)&&(n=RegExp.$1.replace(/^\s*(?:<!--StartFragment-->)(.+)(?:<!--EndFragment-->)?\s*$/,"$1")),p=0!==n.trim().length),!p&&-1!==c.indexOf("text/plain")){var u=WCF.String.escapeHTML(o.originalEvent.clipboardData.getData("text/plain"));a="";var f=u.split("\n");1===f.length?a=u:f.forEach(function(t){t=t.trim(),""===t&&(t="<br>"),a+="<p>"+t+"</p>"})}}null===a&&null===n||o.preventDefault(),"android"===t.platform()&&"chrome"===t.browser()?s(o):l.call(this,o)}.bind(this),require(["Environment"],function(e){if(t=e,"ios"===t.platform()){var i=this.paste.appendPasteBox;this.paste.appendPasteBox=function(){this.$pasteBox.css({fontSize:"16px",height:"1px",left:"1px",overflow:"hidden",position:"absolute",top:~~(window.innerHeight/4)+window.pageYOffset+"px",width:"1px"}),i.call(this)}.bind(this)}}.bind(this));var o=this.paste.createPasteBox;this.paste.createPasteBox=function(t){null===n&&null===a&&o.call(this,t)}.bind(this);var d=this.paste.getPasteBoxCode;this.paste.getPasteBoxCode=function(t){if(null!==n||null!==a)return this.tmpScrollTop=void 0,n||a;var s=d.call(this,t);return i?e:!t||s&&!r?s:e}.bind(this),this.core.editor().off("paste.redactor").on("paste.redactor",this.paste.init.bind(this)),this.paste.detectClipboardUpload=function(t){t=t.originalEvent||t;var e=null;if(r){if(!window.clipboardData.files.length)return!1;e=window.clipboardData.files.item(0)}else{if(this.detect.isFirefox())return!1;var i=t.clipboardData,n=i.types;if(Array.isArray(n)&&-1!==n.indexOf("public.tiff")){if(0===i.files.length)return;if(1!==i.files.length)return t.preventDefault(),!1;e=i.files[0],h=!0,null!==e&&t.preventDefault()}if(null===e){if(!i.items||!i.items.length)return;if(this.detect.isWebkit()){for(var a,s=!1,l=!1,o=0,d=i.items.length;o<d;o++)a=i.items[o],"string"===a.kind&&"text/html"===a.type?l=!0:"file"===a.kind&&(s=!0);if(s&&l)return!1}var h=!1;if(null===(e=i.items[0].getAsFile())&&(this.detect.isWebkit()&&i.items.length>1&&(e=i.items[1].getAsFile(),h=!0,null!==e&&t.preventDefault()),null===e))return!1}}var c=new FileReader;return c.readAsDataURL(e),c.onload=this.paste.insertFromClipboard.bind(this),!1===h}.bind(this),this.paste.insertFromClipboard=function(t){window.FormData&&(this.buffer.set(),WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this.$element[0].id,{blob:this.utils.dataURItoBlob(t.target.result)}),this.rtePaste=!1)}.bind(this);var h="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",c=this.paste.insert;this.paste.insert=function(t,e){if(i&&(e.pre=!0),this.utils.isCurrentOrParent("kbd")){c.call(this,t,e);var n=this.selection.current();n.nodeType===Node.TEXT_NODE&&(n=n.parentNode);for(var a=n.closest("kbd"),s=elByTag("p",a);s.length;)s[0].outerHTML=s[0].innerHTML;var l=a.innerHTML.split(/<br\s*\/?>/);if(l.length>1){for(var o=this.selection.block(),d=1,p=l.length;d<p;d++){var u=elCreate(o.nodeName);u.innerHTML="<kbd>"+l[d]+(d===p-1?this.marker.html():"")+"</kbd>",o.parentNode.insertBefore(u,o.nextSibling),o=u}a.innerHTML=l[0],this.selection.restore()}}else{if(e.pre)return c.call(this,t,e);var f=elCreate("div");f.innerHTML=t;var b=[];e.pre||e.text||elBySelAll("img",f,function(t){var e=t.src;if(0===e.indexOf("data:image")&&e!==h){t.src=h;var i=WCF.getUUID();elData(t,"uuid",i),b.push({src:e,uuid:i}),elHide(t)}}.bind(this)),elBySelAll("pre",f,function(t){elBySelAll("br",t,function(t){var e=t.parentNode;e.insertBefore(document.createTextNode("\n"),t),e.removeChild(t)})}),c.call(this,f.innerHTML,e);var v=window.getSelection();v.rangeCount&&"A"===v.anchorNode.nodeName&&v.anchorOffset===v.anchorNode.childNodes.length&&this.caret.after(v.anchorNode),b.length&&window.setTimeout(function(){for(var t,e,i=0,n=b.length;i<n;i++)t=b[i],(e=elBySel('img[data-uuid="'+t.uuid+'"]',this.$editor[0]))&&(r?e.parentNode.removeChild(e):WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this.$element[0].id,{blob:this.utils.dataURItoBlob(t.src),replace:e}))}.bind(this),50);var m,g=[],A=this.core.editor()[0];for(d=0,p=A.childNodes.length;d<p;d++)m=A.childNodes[d],m.nodeType===Node.TEXT_NODE&&""===m.textContent&&g.push(m);g.forEach(elRemove),this.WoltLabClean.removeRedundantStyles(),this.rtePaste=!1}}.bind(this),this.paste.clipboardUpload=function(){}}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabPaste=function(){"use strict";var t=null;return{init:function(){var e=null,i=!1,r=document.documentMode&&"object"==typeof window.clipboardData,n=null,a=null,s=function(t){this.rtePaste=!0;var e=!("pre"!==this.opts.type&&!this.utils.isCurrentOrParent("pre"));this.utils.saveScroll(),this.selection.save(),this.paste.createPasteBox(e);var i=this.paste.getPasteBoxCode(e);this.buffer.set(),this.selection.restore(),this.utils.restoreScroll();var r=this.clean.getCurrentType(i);i=this.clean.onPaste(i,r);var n=this.core.callback("paste",i);i=void 0===n?i:n,this.paste.insert(i,r),this.rtePaste=!1,e&&this.clean.cleanPre()}.bind(this),l=this.paste.init;this.paste.init=function(o){a=n=null;var d="pre"===this.opts.type||this.utils.isCurrentOrParent("pre");if(i=!d&&this.utils.isCurrentOrParent("kbd"),d||i){e=r?window.clipboardData.getData("Text"):o.originalEvent.clipboardData.getData("text/plain");var h=this.clean.encodeEntities;this.clean.encodeEntities=function(t){return this.clean.encodeEntities=h,WCF.String.escapeHTML(t)}.bind(this)}else if(!r){var c=o.originalEvent.clipboardData.types,p=!1;if(-1!==c.indexOf("text/html")&&(n=o.originalEvent.clipboardData.getData("text/html"),n.trim().match(/^<html[^>]*>[\s\S]*?<body[^>]*>([\s\S]+)<\/body>[\s\S]*?<\/html>$/)&&(n=RegExp.$1.replace(/^\s*(?:<!--StartFragment-->)(.+)(?:<!--EndFragment-->)?\s*$/,"$1")),p=0!==n.trim().length),!p&&-1!==c.indexOf("text/plain")){var f=WCF.String.escapeHTML(o.originalEvent.clipboardData.getData("text/plain"));a="";var u=f.split("\n");1===u.length?a=f:u.forEach(function(t){t=t.trim(),""===t&&(t="<br>"),a+="<p>"+t+"</p>"})}}null===a&&null===n||o.preventDefault(),"android"===t.platform()&&"chrome"===t.browser()?s(o):l.call(this,o)}.bind(this),require(["Environment"],function(e){if(t=e,"ios"===t.platform()){var i=this.paste.appendPasteBox;this.paste.appendPasteBox=function(){this.$pasteBox.css({fontSize:"16px",height:"1px",left:"1px",overflow:"hidden",position:"absolute",top:~~(window.innerHeight/4)+window.pageYOffset+"px",width:"1px"}),i.call(this)}.bind(this)}}.bind(this));var o=this.paste.createPasteBox;this.paste.createPasteBox=function(t){null===n&&null===a&&o.call(this,t)}.bind(this);var d=this.paste.getPasteBoxCode;this.paste.getPasteBoxCode=function(t){if(null!==n||null!==a)return this.tmpScrollTop=void 0,n||a;var s=d.call(this,t);return i?e:!t||s&&!r?s:e}.bind(this),this.core.editor().off("paste.redactor").on("paste.redactor",this.paste.init.bind(this)),this.paste.detectClipboardUpload=function(t){t=t.originalEvent||t;var e=null;if(r){if(!window.clipboardData.files.length)return!1;e=window.clipboardData.files.item(0)}else{if(this.detect.isFirefox())return!1;var i=t.clipboardData,n=i.types;if(Array.isArray(n)&&-1!==n.indexOf("public.tiff")){if(0===i.files.length)return;if(1!==i.files.length)return t.preventDefault(),!1;e=i.files[0],h=!0,null!==e&&t.preventDefault()}if(null===e){if(!i.items||!i.items.length)return;if(this.detect.isWebkit()){for(var a,s=!1,l=!1,o=0,d=i.items.length;o<d;o++)a=i.items[o],"string"===a.kind&&"text/html"===a.type?l=!0:"file"===a.kind&&(s=!0);if(s&&l)return!1}var h=!1;if(null===(e=i.items[0].getAsFile())&&(this.detect.isWebkit()&&i.items.length>1&&(e=i.items[1].getAsFile(),h=!0,null!==e&&t.preventDefault()),null===e))return!1}}var c=new FileReader;return c.readAsDataURL(e),c.onload=this.paste.insertFromClipboard.bind(this),!1===h}.bind(this),this.paste.insertFromClipboard=function(t){window.FormData&&(this.buffer.set(),WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this.$element[0].id,{blob:this.utils.dataURItoBlob(t.target.result)}),this.rtePaste=!1)}.bind(this);var h="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",c=this.paste.insert;this.paste.insert=function(t,e){if(i&&(e.pre=!0),this.utils.isCurrentOrParent("kbd")){c.call(this,t,e);var n=this.selection.current();n.nodeType===Node.TEXT_NODE&&(n=n.parentNode);for(var a=n.closest("kbd"),s=elByTag("p",a);s.length;)s[0].outerHTML=s[0].innerHTML;var l=a.innerHTML.split(/<br\s*\/?>/);if(l.length>1){for(var o=this.selection.block(),d=1,p=l.length;d<p;d++){var f=elCreate(o.nodeName);f.innerHTML="<kbd>"+l[d]+(d===p-1?this.marker.html():"")+"</kbd>",o.parentNode.insertBefore(f,o.nextSibling),o=f}a.innerHTML=l[0],this.selection.restore()}}else{if(e.pre)return c.call(this,t,e);var u=elCreate("div");if(u.innerHTML=t,1===u.childElementCount&&"A"===u.children[0].nodeName){var b=u.children[0];if(u.firstChild===b&&u.lastChild===b&&b.href===b.textContent){for(;b.childNodes.length;)u.insertBefore(b.childNodes[0],b);u.removeChild(b)}}var v=[];e.pre||e.text||elBySelAll("img",u,function(t){var e=t.src;if(0===e.indexOf("data:image")&&e!==h){t.src=h;var i=WCF.getUUID();elData(t,"uuid",i),v.push({src:e,uuid:i}),elHide(t)}}.bind(this)),elBySelAll("pre",u,function(t){elBySelAll("br",t,function(t){var e=t.parentNode;e.insertBefore(document.createTextNode("\n"),t),e.removeChild(t)})}),c.call(this,u.innerHTML,e);var m=window.getSelection();m.rangeCount&&"A"===m.anchorNode.nodeName&&m.anchorOffset===m.anchorNode.childNodes.length&&this.caret.after(m.anchorNode),v.length&&window.setTimeout(function(){for(var t,e,i=0,n=v.length;i<n;i++)t=v[i],(e=elBySel('img[data-uuid="'+t.uuid+'"]',this.$editor[0]))&&(r?e.parentNode.removeChild(e):WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this.$element[0].id,{blob:this.utils.dataURItoBlob(t.src),replace:e}))}.bind(this),50);var g,A=[],x=this.core.editor()[0];for(d=0,p=x.childNodes.length;d<p;d++)g=x.childNodes[d],g.nodeType===Node.TEXT_NODE&&""===g.textContent&&A.push(g);A.forEach(elRemove),this.WoltLabClean.removeRedundantStyles(),this.rtePaste=!1}}.bind(this),this.paste.clipboardUpload=function(){}}}}; })(this);
// plugins/WoltLabQuote.js
(function (window, undefined) { $.Redactor.prototype.WoltLabQuote=function(){"use strict";return{init:function(){var t=this.button.add("woltlabQuote","");this.WoltLabBlock.register("woltlab-quote",!0),this.opts.replaceTags.blockquote="woltlab-quote",this.opts.activeButtonsStates["woltlab-quote"]="woltlabQuote",require(["WoltLabSuite/Core/Ui/Redactor/Quote"],function(o){new o(this,t)}.bind(this))}}}; })(this);
(function (window, undefined) { $.Redactor.prototype.WoltLabSmiley=function(){"use strict";var t=0;return{init:function(){require(["EventHandler"],function(t){t.add("com.woltlab.wcf.redactor2","insertSmiley_"+this.$element[0].id,this.WoltLabSmiley._insert.bind(this))}.bind(this))},_insert:function(e){if(!this.WoltLabSource.isActive()){this.buffer.set();var i="wscSmiley_"+this.uuid+"_"+t++,r=e.img.cloneNode();r.id=i,this.insert.html(r.outerHTML),r=elById(i),r.removeAttribute("id"),this.caret.after(r),r.outerHTML=r.outerHTML,this.WoltLabCaret.forceSelectionSave()}}}}; })(this);
// plugins/WoltLabSource.js
-(function (window, undefined) { $.Redactor.prototype.WoltLabSource=function(){"use strict";return{init:function(){function e(e){elBySelAll(".icon, .fa",e,function(e){var t=e.className.split(" ");t=t.filter(function(e){return"fa"!==e&&"icon"!==e&&(!e.match(/^icon\d{2}$/)&&!e.match(/^fa-[a-z\-]+$/))}),e.className=t.join(" "),""===e.className.trim()&&""===e.innerHTML&&elRemove(e)})}var t=this.$element[0].id,n=function(e){elBySelAll("woltlab-quote",e,function(e){if(2===e.childElementCount&&"P"===e.children[0].nodeName&&"P"===e.children[1].nodeName){var t=e.children[0];if(""===t.innerHTML.trim()){"<br>"===e.children[1].innerHTML.trim()&&e.removeChild(t)}}})},i=function(e){elBySelAll("pre, woltlab-quote, woltlab-spoiler",e,function(e){e.removeAttribute("data-title")}),WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","source_stripIntermediateCode_"+t,{div:e})};this.source.setCaretOnShow=function(){},this.source.setCaretOnHide=function(e){return e};var r=this.source.hide;this.source.hide=function(){var t=$("<div />").html(this.source.$textarea.val());e(t[0]),this.source.$textarea.val(t[0].innerHTML),r.call(this),setTimeout(function(){this.focus.end(),n(this.core.editor()[0])}.bind(this),100),this.placeholder.enable()}.bind(this);var o=this.source.$textarea[0];this.$element[0].parentNode.insertBefore(o,this.$element[0]);var l=this.source.show;this.source.show=function(){var e=this.$editor[0].offsetHeight,t=this.code.get();l.call(this),this.source.$textarea.val(t.replace(/ /g," ")),o.style.setProperty("height",Math.ceil(e)+"px",""),o.style.setProperty("display","block","");var r=elCreate("div");r.innerHTML=o.value,n(r),i(r),o.value=this.WoltLabSource.format(r.innerHTML),o.selectionStart=o.selectionEnd=o.value.length}.bind(this),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","validate_"+t,function(e){o.clientHeight&&(e.api.throwError(this.$element[0],WCF.Language.get("wcf.editor.source.error.active")),e.valid=!1)}.bind(this))},isActive:function(){return"none"===this.$editor[0].style.getPropertyValue("display")},format:function(e){var t=["ul","ol","li"];this.block.tags.forEach(function(e){t.push(e)}),t=t.join("|").toLowerCase();var n=["p","li"],i="[^'\">]*(?:(?:\"[^\"]*\"|'[^']*')[^'\">]*)*",r=[];e=e.replace(new RegExp("<pre"+i+">[\\s\\S]*?</pre>","g"),function(e){return r.push(e),"@@@WCF_PRE_BACKUP_"+(r.length-1)+"@@@"}),e=e.replace(new RegExp("\\s*</("+t+")>\\s*","g"),function(e,t){return(-1===n.indexOf(t)?"\n":"")+"</"+t+">"}),e=e.replace(new RegExp("\\s*<("+t+")("+i+")>\\s*","g"),function(e,t,i){return"\n<"+t+i+">"+(-1===n.indexOf(t)?"\n":"")}),e=e.replace(/<woltlab-quote([^>]*)>\n\t*\n(\t*)<p/,"<woltlab-quote$1>\n$2<p"),e=e.replace(new RegExp("<(ol|ul)("+i+")>\\s*","g"),"<$1$2>\n"),e=e.replace(/(<\/[ou]l>)<\/li>/g,"$1\n</li>");var o,l,a,s=e.split(/\n/),c=0,u=new RegExp("^<("+t+")"),h=new RegExp("^</("+t+")>$"),f=!1;for(o=0,l=s.length;o<l;o++){if(a=s[o],f=!1,a.match(u)?-1===n.indexOf(RegExp.$1)&&(f=!0):a.match(h)&&-1===n.indexOf(RegExp.$1)&&c--,c>0){var d=c;for(s[o]="";d--;)s[o]+="\t";s[o]+=a}f&&c++}for(e=s.join("\n"),o=0,l=r.length;o<l;o++)e=e.replace("@@@WCF_PRE_BACKUP_"+o+"@@@",r[o]);return e=e.replace(/\r?\n<\/pre>/g,"</pre>"),e.trim()}}}; })(this);
+(function (window, undefined) { $.Redactor.prototype.WoltLabSource=function(){"use strict";return{init:function(){function e(e){elBySelAll(".icon, .fa",e,function(e){var t=e.className.split(" ");t=t.filter(function(e){return"fa"!==e&&"icon"!==e&&(!e.match(/^icon\d{2}$/)&&!e.match(/^fa-[a-z\-]+$/))}),e.className=t.join(" "),""===e.className.trim()&&""===e.innerHTML&&elRemove(e)})}var t=this.$element[0].id,n=function(e){elBySelAll("woltlab-quote",e,function(e){if(2===e.childElementCount&&"P"===e.children[0].nodeName&&"P"===e.children[1].nodeName){var t=e.children[0];if(""===t.innerHTML.trim()){"<br>"===e.children[1].innerHTML.trim()&&e.removeChild(t)}}})},i=function(e){elBySelAll("pre, woltlab-quote, woltlab-spoiler",e,function(e){e.removeAttribute("data-title")}),WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","source_stripIntermediateCode_"+t,{div:e})},r=this.source.hide;this.source.hide=function(){var t=$("<div />").html(this.source.$textarea.val());e(t[0]),this.source.$textarea.val(t[0].innerHTML),r.call(this),setTimeout(function(){this.focus.end(),n(this.core.editor()[0])}.bind(this),100),this.placeholder.enable()}.bind(this);var o=this.source.$textarea[0];this.$element[0].parentNode.insertBefore(o,this.$element[0]);var l=this.source.show;this.source.show=function(){var e=this.$editor[0].offsetHeight,t=this.code.get();l.call(this),this.source.$textarea.val(t.replace(/ /g," ")),o.style.setProperty("height",Math.ceil(e)+"px",""),o.style.setProperty("display","block","");var r=elCreate("div");r.innerHTML=o.value,n(r),i(r),o.value=this.WoltLabSource.format(r.innerHTML),o.selectionStart=o.selectionEnd=o.value.length}.bind(this),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","validate_"+t,function(e){o.clientHeight&&(e.api.throwError(this.$element[0],WCF.Language.get("wcf.editor.source.error.active")),e.valid=!1)}.bind(this))},isActive:function(){return"none"===this.$editor[0].style.getPropertyValue("display")},format:function(e){var t=["ul","ol","li","table","tbody","thead","tr","td","th"];this.block.tags.forEach(function(e){t.push(e)}),t=t.join("|").toLowerCase();var n=["p","li","td","th"],i="[^'\">]*(?:(?:\"[^\"]*\"|'[^']*')[^'\">]*)*",r=[];e=e.replace(new RegExp("<pre"+i+">[\\s\\S]*?</pre>","g"),function(e){return r.push(e),"@@@WCF_PRE_BACKUP_"+(r.length-1)+"@@@"}),e=e.replace(new RegExp("\\s*</("+t+")>\\s*","g"),function(e,t){return(-1===n.indexOf(t)?"\n":"")+"</"+t+">"}),e=e.replace(new RegExp("\\s*<("+t+")("+i+")>\\s*","g"),function(e,t,i){return"\n<"+t+i+">"+(-1===n.indexOf(t)?"\n":"")}),e=e.replace(new RegExp("(<(?:"+t+")(?:"+i+")>\n)\n+(?=<(?:"+t+")(?:"+i+")>)","g"),"$1"),e=e.replace(/<woltlab-quote([^>]*)>\n\t*\n(\t*)<p/,"<woltlab-quote$1>\n$2<p"),e=e.replace(new RegExp("<(ol|ul)("+i+")>\\s*","g"),"<$1$2>\n"),e=e.replace(/(<\/[ou]l>)<\/li>/g,"$1\n</li>");var o,l,a,c=e.split(/\n/),s=0,h=new RegExp("^<("+t+")"),u=new RegExp("^</("+t+")>$"),d=!1;for(o=0,l=c.length;o<l;o++){if(a=c[o],d=!1,a.match(h)?-1===n.indexOf(RegExp.$1)&&(d=!0):a.match(u)&&-1===n.indexOf(RegExp.$1)&&s--,s>0){var p=s;for(c[o]="";p--;)c[o]+="\t";c[o]+=a}d&&s++}for(e=c.join("\n"),o=0,l=r.length;o<l;o++)e=e.replace("@@@WCF_PRE_BACKUP_"+o+"@@@",r[o]);return e=e.replace(/\r?\n<\/pre>/g,"</pre>"),e.trim()}}}; })(this);
// plugins/WoltLabSpoiler.js
(function (window, undefined) { $.Redactor.prototype.WoltLabSpoiler=function(){"use strict";return{init:function(){this.WoltLabBlock.register("woltlab-spoiler",!0),this.opts.activeButtonsStates["woltlab-spoiler"]="woltlabSpoiler",require(["WoltLabSuite/Core/Ui/Redactor/Spoiler"],function(t){new t(this)}.bind(this))}}}; })(this);
(function (window, undefined) { !function(t){t.Redactor.prototype.alignment=function(){return{langs:{en:{align:"Align","align-left":"Align Left","align-center":"Align Center","align-right":"Align Right","align-justify":"Align Justify"}},init:function(){var t=this,e={};e.left={title:t.lang.get("align-left"),func:t.alignment.setLeft},e.center={title:t.lang.get("align-center"),func:t.alignment.setCenter},e.right={title:t.lang.get("align-right"),func:t.alignment.setRight},e.justify={title:t.lang.get("align-justify"),func:t.alignment.setJustify};var i=this.button.add("alignment",this.lang.get("align"));this.button.setIcon(i,'<i class="re-icon-alignment"></i>'),this.button.addDropdown(i,e)},removeAlign:function(){this.block.removeClass("text-center"),this.block.removeClass("text-right"),this.block.removeClass("text-justify")},setLeft:function(){this.buffer.set(),this.alignment.removeAlign()},setCenter:function(){this.buffer.set(),this.alignment.removeAlign(),this.block.addClass("text-center"),this.core.editor().focus()},setRight:function(){this.buffer.set(),this.alignment.removeAlign(),this.block.addClass("text-right"),this.core.editor().focus()},setJustify:function(){this.buffer.set(),this.alignment.removeAlign(),this.block.addClass("text-justify"),this.core.editor().focus()}}}}(jQuery); })(this);
// plugins/source.js
-(function (window, undefined) { !function(t){t.Redactor.prototype.source=function(){return{init:function(){var e=this.button.addFirst("html","HTML");this.button.addCallback(e,this.source.toggle);var s={width:"100%",margin:"0",background:"#111","box-sizing":"border-box",color:"rgba(255, 255, 255, .8)","font-size":"14px",outline:"none",padding:"16px","line-height":"22px","font-family":'Menlo, Monaco, Consolas, "Courier New", monospace'};this.source.$textarea=t("<textarea />"),this.source.$textarea.css(s).hide(),"textarea"===this.opts.type?this.core.box().append(this.source.$textarea):this.core.box().after(this.source.$textarea),this.core.element().on("destroy.callback.redactor",t.proxy(function(){this.source.$textarea.remove()},this))},toggle:function(){return this.source.$textarea.hasClass("open")?this.source.hide():this.source.show()},setCaretOnShow:function(){this.source.offset=this.offset.get();t(window).scrollTop(),this.core.editor().innerWidth(),this.core.editor().innerHeight();this.source.start=0,this.source.end=0;var e=t("<div/>").append(t.parseHTML(this.core.editor().html(),document,!0)),s=e.find("span.redactor-selection-marker");if(s.length>0){var r=e.html().replace(/&/g,"&");1===s.length?(this.source.start=this.utils.strpos(r,e.find("#selection-marker-1").prop("outerHTML")),this.source.end=this.source.start):2===s.length&&(this.source.start=this.utils.strpos(r,e.find("#selection-marker-1").prop("outerHTML")),this.source.end=this.utils.strpos(r,e.find("#selection-marker-2").prop("outerHTML"))-e.find("#selection-marker-1").prop("outerHTML").toString().length)}},setCaretOnHide:function(t){if(this.source.start=this.source.$textarea.get(0).selectionStart,this.source.end=this.source.$textarea.get(0).selectionEnd,this.source.start>this.source.end&&this.source.end>0){var e=this.source.end,s=this.source.start;this.source.start=e,this.source.end=s}if(this.source.start=this.source.enlargeOffset(t,this.source.start),this.source.end=this.source.enlargeOffset(t,this.source.end),t=t.substr(0,this.source.start)+this.marker.html(1)+t.substr(this.source.start),this.source.end>this.source.start){var r=this.marker.html(1).toString().length;t=t.substr(0,this.source.end+r)+this.marker.html(2)+t.substr(this.source.end+r)}return t},hide:function(){this.source.$textarea.removeClass("open").hide(),this.source.$textarea.off(".redactor-source");var t=this.source.$textarea.val();t=this.paragraphize.load(t),t=this.source.setCaretOnHide(t),this.code.start(t),this.button.enableAll(),this.core.editor().show().focus(),this.selection.restore(),this.code.sync()},show:function(){this.selection.save(),this.source.setCaretOnShow();var e=this.core.editor().innerHeight(),s=this.code.get();s=s.replace(/\n\n\n/g,"\n"),s=s.replace(/\n\n/g,"\n"),this.core.editor().hide(),this.button.disableAll("html"),this.source.$textarea.val(s).height(e).addClass("open").show(),this.source.$textarea.on("keyup.redactor-source",t.proxy(function(){"textarea"===this.opts.type&&this.core.textarea().val(this.source.$textarea.val())},this)),this.marker.remove(),t(window).scrollTop(scroll),this.source.$textarea[0].setSelectionRange&&this.source.$textarea[0].setSelectionRange(this.source.start,this.source.end),this.source.$textarea[0].scrollTop=0,setTimeout(t.proxy(function(){this.source.$textarea.focus()},this),0)},enlargeOffset:function(t,e){var s=t.length,r=0;if(">"===t[e])r++;else for(var o=e;o<=s&&(r++,">"!==t[o]);o++)if("<"===t[o]||o===s){r=0;break}return e+r}}}}(jQuery); })(this);
+(function (window, undefined) { !function(e){e.Redactor.prototype.source=function(){return{init:function(){var t=this.button.addFirst("html","HTML");this.button.addCallback(t,this.source.toggle);var o={width:"100%",margin:"0",background:"#111","box-sizing":"border-box",color:"rgba(255, 255, 255, .8)","font-size":"14px",outline:"none",padding:"16px","line-height":"22px","font-family":'Menlo, Monaco, Consolas, "Courier New", monospace'};this.source.$textarea=e("<textarea />"),this.source.$textarea.css(o).hide(),"textarea"===this.opts.type?this.core.box().append(this.source.$textarea):this.core.box().after(this.source.$textarea),this.core.element().on("destroy.callback.redactor",e.proxy(function(){this.source.$textarea.remove()},this))},toggle:function(){return this.source.$textarea.hasClass("open")?this.source.hide():this.source.show()},setCaretOnShow:function(){},setCaretOnHide:function(e){return e},hide:function(){this.source.$textarea.removeClass("open").hide(),this.source.$textarea.off(".redactor-source");var e=this.source.$textarea.val();e=this.paragraphize.load(e),this.code.start(e),this.button.enableAll(),this.core.editor().show().focus(),this.selection.restore(),this.code.sync()},show:function(){this.selection.save();var t=this.core.editor().innerHeight(),o=this.code.get();o=o.replace(/\n\n\n/g,"\n"),o=o.replace(/\n\n/g,"\n"),this.core.editor().hide(),this.button.disableAll(["html","woltlabFullscreen"]),this.source.$textarea.val(o).height(t).addClass("open").show(),this.source.$textarea.on("keyup.redactor-source",e.proxy(function(){"textarea"===this.opts.type&&this.core.textarea().val(this.source.$textarea.val())},this)),this.marker.remove(),e(window).scrollTop(scroll),this.source.$textarea[0].setSelectionRange&&this.source.$textarea[0].setSelectionRange(this.source.start,this.source.end),this.source.$textarea[0].scrollTop=0,setTimeout(e.proxy(function(){this.source.$textarea.focus(),this.button.setActive("html")},this),0)},enlargeOffset:function(){}}}}(jQuery); })(this);
// plugins/table.js
(function (window, undefined) { !function(e){e.Redactor.prototype.table=function(){return{langs:{en:{table:"Table","insert-table":"Insert table","insert-row-above":"Insert row above","insert-row-below":"Insert row below","insert-column-left":"Insert column left","insert-column-right":"Insert column right","add-head":"Add head","delete-head":"Delete head","delete-column":"Delete column","delete-row":"Delete row","delete-table":"Delete table"}},init:function(){var e={};e.insert_table={title:this.lang.get("insert-table"),func:this.table.insert,observe:{element:"table",in:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.insert_row_above={title:this.lang.get("insert-row-above"),func:this.table.addRowAbove,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.insert_row_below={title:this.lang.get("insert-row-below"),func:this.table.addRowBelow,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.insert_column_left={title:this.lang.get("insert-column-left"),func:this.table.addColumnLeft,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.insert_column_right={title:this.lang.get("insert-column-right"),func:this.table.addColumnRight,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.add_head={title:this.lang.get("add-head"),func:this.table.addHead,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.delete_head={title:this.lang.get("delete-head"),func:this.table.deleteHead,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.delete_column={title:this.lang.get("delete-column"),func:this.table.deleteColumn,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.delete_row={title:this.lang.get("delete-row"),func:this.table.deleteRow,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}},e.delete_table={title:this.lang.get("delete-table"),func:this.table.deleteTable,observe:{element:"table",out:{attr:{class:"redactor-dropdown-link-inactive","aria-disabled":!0}}}};var t=this.button.addBefore("link","table",this.lang.get("table"));this.button.setIcon(t,'<i class="re-icon-table"></i>'),this.button.addDropdown(t,e)},insert:function(){if(!this.table.getTable()){this.placeholder.hide();for(var t=e("<div>"),a=e("<table />"),i=0;i<2;i++){for(var r=e("<tr>"),l=0;l<3;l++){var n=e("<td>"+this.opts.invisibleSpace+"</td>");0===i&&0===l&&n.append(this.marker.get()),e(r).append(n)}a.append(r)}t.append(a);var s=t.html();this.buffer.set();var o=this.selection.current();0!==e(o).closest("li",this.core.editor()[0]).length?e(o).closest("ul, ol").first().after(s):(this.air.collapsed(),this.insert.html(s)),this.selection.restore(),this.core.callback("insertedTable",a)}},getTable:function(){var t=e(this.selection.current()).closest("table");return!!this.utils.isRedactorParent(t)&&(0!==t.length&&t)},restoreAfterDelete:function(e){this.selection.restore(),e.find("span.redactor-selection-marker").remove()},deleteTable:function(){var e=this.table.getTable();if(e){this.buffer.set();var t=e.next();this.opts.linebreaks||0===t.length?this.caret.after(e):this.caret.start(t),e.remove()}},deleteRow:function(){var t=this.table.getTable();if(t){var a=e(this.selection.current());this.buffer.set();var i=a.closest("tr"),r=i.prev().length?i.prev():i.next();if(r.length){var l=r.children("td, th").first();l.length&&l.prepend(this.marker.get())}i.remove(),this.table.restoreAfterDelete(t)}},deleteColumn:function(){var t=this.table.getTable();if(t){this.buffer.set();var a=e(this.selection.current()),i=a.closest("td, th"),r=i[0].cellIndex;t.find("tr").each(e.proxy(function(t,a){var i=e(a),l=r-1<0?r+1:r-1;0===t&&i.find("td, th").eq(l).prepend(this.marker.get()),i.find("td, th").eq(r).remove()},this)),this.table.restoreAfterDelete(t)}},addHead:function(){var t=this.table.getTable();if(t){if(this.buffer.set(),0!==t.find("thead").length)return void this.table.deleteHead();var a=t.find("tr").first().clone();a.find("td").replaceWith(e.proxy(function(){return e("<th>").html(this.opts.invisibleSpace)},this)),$thead=e("<thead></thead>").append(a),t.prepend($thead)}},deleteHead:function(){var e=this.table.getTable();if(e){var t=e.find("thead");0!==t.length&&(this.buffer.set(),t.remove())}},addRowAbove:function(){this.table.addRow("before")},addRowBelow:function(){this.table.addRow("after")},addColumnLeft:function(){this.table.addColumn("before")},addColumnRight:function(){this.table.addColumn("after")},addRow:function(t){if(this.table.getTable()){this.buffer.set();var a=e(this.selection.current()),i=a.closest("tr"),r=i.clone();r.find("th").replaceWith(function(){var t=e("<td>");return t[0].attributes=this.attributes,t.append(e(this).contents())}),r.find("td").html(this.opts.invisibleSpace),"after"===t?i.after(r):i.before(r)}},addColumn:function(t){var a=this.table.getTable();if(a){var i=0,r=e(this.selection.current());this.buffer.set();var l=r.closest("tr"),n=r.closest("td, th");l.find("td, th").each(e.proxy(function(t,a){e(a)[0]===n[0]&&(i=t)},this)),a.find("tr").each(e.proxy(function(a,r){var l=e(r).find("td, th").eq(i),n=l.clone();n.html(this.opts.invisibleSpace),"after"===t?l.after(n):l.before(n)},this))}}}}}(jQuery); })(this);
// WCF.Combined.min.js -- DO NOT EDIT
// 3rdParty/jquery.js
-(function (window, undefined) { !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(e,t){t=t||ne;var n=t.createElement("script");n.text=e,t.head.appendChild(n).parentNode.removeChild(n)}function r(e){var t=!!e&&"length"in e&&e.length,n=he.type(e);return"function"!==n&&!he.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function i(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}function o(e,t,n){return he.isFunction(t)?he.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?he.grep(e,function(e){return e===t!==n}):"string"!=typeof t?he.grep(e,function(e){return se.call(t,e)>-1!==n}):Ee.test(t)?he.filter(t,e,n):(t=he.filter(t,e),he.grep(e,function(e){return se.call(t,e)>-1!==n&&1===e.nodeType}))}function a(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function s(e){var t={};return he.each(e.match(je)||[],function(e,n){t[n]=!0}),t}function u(e){return e}function l(e){throw e}function c(e,t,n,r){var i;try{e&&he.isFunction(i=e.promise)?i.call(e).done(t).fail(n):e&&he.isFunction(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}function f(){ne.removeEventListener("DOMContentLoaded",f),e.removeEventListener("load",f),he.ready()}function p(){this.expando=he.expando+p.uid++}function d(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Pe.test(e)?JSON.parse(e):e)}function h(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(Re,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n=d(n)}catch(e){}Oe.set(e,t,n)}else n=void 0;return n}function g(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return he.css(e,t,"")},u=s(),l=n&&n[3]||(he.cssNumber[t]?"":"px"),c=(he.cssNumber[t]||"px"!==l&&+u)&&Ie.exec(he.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do{o=o||".5",c/=o,he.style(e,t,c+l)}while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function v(e){var t,n=e.ownerDocument,r=e.nodeName,i=_e[r];return i||(t=n.body.appendChild(n.createElement(r)),i=he.css(t,"display"),t.parentNode.removeChild(t),"none"===i&&(i="block"),_e[r]=i,i)}function m(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)r=e[o],r.style&&(n=r.style.display,t?("none"===n&&(i[o]=Fe.get(r,"display")||null,i[o]||(r.style.display="")),""===r.style.display&&$e(r)&&(i[o]=v(r))):"none"!==n&&(i[o]="none",Fe.set(r,"display",n)));for(o=0;o<a;o++)null!=i[o]&&(e[o].style.display=i[o]);return e}function y(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&i(e,t)?he.merge([e],n):n}function x(e,t){for(var n=0,r=e.length;n<r;n++)Fe.set(e[n],"globalEval",!t||Fe.get(t[n],"globalEval"))}function b(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===he.type(o))he.merge(p,o.nodeType?[o]:o);else if(Ge.test(o)){for(a=a||f.appendChild(t.createElement("div")),s=(Xe.exec(o)||["",""])[1].toLowerCase(),u=Ve[s]||Ve._default,a.innerHTML=u[1]+he.htmlPrefilter(o)+u[2],c=u[0];c--;)a=a.lastChild;he.merge(p,a.childNodes),a=f.firstChild,a.textContent=""}else p.push(t.createTextNode(o));for(f.textContent="",d=0;o=p[d++];)if(r&&he.inArray(o,r)>-1)i&&i.push(o);else if(l=he.contains(o.ownerDocument,o),a=y(f.appendChild(o),"script"),l&&x(a),n)for(c=0;o=a[c++];)Ue.test(o.type||"")&&n.push(o);return f}function w(){return!0}function T(){return!1}function C(){try{return ne.activeElement}catch(e){}}function E(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)E(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=T;else if(!i)return e;return 1===o&&(a=i,i=function(e){return he().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=he.guid++)),e.each(function(){he.event.add(this,t,i,r,n)})}function k(e,t){return i(e,"table")&&i(11!==t.nodeType?t:t.firstChild,"tr")?he(">tbody",e)[0]||e:e}function S(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function N(e){var t=nt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function D(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Fe.hasData(e)&&(o=Fe.access(e),a=Fe.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n<r;n++)he.event.add(t,i,l[i][n])}Oe.hasData(e)&&(s=Oe.access(e),u=he.extend({},s),Oe.set(t,u))}}function j(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ze.test(e.type)?t.checked=e.checked:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}function A(e,t,r,i){t=oe.apply([],t);var o,a,s,u,l,c,f=0,p=e.length,d=p-1,h=t[0],g=he.isFunction(h);if(g||p>1&&"string"==typeof h&&!de.checkClone&&tt.test(h))return e.each(function(n){var o=e.eq(n);g&&(t[0]=h.call(this,n,o.html())),A(o,t,r,i)});if(p&&(o=b(t,e[0].ownerDocument,!1,e,i),a=o.firstChild,1===o.childNodes.length&&(o=a),a||i)){for(s=he.map(y(o,"script"),S),u=s.length;f<p;f++)l=o,f!==d&&(l=he.clone(l,!0,!0),u&&he.merge(s,y(l,"script"))),r.call(e[f],l,f);if(u)for(c=s[s.length-1].ownerDocument,he.map(s,N),f=0;f<u;f++)l=s[f],Ue.test(l.type||"")&&!Fe.access(l,"globalEval")&&he.contains(c,l)&&(l.src?he._evalUrl&&he._evalUrl(l.src):n(l.textContent.replace(rt,""),c))}return e}function q(e,t,n){for(var r,i=t?he.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||he.cleanData(y(r)),r.parentNode&&(n&&he.contains(r.ownerDocument,r)&&x(y(r,"script")),r.parentNode.removeChild(r));return e}function L(e,t,n){var r,i,o,a,s=e.style;return n=n||at(e),n&&(a=n.getPropertyValue(t)||n[t],""!==a||he.contains(e.ownerDocument,e)||(a=he.style(e,t)),!de.pixelMarginRight()&&ot.test(a)&&it.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function H(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function F(e){if(e in pt)return e;for(var t=e[0].toUpperCase()+e.slice(1),n=ft.length;n--;)if((e=ft[n]+t)in pt)return e}function O(e){var t=he.cssProps[e];return t||(t=he.cssProps[e]=F(e)||e),t}function P(e,t,n){var r=Ie.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function R(e,t,n,r,i){var o,a=0;for(o=n===(r?"border":"content")?4:"width"===t?1:0;o<4;o+=2)"margin"===n&&(a+=he.css(e,n+We[o],!0,i)),r?("content"===n&&(a-=he.css(e,"padding"+We[o],!0,i)),"margin"!==n&&(a-=he.css(e,"border"+We[o]+"Width",!0,i))):(a+=he.css(e,"padding"+We[o],!0,i),"padding"!==n&&(a+=he.css(e,"border"+We[o]+"Width",!0,i)));return a}function M(e,t,n){var r,i=at(e),o=L(e,t,i),a="border-box"===he.css(e,"boxSizing",!1,i);return ot.test(o)?o:(r=a&&(de.boxSizingReliable()||o===e.style[t]),"auto"===o&&(o=e["offset"+t[0].toUpperCase()+t.slice(1)]),(o=parseFloat(o)||0)+R(e,t,n||(a?"border":"content"),r,i)+"px")}function I(e,t,n,r,i){return new I.prototype.init(e,t,n,r,i)}function W(){ht&&(!1===ne.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(W):e.setTimeout(W,he.fx.interval),he.fx.tick())}function $(){return e.setTimeout(function(){dt=void 0}),dt=he.now()}function B(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)n=We[r],i["margin"+n]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function _(e,t,n){for(var r,i=(U.tweeners[t]||[]).concat(U.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function z(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&$e(e),v=Fe.get(e,"fxshow");n.queue||(a=he._queueHooks(e,"fx"),null==a.unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,he.queue(e,"fx").length||a.empty.fire()})}));for(r in t)if(i=t[r],gt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||he.style(e,r)}if((u=!he.isEmptyObject(t))||!he.isEmptyObject(d)){f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],l=v&&v.display,null==l&&(l=Fe.get(e,"display")),c=he.css(e,"display"),"none"===c&&(l?c=l:(m([e],!0),l=e.style.display||l,c=he.css(e,"display"),m([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===he.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1;for(r in d)u||(v?"hidden"in v&&(g=v.hidden):v=Fe.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&m([e],!0),p.done(function(){g||m([e]),Fe.remove(e,"fxshow");for(r in d)he.style(e,r,d[r])})),u=_(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}}function X(e,t){var n,r,i,o,a;for(n in e)if(r=he.camelCase(n),i=t[r],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=he.cssHooks[r])&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function U(e,t,n){var r,i,o=0,a=U.prefilters.length,s=he.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=dt||$(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;a<u;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),o<1&&u?n:(u||s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:he.extend({},t),opts:he.extend(!0,{specialEasing:{},easing:he.easing._default},n),originalProperties:t,originalOptions:n,startTime:dt||$(),duration:n.duration,tweens:[],createTween:function(t,n){var r=he.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;n<r;n++)l.tweens[n].run(1);return t?(s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l,t])):s.rejectWith(e,[l,t]),this}}),c=l.props;for(X(c,l.opts.specialEasing);o<a;o++)if(r=U.prefilters[o].call(l,e,c,l.opts))return he.isFunction(r.stop)&&(he._queueHooks(l.elem,l.opts.queue).stop=he.proxy(r.stop,r)),r;return he.map(c,_,l),he.isFunction(l.opts.start)&&l.opts.start.call(e,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),he.fx.timer(he.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l}function V(e){return(e.match(je)||[]).join(" ")}function G(e){return e.getAttribute&&e.getAttribute("class")||""}function Y(e,t,n,r){var i;if(Array.isArray(t))he.each(t,function(t,i){n||St.test(e)?r(e,i):Y(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==he.type(t))r(e,t);else for(i in t)Y(e+"["+i+"]",t[i],n,r)}function Q(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(je)||[];if(he.isFunction(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function J(e,t,n,r){function i(s){var u;return o[s]=!0,he.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||a||o[l]?a?!(u=l):void 0:(t.dataTypes.unshift(l),i(l),!1)}),u}var o={},a=e===Mt;return i(t.dataTypes[0])||!o["*"]&&i("*")}function K(e,t){var n,r,i=he.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&he.extend(!0,e,r),e}function Z(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function ee(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if(s=i.split(" "),s[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}var te=[],ne=e.document,re=Object.getPrototypeOf,ie=te.slice,oe=te.concat,ae=te.push,se=te.indexOf,ue={},le=ue.toString,ce=ue.hasOwnProperty,fe=ce.toString,pe=fe.call(Object),de={},he=function(e,t){return new he.fn.init(e,t)},ge=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,ve=/^-ms-/,me=/-([a-z])/g,ye=function(e,t){return t.toUpperCase()};he.fn=he.prototype={jquery:"3.2.1",constructor:he,length:0,toArray:function(){return ie.call(this)},get:function(e){return null==e?ie.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=he.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return he.each(this,e)},map:function(e){return this.pushStack(he.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(ie.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:ae,sort:te.sort,splice:te.splice},he.extend=he.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||he.isFunction(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],r=e[t],a!==r&&(l&&r&&(he.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&he.isPlainObject(n)?n:{},a[t]=he.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},he.extend({expando:"jQuery"+("3.2.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isFunction:function(e){return"function"===he.type(e)},isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){var t=he.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==le.call(e))&&(!(t=re(e))||"function"==typeof(n=ce.call(t,"constructor")&&t.constructor)&&fe.call(n)===pe)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(e){n(e)},camelCase:function(e){return e.replace(ve,"ms-").replace(me,ye)},each:function(e,t){var n,i=0;if(r(e))for(n=e.length;i<n&&!1!==t.call(e[i],i,e[i]);i++);else for(i in e)if(!1===t.call(e[i],i,e[i]))break;return e},trim:function(e){return null==e?"":(e+"").replace(ge,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(r(Object(e))?he.merge(n,"string"==typeof e?[e]:e):ae.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:se.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var i,o,a=0,s=[];if(r(e))for(i=e.length;a<i;a++)null!=(o=t(e[a],a,n))&&s.push(o);else for(a in e)null!=(o=t(e[a],a,n))&&s.push(o);return oe.apply([],s)},guid:1,proxy:function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),he.isFunction(e))return r=ie.call(arguments,2),i=function(){return e.apply(t||this,r.concat(ie.call(arguments)))},i.guid=e.guid=e.guid||he.guid++,i},now:Date.now,support:de}),"function"==typeof Symbol&&(he.fn[Symbol.iterator]=te[Symbol.iterator]),he.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){ue["[object "+t+"]"]=t.toLowerCase()});var xe=function(e){function t(e,t,n,r){var i,o,a,s,u,c,p,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!r&&((t?t.ownerDocument||t:I)!==q&&A(t),t=t||q,H)){if(11!==h&&(u=ge.exec(e)))if(i=u[1]){if(9===h){if(!(a=t.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(d&&(a=d.getElementById(i))&&R(t,a)&&a.id===i)return n.push(a),n}else{if(u[2])return Q.apply(n,t.getElementsByTagName(e)),n;if((i=u[3])&&b.getElementsByClassName&&t.getElementsByClassName)return Q.apply(n,t.getElementsByClassName(i)),n}if(b.qsa&&!z[e+" "]&&(!F||!F.test(e))){if(1!==h)d=t,p=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(xe,be):t.setAttribute("id",s=M),c=E(e),o=c.length;o--;)c[o]="#"+s+" "+f(c[o]);p=c.join(","),d=ve.test(e)&&l(t.parentNode)||t}if(p)try{return Q.apply(n,d.querySelectorAll(p)),n}catch(e){}finally{s===M&&t.removeAttribute("id")}}}return S(e.replace(oe,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>w.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[M]=!0,e}function i(e){var t=q.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)w.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&Te(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function l(e){return e&&void 0!==e.getElementsByTagName&&e}function c(){}function f(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=$++;return t.first?function(t,n,i){for(;t=t[r];)if(1===t.nodeType||a)return e(t,n,i);return!1}:function(t,n,u){var l,c,f,p=[W,s];if(u){for(;t=t[r];)if((1===t.nodeType||a)&&e(t,n,u))return!0}else for(;t=t[r];)if(1===t.nodeType||a)if(f=t[M]||(t[M]={}),c=f[t.uniqueID]||(f[t.uniqueID]={}),i&&i===t.nodeName.toLowerCase())t=t[r]||t;else{if((l=c[o])&&l[0]===W&&l[1]===s)return p[2]=l[2];if(c[o]=p,p[2]=e(t,n,u))return!0}return!1}}function d(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function h(e,n,r){for(var i=0,o=n.length;i<o;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function v(e,t,n,i,o,a){return i&&!i[M]&&(i=v(i)),o&&!o[M]&&(o=v(o,a)),r(function(r,a,s,u){var l,c,f,p=[],d=[],v=a.length,m=r||h(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?m:g(m,p,e,s,u),x=n?o||(r?e:v||i)?[]:a:y;if(n&&n(y,x,s,u),i)for(l=g(x,d),i(l,[],s,u),c=l.length;c--;)(f=l[c])&&(x[d[c]]=!(y[d[c]]=f));if(r){if(o||e){if(o){for(l=[],c=x.length;c--;)(f=x[c])&&l.push(y[c]=f);o(null,x=[],l,u)}for(c=x.length;c--;)(f=x[c])&&(l=o?K(r,f):p[c])>-1&&(r[l]=!(a[l]=f))}}else x=g(x===a?x.splice(v,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function m(e){for(var t,n,r,i=e.length,o=w.relative[e[0].type],a=o||w.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return K(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==N)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s<i;s++)if(n=w.relative[e[s].type])c=[p(d(c),n)];else{if(n=w.filter[e[s].type].apply(null,e[s].matches),n[M]){for(r=++s;r<i&&!w.relative[e[r].type];r++);return v(s>1&&d(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(oe,"$1"),n,s<r&&m(e.slice(s,r)),r<i&&m(e=e.slice(r)),r<i&&f(e))}c.push(n)}return d(c)}function y(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,u,l){var c,f,p,d=0,h="0",v=r&&[],m=[],y=N,x=r||o&&w.find.TAG("*",l),b=W+=null==y?1:Math.random()||.1,T=x.length;for(l&&(N=a===q||a||l);h!==T&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===q||(A(c),s=!H);p=e[f++];)if(p(c,a||q,s)){u.push(c);break}l&&(W=b)}i&&((c=!p&&c)&&d--,r&&v.push(c))}if(d+=h,i&&h!==d){for(f=0;p=n[f++];)p(v,m,a,s);if(r){if(d>0)for(;h--;)v[h]||m[h]||(m[h]=G.call(u));m=g(m)}Q.apply(u,m),l&&!r&&m.length>0&&d+n.length>1&&t.uniqueSort(u)}return l&&(W=b,N=y),v};return i?r(a):a}var x,b,w,T,C,E,k,S,N,D,j,A,q,L,H,F,O,P,R,M="sizzle"+1*new Date,I=e.document,W=0,$=0,B=n(),_=n(),z=n(),X=function(e,t){return e===t&&(j=!0),0},U={}.hasOwnProperty,V=[],G=V.pop,Y=V.push,Q=V.push,J=V.slice,K=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},Z="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ee="[\\x20\\t\\r\\n\\f]",te="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",ne="\\["+ee+"*("+te+")(?:"+ee+"*([*^$|!~]?=)"+ee+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+te+"))|)"+ee+"*\\]",re=":("+te+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ne+")*)|.*)\\)|)",ie=new RegExp(ee+"+","g"),oe=new RegExp("^"+ee+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ee+"+$","g"),ae=new RegExp("^"+ee+"*,"+ee+"*"),se=new RegExp("^"+ee+"*([>+~]|"+ee+")"+ee+"*"),ue=new RegExp("="+ee+"*([^\\]'\"]*?)"+ee+"*\\]","g"),le=new RegExp(re),ce=new RegExp("^"+te+"$"),fe={ID:new RegExp("^#("+te+")"),CLASS:new RegExp("^\\.("+te+")"),TAG:new RegExp("^("+te+"|[*])"),ATTR:new RegExp("^"+ne),PSEUDO:new RegExp("^"+re),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ee+"*(even|odd|(([+-]|)(\\d*)n|)"+ee+"*(?:([+-]|)"+ee+"*(\\d+)|))"+ee+"*\\)|)","i"),bool:new RegExp("^(?:"+Z+")$","i"),needsContext:new RegExp("^"+ee+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ee+"*((?:-\\d)?\\d*)"+ee+"*\\)|)(?=[^-]|$)","i")},pe=/^(?:input|select|textarea|button)$/i,de=/^h\d$/i,he=/^[^{]+\{\s*\[native \w/,ge=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,me=new RegExp("\\\\([\\da-f]{1,6}"+ee+"?|("+ee+")|.)","ig"),ye=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},xe=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,be=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},we=function(){A()},Te=p(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{Q.apply(V=J.call(I.childNodes),I.childNodes),V[I.childNodes.length].nodeType}catch(e){Q={apply:V.length?function(e,t){Y.apply(e,J.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}b=t.support={},C=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},A=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:I;return r!==q&&9===r.nodeType&&r.documentElement?(q=r,L=q.documentElement,H=!C(q),I!==q&&(n=q.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",we,!1):n.attachEvent&&n.attachEvent("onunload",we)),b.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),b.getElementsByTagName=i(function(e){return e.appendChild(q.createComment("")),!e.getElementsByTagName("*").length}),b.getElementsByClassName=he.test(q.getElementsByClassName),b.getById=i(function(e){return L.appendChild(e).id=M,!q.getElementsByName||!q.getElementsByName(M).length}),b.getById?(w.filter.ID=function(e){var t=e.replace(me,ye);return function(e){return e.getAttribute("id")===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n=t.getElementById(e);return n?[n]:[]}}):(w.filter.ID=function(e){var t=e.replace(me,ye);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),w.find.TAG=b.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):b.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},w.find.CLASS=b.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&H)return t.getElementsByClassName(e)},O=[],F=[],(b.qsa=he.test(q.querySelectorAll))&&(i(function(e){L.appendChild(e).innerHTML="<a id='"+M+"'></a><select id='"+M+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ee+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ee+"*(?:value|"+Z+")"),e.querySelectorAll("[id~="+M+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+M+"+*").length||F.push(".#.+[+~]")}),i(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=q.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ee+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&F.push(":enabled",":disabled"),L.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(b.matchesSelector=he.test(P=L.matches||L.webkitMatchesSelector||L.mozMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&i(function(e){b.disconnectedMatch=P.call(e,"*"),P.call(e,"[s!='']:x"),O.push("!=",re)}),F=F.length&&new RegExp(F.join("|")),O=O.length&&new RegExp(O.join("|")),t=he.test(L.compareDocumentPosition),R=t||he.test(L.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},X=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!b.sortDetached&&t.compareDocumentPosition(e)===n?e===q||e.ownerDocument===I&&R(I,e)?-1:t===q||t.ownerDocument===I&&R(I,t)?1:D?K(D,e)-K(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===q?-1:t===q?1:i?-1:o?1:D?K(D,e)-K(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===I?-1:u[r]===I?1:0},q):q},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==q&&A(e),n=n.replace(ue,"='$1']"),b.matchesSelector&&H&&!z[n+" "]&&(!O||!O.test(n))&&(!F||!F.test(n)))try{var r=P.call(e,n);if(r||b.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return t(n,q,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==q&&A(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==q&&A(e);var n=w.attrHandle[t.toLowerCase()],r=n&&U.call(w.attrHandle,t.toLowerCase())?n(e,t,!H):void 0;return void 0!==r?r:b.attributes||!H?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.escape=function(e){return(e+"").replace(xe,be)},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!b.detectDuplicates,D=!b.sortStable&&e.slice(0),e.sort(X),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},T=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=T(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=T(t);return n},w=t.selectors={cacheLength:50,createPseudo:r,match:fe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(me,ye),e[3]=(e[3]||e[4]||e[5]||"").replace(me,ye),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return fe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&le.test(n)&&(t=E(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(me,ye).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=B[e+" "];return t||(t=new RegExp("(^|"+ee+")"+e+"("+ee+"|$)"))&&B(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ie," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,m=s&&t.nodeName.toLowerCase(),y=!u&&!s,x=!1;if(v){if(o){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===m:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&y){for(p=v,f=p[M]||(p[M]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===W&&l[1],x=d&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[W,d,x];break}}else if(y&&(p=t,f=p[M]||(p[M]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===W&&l[1],x=d),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==m:1!==p.nodeType)||!++x||(y&&(f=p[M]||(p[M]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),c[e]=[W,x]),p!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=w.pseudos[e]||w.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[M]?o(n):o.length>1?(i=[e,e,"",n],w.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=K(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(oe,"$1"));return i[M]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(me,ye),function(t){return(t.textContent||t.innerText||T(t)).indexOf(e)>-1}}),lang:r(function(e){return ce.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(me,ye).toLowerCase(),function(t){var n;do{if(n=H?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===L},focus:function(e){return e===q.activeElement&&(!q.hasFocus||q.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:s(!1),disabled:s(!0),checked:function(e){var t=e.nodeName.toLowerCase()
-;return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!w.pseudos.empty(e)},header:function(e){return de.test(e.nodeName)},input:function(e){return pe.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[n<0?n+t:n]}),even:u(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},w.pseudos.nth=w.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})w.pseudos[x]=function(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}(x);for(x in{submit:!0,reset:!0})w.pseudos[x]=function(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}(x);return c.prototype=w.filters=w.pseudos,w.setFilters=new c,E=t.tokenize=function(e,n){var r,i,o,a,s,u,l,c=_[e+" "];if(c)return n?0:c.slice(0);for(s=e,u=[],l=w.preFilter;s;){r&&!(i=ae.exec(s))||(i&&(s=s.slice(i[0].length)||s),u.push(o=[])),r=!1,(i=se.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(oe," ")}),s=s.slice(r.length));for(a in w.filter)!(i=fe[a].exec(s))||l[a]&&!(i=l[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):_(e,u).slice(0)},k=t.compile=function(e,t){var n,r=[],i=[],o=z[e+" "];if(!o){for(t||(t=E(e)),n=t.length;n--;)o=m(t[n]),o[M]?r.push(o):i.push(o);o=z(e,y(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,u,c="function"==typeof e&&e,p=!r&&E(e=c.selector||e);if(n=n||[],1===p.length){if(o=p[0]=p[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&H&&w.relative[o[1].type]){if(!(t=(w.find.ID(a.matches[0].replace(me,ye),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=fe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!w.relative[s=a.type]);)if((u=w.find[s])&&(r=u(a.matches[0].replace(me,ye),ve.test(o[0].type)&&l(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&f(o)))return Q.apply(n,r),n;break}}return(c||k(e,p))(r,t,!H,n,!t||ve.test(e)&&l(t.parentNode)||t),n},b.sortStable=M.split("").sort(X).join("")===M,b.detectDuplicates=!!j,A(),b.sortDetached=i(function(e){return 1&e.compareDocumentPosition(q.createElement("fieldset"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),b.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(Z,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);he.find=xe,he.expr=xe.selectors,he.expr[":"]=he.expr.pseudos,he.uniqueSort=he.unique=xe.uniqueSort,he.text=xe.getText,he.isXMLDoc=xe.isXML,he.contains=xe.contains,he.escapeSelector=xe.escape;var be=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&he(e).is(n))break;r.push(e)}return r},we=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},Te=he.expr.match.needsContext,Ce=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,Ee=/^.[^:#\[\.,]*$/;he.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?he.find.matchesSelector(r,e)?[r]:[]:he.find.matches(e,he.grep(t,function(e){return 1===e.nodeType}))},he.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(he(e).filter(function(){for(t=0;t<r;t++)if(he.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)he.find(e,i[t],n);return r>1?he.uniqueSort(n):n},filter:function(e){return this.pushStack(o(this,e||[],!1))},not:function(e){return this.pushStack(o(this,e||[],!0))},is:function(e){return!!o(this,"string"==typeof e&&Te.test(e)?he(e):e||[],!1).length}});var ke,Se=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(he.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||ke,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:Se.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof he?t[0]:t,he.merge(this,he.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:ne,!0)),Ce.test(r[1])&&he.isPlainObject(t))for(r in t)he.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=ne.getElementById(r[2]),i&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):he.isFunction(e)?void 0!==n.ready?n.ready(e):e(he):he.makeArray(e,this)}).prototype=he.fn,ke=he(ne);var Ne=/^(?:parents|prev(?:Until|All))/,De={children:!0,contents:!0,next:!0,prev:!0};he.fn.extend({has:function(e){var t=he(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(he.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&he(e);if(!Te.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&he.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?he.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?se.call(he(e),this[0]):se.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(he.uniqueSort(he.merge(this.get(),he(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),he.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return be(e,"parentNode")},parentsUntil:function(e,t,n){return be(e,"parentNode",n)},next:function(e){return a(e,"nextSibling")},prev:function(e){return a(e,"previousSibling")},nextAll:function(e){return be(e,"nextSibling")},prevAll:function(e){return be(e,"previousSibling")},nextUntil:function(e,t,n){return be(e,"nextSibling",n)},prevUntil:function(e,t,n){return be(e,"previousSibling",n)},siblings:function(e){return we((e.parentNode||{}).firstChild,e)},children:function(e){return we(e.firstChild)},contents:function(e){return i(e,"iframe")?e.contentDocument:(i(e,"template")&&(e=e.content||e),he.merge([],e.childNodes))}},function(e,t){he.fn[e]=function(n,r){var i=he.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=he.filter(r,i)),this.length>1&&(De[e]||he.uniqueSort(i),Ne.test(e)&&i.reverse()),this.pushStack(i)}});var je=/[^\x20\t\r\n\f]+/g;he.Callbacks=function(e){e="string"==typeof e?s(e):he.extend({},e);var t,n,r,i,o=[],a=[],u=-1,l=function(){for(i=i||e.once,r=t=!0;a.length;u=-1)for(n=a.shift();++u<o.length;)!1===o[u].apply(n[0],n[1])&&e.stopOnFalse&&(u=o.length,n=!1);e.memory||(n=!1),t=!1,i&&(o=n?[]:"")},c={add:function(){return o&&(n&&!t&&(u=o.length-1,a.push(n)),function t(n){he.each(n,function(n,r){he.isFunction(r)?e.unique&&c.has(r)||o.push(r):r&&r.length&&"string"!==he.type(r)&&t(r)})}(arguments),n&&!t&&l()),this},remove:function(){return he.each(arguments,function(e,t){for(var n;(n=he.inArray(t,o,n))>-1;)o.splice(n,1),n<=u&&u--}),this},has:function(e){return e?he.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],a.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},he.extend({Deferred:function(t){var n=[["notify","progress",he.Callbacks("memory"),he.Callbacks("memory"),2],["resolve","done",he.Callbacks("once memory"),he.Callbacks("once memory"),0,"resolved"],["reject","fail",he.Callbacks("once memory"),he.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return he.Deferred(function(t){he.each(n,function(n,r){var i=he.isFunction(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&he.isFunction(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){function o(t,n,r,i){return function(){var s=this,c=arguments,f=function(){var e,f;if(!(t<a)){if((e=r.apply(s,c))===n.promise())throw new TypeError("Thenable self-resolution");f=e&&("object"==typeof e||"function"==typeof e)&&e.then,he.isFunction(f)?i?f.call(e,o(a,n,u,i),o(a,n,l,i)):(a++,f.call(e,o(a,n,u,i),o(a,n,l,i),o(a,n,u,n.notifyWith))):(r!==u&&(s=void 0,c=[e]),(i||n.resolveWith)(s,c))}},p=i?f:function(){try{f()}catch(e){he.Deferred.exceptionHook&&he.Deferred.exceptionHook(e,p.stackTrace),t+1>=a&&(r!==l&&(s=void 0,c=[e]),n.rejectWith(s,c))}};t?p():(he.Deferred.getStackHook&&(p.stackTrace=he.Deferred.getStackHook()),e.setTimeout(p))}}var a=0;return he.Deferred(function(e){n[0][3].add(o(0,e,he.isFunction(i)?i:u,e.notifyWith)),n[1][3].add(o(0,e,he.isFunction(t)?t:u)),n[2][3].add(o(0,e,he.isFunction(r)?r:l))}).promise()},promise:function(e){return null!=e?he.extend(e,i):i}},o={};return he.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[0][2].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=ie.call(arguments),o=he.Deferred(),a=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?ie.call(arguments):n,--t||o.resolveWith(r,i)}};if(t<=1&&(c(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||he.isFunction(i[n]&&i[n].then)))return o.then();for(;n--;)c(i[n],a(n),o.reject);return o.promise()}});var Ae=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;he.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&Ae.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},he.readyException=function(t){e.setTimeout(function(){throw t})};var qe=he.Deferred();he.fn.ready=function(e){return qe.then(e).catch(function(e){he.readyException(e)}),this},he.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--he.readyWait:he.isReady)||(he.isReady=!0,!0!==e&&--he.readyWait>0||qe.resolveWith(ne,[he]))}}),he.ready.then=qe.then,"complete"===ne.readyState||"loading"!==ne.readyState&&!ne.documentElement.doScroll?e.setTimeout(he.ready):(ne.addEventListener("DOMContentLoaded",f),e.addEventListener("load",f));var Le=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===he.type(n)){i=!0;for(s in n)Le(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,he.isFunction(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(he(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},He=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};p.uid=1,p.prototype={cache:function(e){var t=e[this.expando];return t||(t={},He(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[he.camelCase(t)]=n;else for(r in t)i[he.camelCase(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][he.camelCase(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){Array.isArray(t)?t=t.map(he.camelCase):(t=he.camelCase(t),t=t in r?[t]:t.match(je)||[]),n=t.length;for(;n--;)delete r[t[n]]}(void 0===t||he.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!he.isEmptyObject(t)}};var Fe=new p,Oe=new p,Pe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Re=/[A-Z]/g;he.extend({hasData:function(e){return Oe.hasData(e)||Fe.hasData(e)},data:function(e,t,n){return Oe.access(e,t,n)},removeData:function(e,t){Oe.remove(e,t)},_data:function(e,t,n){return Fe.access(e,t,n)},_removeData:function(e,t){Fe.remove(e,t)}}),he.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=Oe.get(o),1===o.nodeType&&!Fe.get(o,"hasDataAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=he.camelCase(r.slice(5)),h(o,r,i[r])));Fe.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){Oe.set(this,e)}):Le(this,function(t){var n;if(o&&void 0===t){if(void 0!==(n=Oe.get(o,e)))return n;if(void 0!==(n=h(o,e)))return n}else this.each(function(){Oe.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){Oe.remove(this,e)})}}),he.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Fe.get(e,t),n&&(!r||Array.isArray(n)?r=Fe.access(e,t,he.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=he.queue(e,t),r=n.length,i=n.shift(),o=he._queueHooks(e,t),a=function(){he.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Fe.get(e,n)||Fe.access(e,n,{empty:he.Callbacks("once memory").add(function(){Fe.remove(e,[t+"queue",n])})})}}),he.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?he.queue(this[0],e):void 0===t?this:this.each(function(){var n=he.queue(this,e,t);he._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&he.dequeue(this,e)})},dequeue:function(e){return this.each(function(){he.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=he.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)(n=Fe.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var Me=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,Ie=new RegExp("^(?:([+-])=|)("+Me+")([a-z%]*)$","i"),We=["Top","Right","Bottom","Left"],$e=function(e,t){return e=t||e,"none"===e.style.display||""===e.style.display&&he.contains(e.ownerDocument,e)&&"none"===he.css(e,"display")},Be=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i},_e={};he.fn.extend({show:function(){return m(this,!0)},hide:function(){return m(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){$e(this)?he(this).show():he(this).hide()})}});var ze=/^(?:checkbox|radio)$/i,Xe=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,Ue=/^$|\/(?:java|ecma)script/i,Ve={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};Ve.optgroup=Ve.option,Ve.tbody=Ve.tfoot=Ve.colgroup=Ve.caption=Ve.thead,Ve.th=Ve.td;var Ge=/<|&#?\w+;/;!function(){var e=ne.createDocumentFragment(),t=e.appendChild(ne.createElement("div")),n=ne.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),de.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="<textarea>x</textarea>",de.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var Ye=ne.documentElement,Qe=/^key/,Je=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ke=/^([^.]*)(?:\.(.+)|)/;he.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Fe.get(e);if(v)for(n.handler&&(o=n,n=o.handler,i=o.selector),i&&he.find.matchesSelector(Ye,i),n.guid||(n.guid=he.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==he&&he.event.triggered!==t.type?he.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(je)||[""],l=t.length;l--;)s=Ke.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d&&(f=he.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=he.event.special[d]||{},c=he.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&he.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||(p=u[d]=[],p.delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),he.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Fe.hasData(e)&&Fe.get(e);if(v&&(u=v.events)){for(t=(t||"").match(je)||[""],l=t.length;l--;)if(s=Ke.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){for(f=he.event.special[d]||{},d=(r?f.delegateType:f.bindType)||d,p=u[d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||he.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)he.event.remove(e,d+t[l],n,r,!0);he.isEmptyObject(u)&&Fe.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=he.event.fix(e),u=new Array(arguments.length),l=(Fe.get(this,"events")||{})[s.type]||[],c=he.event.special[s.type]||{};for(u[0]=s,t=1;t<arguments.length;t++)u[t]=arguments[t];if(s.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,s)){for(a=he.event.handlers.call(this,s,l),t=0;(i=a[t++])&&!s.isPropagationStopped();)for(s.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!s.isImmediatePropagationStopped();)s.rnamespace&&!s.rnamespace.test(o.namespace)||(s.handleObj=o,s.data=o.data,void 0!==(r=((he.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,u))&&!1===(s.result=r)&&(s.preventDefault(),s.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,s),s.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&e.button>=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)r=t[n],i=r.selector+" ",void 0===a[i]&&(a[i]=r.needsContext?he(i,this).index(l)>-1:he.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(e,t){Object.defineProperty(he.Event.prototype,e,{enumerable:!0,configurable:!0,get:he.isFunction(t)?function(){if(this.originalEvent)return t(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[e]},set:function(t){Object.defineProperty(this,e,{enumerable:!0,configurable:!0,writable:!0,value:t})}})},fix:function(e){return e[he.expando]?e:new he.Event(e)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==C()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===C()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&i(this,"input"))return this.click(),!1},_default:function(e){return i(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},he.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},he.Event=function(e,t){if(!(this instanceof he.Event))return new he.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?w:T,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&he.extend(this,t),this.timeStamp=e&&e.timeStamp||he.now(),this[he.expando]=!0},he.Event.prototype={constructor:he.Event,isDefaultPrevented:T,isPropagationStopped:T,isImmediatePropagationStopped:T,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=w,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=w,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=w,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},he.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,char:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&Qe.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&Je.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},he.event.addProp),he.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){he.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return i&&(i===r||he.contains(r,i))||(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),he.fn.extend({on:function(e,t,n,r){return E(this,e,t,n,r)},one:function(e,t,n,r){return E(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,he(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=T),this.each(function(){he.event.remove(this,e,n,t)})}});var Ze=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,et=/<script|<style|<link/i,tt=/checked\s*(?:[^=]|=\s*.checked.)/i,nt=/^true\/(.*)/,rt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;he.extend({htmlPrefilter:function(e){return e.replace(Ze,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=he.contains(e.ownerDocument,e);if(!(de.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||he.isXMLDoc(e)))for(a=y(s),o=y(e),r=0,i=o.length;r<i;r++)j(o[r],a[r]);if(t)if(n)for(o=o||y(e),a=a||y(s),r=0,i=o.length;r<i;r++)D(o[r],a[r]);else D(e,s);return a=y(s,"script"),a.length>0&&x(a,!u&&y(e,"script")),s},cleanData:function(e){for(var t,n,r,i=he.event.special,o=0;void 0!==(n=e[o]);o++)if(He(n)){if(t=n[Fe.expando]){if(t.events)for(r in t.events)i[r]?he.event.remove(n,r):he.removeEvent(n,r,t.handle);n[Fe.expando]=void 0}n[Oe.expando]&&(n[Oe.expando]=void 0)}}}),he.fn.extend({detach:function(e){return q(this,e,!0)},remove:function(e){return q(this,e)},text:function(e){return Le(this,function(e){return void 0===e?he.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){k(this,e).appendChild(e)}})},prepend:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=k(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(he.cleanData(y(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return he.clone(this,e,t)})},html:function(e){return Le(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!et.test(e)&&!Ve[(Xe.exec(e)||["",""])[1].toLowerCase()]){e=he.htmlPrefilter(e);try{for(;n<r;n++)t=this[n]||{},1===t.nodeType&&(he.cleanData(y(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return A(this,arguments,function(t){var n=this.parentNode;he.inArray(this,e)<0&&(he.cleanData(y(this)),n&&n.replaceChild(t,this))},e)}}),he.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){he.fn[e]=function(e){for(var n,r=[],i=he(e),o=i.length-1,a=0;a<=o;a++)n=a===o?this:this.clone(!0),he(i[a])[t](n),ae.apply(r,n.get());return this.pushStack(r)}});var it=/^margin/,ot=new RegExp("^("+Me+")(?!px)[a-z%]+$","i"),at=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)};!function(){function t(){if(s){s.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",s.innerHTML="",Ye.appendChild(a);var t=e.getComputedStyle(s);n="1%"!==t.top,o="2px"===t.marginLeft,r="4px"===t.width,s.style.marginRight="50%",i="4px"===t.marginRight,Ye.removeChild(a),s=null}}var n,r,i,o,a=ne.createElement("div"),s=ne.createElement("div");s.style&&(s.style.backgroundClip="content-box",s.cloneNode(!0).style.backgroundClip="",de.clearCloneStyle="content-box"===s.style.backgroundClip,a.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",a.appendChild(s),he.extend(de,{pixelPosition:function(){return t(),n},boxSizingReliable:function(){return t(),r},pixelMarginRight:function(){return t(),i},reliableMarginLeft:function(){return t(),o}}))}();var st=/^(none|table(?!-c[ea]).+)/,ut=/^--/,lt={position:"absolute",visibility:"hidden",display:"block"},ct={letterSpacing:"0",fontWeight:"400"},ft=["Webkit","Moz","ms"],pt=ne.createElement("div").style;he.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=L(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{float:"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=he.camelCase(t),u=ut.test(t),l=e.style;if(u||(t=O(s)),a=he.cssHooks[t]||he.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];o=typeof n,"string"===o&&(i=Ie.exec(n))&&i[1]&&(n=g(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(he.cssNumber[s]?"":"px")),de.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=he.camelCase(t);return ut.test(t)||(t=O(s)),a=he.cssHooks[t]||he.cssHooks[s],a&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=L(e,t,r)),"normal"===i&&t in ct&&(i=ct[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),he.each(["height","width"],function(e,t){he.cssHooks[t]={get:function(e,n,r){if(n)return!st.test(he.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?M(e,t,r):Be(e,lt,function(){return M(e,t,r)})},set:function(e,n,r){var i,o=r&&at(e),a=r&&R(e,t,r,"border-box"===he.css(e,"boxSizing",!1,o),o);return a&&(i=Ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=he.css(e,t)),P(e,n,a)}}}),he.cssHooks.marginLeft=H(de.reliableMarginLeft,function(e,t){if(t)return(parseFloat(L(e,"marginLeft"))||e.getBoundingClientRect().left-Be(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),he.each({margin:"",padding:"",border:"Width"},function(e,t){he.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+We[r]+t]=o[r]||o[r-2]||o[0];return i}},it.test(e)||(he.cssHooks[e+t].set=P)}),he.fn.extend({css:function(e,t){return Le(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=at(e),i=t.length;a<i;a++)o[t[a]]=he.css(e,t[a],!1,r);return o}return void 0!==n?he.style(e,t,n):he.css(e,t)},e,t,arguments.length>1)}}),he.Tween=I,I.prototype={constructor:I,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||he.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(he.cssNumber[n]?"":"px")},cur:function(){var e=I.propHooks[this.prop];return e&&e.get?e.get(this):I.propHooks._default.get(this)},run:function(e){var t,n=I.propHooks[this.prop];return this.options.duration?this.pos=t=he.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):I.propHooks._default.set(this),this}},I.prototype.init.prototype=I.prototype,I.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=he.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){he.fx.step[e.prop]?he.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[he.cssProps[e.prop]]&&!he.cssHooks[e.prop]?e.elem[e.prop]=e.now:he.style(e.elem,e.prop,e.now+e.unit)}}},I.propHooks.scrollTop=I.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},he.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},he.fx=I.prototype.init,he.fx.step={};var dt,ht,gt=/^(?:toggle|show|hide)$/,vt=/queueHooks$/;he.Animation=he.extend(U,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return g(n.elem,e,Ie.exec(t),n),n}]},tweener:function(e,t){he.isFunction(e)?(t=e,e=["*"]):e=e.match(je);for(var n,r=0,i=e.length;r<i;r++)n=e[r],U.tweeners[n]=U.tweeners[n]||[],U.tweeners[n].unshift(t)},prefilters:[z],prefilter:function(e,t){t?U.prefilters.unshift(e):U.prefilters.push(e)}}),he.speed=function(e,t,n){var r=e&&"object"==typeof e?he.extend({},e):{complete:n||!n&&t||he.isFunction(e)&&e,duration:e,easing:n&&t||t&&!he.isFunction(t)&&t};return he.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in he.fx.speeds?r.duration=he.fx.speeds[r.duration]:r.duration=he.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){he.isFunction(r.old)&&r.old.call(this),r.queue&&he.dequeue(this,r.queue)},r},he.fn.extend({fadeTo:function(e,t,n,r){return this.filter($e).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=he.isEmptyObject(e),o=he.speed(t,n,r),a=function(){var t=U(this,he.extend({},e),o);(i||Fe.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&!1!==e&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=he.timers,a=Fe.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&vt.test(i)&&r(a[i])
-;for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));!t&&n||he.dequeue(this,e)})},finish:function(e){return!1!==e&&(e=e||"fx"),this.each(function(){var t,n=Fe.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=he.timers,a=r?r.length:0;for(n.finish=!0,he.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;t<a;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),he.each(["toggle","show","hide"],function(e,t){var n=he.fn[t];he.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(B(t,!0),e,r,i)}}),he.each({slideDown:B("show"),slideUp:B("hide"),slideToggle:B("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){he.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),he.timers=[],he.fx.tick=function(){var e,t=0,n=he.timers;for(dt=he.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||he.fx.stop(),dt=void 0},he.fx.timer=function(e){he.timers.push(e),he.fx.start()},he.fx.interval=13,he.fx.start=function(){ht||(ht=!0,W())},he.fx.stop=function(){ht=null},he.fx.speeds={slow:600,fast:200,_default:400},he.fn.delay=function(t,n){return t=he.fx?he.fx.speeds[t]||t:t,n=n||"fx",this.queue(n,function(n,r){var i=e.setTimeout(n,t);r.stop=function(){e.clearTimeout(i)}})},function(){var e=ne.createElement("input"),t=ne.createElement("select"),n=t.appendChild(ne.createElement("option"));e.type="checkbox",de.checkOn=""!==e.value,de.optSelected=n.selected,e=ne.createElement("input"),e.value="t",e.type="radio",de.radioValue="t"===e.value}();var mt,yt=he.expr.attrHandle;he.fn.extend({attr:function(e,t){return Le(this,he.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){he.removeAttr(this,e)})}}),he.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?he.prop(e,t,n):(1===o&&he.isXMLDoc(e)||(i=he.attrHooks[t.toLowerCase()]||(he.expr.match.bool.test(t)?mt:void 0)),void 0!==n?null===n?void he.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=he.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!de.radioValue&&"radio"===t&&i(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(je);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),mt={set:function(e,t,n){return!1===t?he.removeAttr(e,n):e.setAttribute(n,n),n}},he.each(he.expr.match.bool.source.match(/\w+/g),function(e,t){var n=yt[t]||he.find.attr;yt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=yt[a],yt[a]=i,i=null!=n(e,t,r)?a:null,yt[a]=o),i}});var xt=/^(?:input|select|textarea|button)$/i,bt=/^(?:a|area)$/i;he.fn.extend({prop:function(e,t){return Le(this,he.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[he.propFix[e]||e]})}}),he.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&he.isXMLDoc(e)||(t=he.propFix[t]||t,i=he.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=he.find.attr(e,"tabindex");return t?parseInt(t,10):xt.test(e.nodeName)||bt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),de.optSelected||(he.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),he.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){he.propFix[this.toLowerCase()]=this}),he.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(he.isFunction(e))return this.each(function(t){he(this).addClass(e.call(this,t,G(this)))});if("string"==typeof e&&e)for(t=e.match(je)||[];n=this[u++];)if(i=G(n),r=1===n.nodeType&&" "+V(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=V(r),i!==s&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(he.isFunction(e))return this.each(function(t){he(this).removeClass(e.call(this,t,G(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(je)||[];n=this[u++];)if(i=G(n),r=1===n.nodeType&&" "+V(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=V(r),i!==s&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):he.isFunction(e)?this.each(function(n){he(this).toggleClass(e.call(this,n,G(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=he(this),o=e.match(je)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=G(this),t&&Fe.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":Fe.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+V(G(n))+" ").indexOf(t)>-1)return!0;return!1}});var wt=/\r/g;he.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=he.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,he(this).val()):e,null==i?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=he.map(i,function(e){return null==e?"":e+""})),(t=he.valHooks[this.type]||he.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=he.valHooks[i.type]||he.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(wt,""):null==n?"":n)}}}),he.extend({valHooks:{option:{get:function(e){var t=he.find.attr(e,"value");return null!=t?t:V(he.text(e))}},select:{get:function(e){var t,n,r,o=e.options,a=e.selectedIndex,s="select-one"===e.type,u=s?null:[],l=s?a+1:o.length;for(r=a<0?l:s?a:0;r<l;r++)if(n=o[r],(n.selected||r===a)&&!n.disabled&&(!n.parentNode.disabled||!i(n.parentNode,"optgroup"))){if(t=he(n).val(),s)return t;u.push(t)}return u},set:function(e,t){for(var n,r,i=e.options,o=he.makeArray(t),a=i.length;a--;)r=i[a],(r.selected=he.inArray(he.valHooks.option.get(r),o)>-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),he.each(["radio","checkbox"],function(){he.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=he.inArray(he(e).val(),t)>-1}},de.checkOn||(he.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Tt=/^(?:focusinfocus|focusoutblur)$/;he.extend(he.event,{trigger:function(t,n,r,i){var o,a,s,u,l,c,f,p=[r||ne],d=ce.call(t,"type")?t.type:t,h=ce.call(t,"namespace")?t.namespace.split("."):[];if(a=s=r=r||ne,3!==r.nodeType&&8!==r.nodeType&&!Tt.test(d+he.event.triggered)&&(d.indexOf(".")>-1&&(h=d.split("."),d=h.shift(),h.sort()),l=d.indexOf(":")<0&&"on"+d,t=t[he.expando]?t:new he.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:he.makeArray(n,[t]),f=he.event.special[d]||{},i||!f.trigger||!1!==f.trigger.apply(r,n))){if(!i&&!f.noBubble&&!he.isWindow(r)){for(u=f.delegateType||d,Tt.test(u+d)||(a=a.parentNode);a;a=a.parentNode)p.push(a),s=a;s===(r.ownerDocument||ne)&&p.push(s.defaultView||s.parentWindow||e)}for(o=0;(a=p[o++])&&!t.isPropagationStopped();)t.type=o>1?u:f.bindType||d,c=(Fe.get(a,"events")||{})[t.type]&&Fe.get(a,"handle"),c&&c.apply(a,n),(c=l&&a[l])&&c.apply&&He(a)&&(t.result=c.apply(a,n),!1===t.result&&t.preventDefault());return t.type=d,i||t.isDefaultPrevented()||f._default&&!1!==f._default.apply(p.pop(),n)||!He(r)||l&&he.isFunction(r[d])&&!he.isWindow(r)&&(s=r[l],s&&(r[l]=null),he.event.triggered=d,r[d](),he.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=he.extend(new he.Event,n,{type:e,isSimulated:!0});he.event.trigger(r,null,t)}}),he.fn.extend({trigger:function(e,t){return this.each(function(){he.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return he.event.trigger(e,t,n,!0)}}),he.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){he.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),he.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),de.focusin="onfocusin"in e,de.focusin||he.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){he.event.simulate(t,e.target,he.event.fix(e))};he.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=Fe.access(r,t);i||r.addEventListener(e,n,!0),Fe.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=Fe.access(r,t)-1;i?Fe.access(r,t,i):(r.removeEventListener(e,n,!0),Fe.remove(r,t))}}});var Ct=e.location,Et=he.now(),kt=/\?/;he.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||he.error("Invalid XML: "+t),n};var St=/\[\]$/,Nt=/\r?\n/g,Dt=/^(?:submit|button|image|reset|file)$/i,jt=/^(?:input|select|textarea|keygen)/i;he.param=function(e,t){var n,r=[],i=function(e,t){var n=he.isFunction(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!he.isPlainObject(e))he.each(e,function(){i(this.name,this.value)});else for(n in e)Y(n,e[n],t,i);return r.join("&")},he.fn.extend({serialize:function(){return he.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=he.prop(this,"elements");return e?he.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!he(this).is(":disabled")&&jt.test(this.nodeName)&&!Dt.test(e)&&(this.checked||!ze.test(e))}).map(function(e,t){var n=he(this).val();return null==n?null:Array.isArray(n)?he.map(n,function(e){return{name:t.name,value:e.replace(Nt,"\r\n")}}):{name:t.name,value:n.replace(Nt,"\r\n")}}).get()}});var At=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ft=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=ne.createElement("a");Wt.href=Ct.href,he.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Ft.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":he.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?K(K(e,he.ajaxSettings),t):K(he.ajaxSettings,e)},ajaxPrefilter:Q(Rt),ajaxTransport:Q(Mt),ajax:function(t,n){function r(t,n,r,s){var l,p,d,b,w,T=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",C.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Z(h,C,r)),b=ee(h,b,C,l),l?(h.ifModified&&(w=C.getResponseHeader("Last-Modified"),w&&(he.lastModified[o]=w),(w=C.getResponseHeader("etag"))&&(he.etag[o]=w)),204===t||"HEAD"===h.type?T="nocontent":304===t?T="notmodified":(T=b.state,p=b.data,d=b.error,l=!d)):(d=T,!t&&T||(T="error",t<0&&(t=0))),C.status=t,C.statusText=(n||T)+"",l?m.resolveWith(g,[p,T,C]):m.rejectWith(g,[C,T,d]),C.statusCode(x),x=void 0,f&&v.trigger(l?"ajaxSuccess":"ajaxError",[C,h,l?p:d]),y.fireWith(g,[C,T]),f&&(v.trigger("ajaxComplete",[C,h]),--he.active||he.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=he.ajaxSetup({},n),g=h.context||h,v=h.context&&(g.nodeType||g.jquery)?he(g):he.event,m=he.Deferred(),y=he.Callbacks("once memory"),x=h.statusCode||{},b={},w={},T="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=Ht.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)C.always(e[C.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(m.promise(C),h.url=((t||h.url||Ct.href)+"").replace(Pt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(je)||[""],null==h.crossDomain){l=ne.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Wt.protocol+"//"+Wt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=he.param(h.data,h.traditional)),J(Rt,h,n,C),c)return C;f=he.event&&h.global,f&&0==he.active++&&he.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Ot.test(h.type),o=h.url.replace(qt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(At,"+")):(d=h.url.slice(o.length),h.data&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Lt,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(he.lastModified[o]&&C.setRequestHeader("If-Modified-Since",he.lastModified[o]),he.etag[o]&&C.setRequestHeader("If-None-Match",he.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&C.setRequestHeader("Content-Type",h.contentType),C.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+It+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)C.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,C,h)||c))return C.abort();if(T="abort",y.add(h.complete),C.done(h.success),C.fail(h.error),i=J(Mt,h,n,C)){if(C.readyState=1,f&&v.trigger("ajaxSend",[C,h]),c)return C;h.async&&h.timeout>0&&(u=e.setTimeout(function(){C.abort("timeout")},h.timeout));try{c=!1,i.send(b,r)}catch(e){if(c)throw e;r(-1,e)}}else r(-1,"No Transport");return C},getJSON:function(e,t,n){return he.get(e,t,n,"json")},getScript:function(e,t){return he.get(e,void 0,t,"script")}}),he.each(["get","post"],function(e,t){he[t]=function(e,n,r,i){return he.isFunction(n)&&(i=i||r,r=n,n=void 0),he.ajax(he.extend({url:e,type:t,dataType:i,data:n,success:r},he.isPlainObject(e)&&e))}}),he._evalUrl=function(e){return he.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},he.fn.extend({wrapAll:function(e){var t;return this[0]&&(he.isFunction(e)&&(e=e.call(this[0])),t=he(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return he.isFunction(e)?this.each(function(t){he(this).wrapInner(e.call(this,t))}):this.each(function(){var t=he(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=he.isFunction(e);return this.each(function(n){he(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){he(this).replaceWith(this.childNodes)}),this}}),he.expr.pseudos.hidden=function(e){return!he.expr.pseudos.visible(e)},he.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},he.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var $t={0:200,1223:204},Bt=he.ajaxSettings.xhr();de.cors=!!Bt&&"withCredentials"in Bt,de.ajax=Bt=!!Bt,he.ajaxTransport(function(t){var n,r;if(de.cors||Bt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o($t[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),he.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),he.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return he.globalEval(e),e}}}),he.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),he.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=he("<script>").prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),ne.head.appendChild(t[0])},abort:function(){n&&n()}}}});var _t=[],zt=/(=)\?(?=&|$)|\?\?/;he.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=_t.pop()||he.expando+"_"+Et++;return this[e]=!0,e}}),he.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=!1!==t.jsonp&&(zt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&zt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=he.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(zt,"$1"+i):!1!==t.jsonp&&(t.url+=(kt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||he.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?he(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,_t.push(i)),a&&he.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),de.createHTMLDocument=function(){var e=ne.implementation.createHTMLDocument("").body;return e.innerHTML="<form></form><form></form>",2===e.childNodes.length}(),he.parseHTML=function(e,t,n){if("string"!=typeof e)return[];"boolean"==typeof t&&(n=t,t=!1);var r,i,o;return t||(de.createHTMLDocument?(t=ne.implementation.createHTMLDocument(""),r=t.createElement("base"),r.href=ne.location.href,t.head.appendChild(r)):t=ne),i=Ce.exec(e),o=!n&&[],i?[t.createElement(i[1])]:(i=b([e],t,o),o&&o.length&&he(o).remove(),he.merge([],i.childNodes))},he.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=V(e.slice(s)),e=e.slice(0,s)),he.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&he.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?he("<div>").append(he.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},he.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){he.fn[t]=function(e){return this.on(t,e)}}),he.expr.pseudos.animated=function(e){return he.grep(he.timers,function(t){return e===t.elem}).length},he.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=he.css(e,"position"),f=he(e),p={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=he.css(e,"top"),u=he.css(e,"left"),l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),he.isFunction(t)&&(t=t.call(e,n,he.extend({},s))),null!=t.top&&(p.top=t.top-s.top+a),null!=t.left&&(p.left=t.left-s.left+i),"using"in t?t.using.call(e,p):f.css(p)}},he.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){he.offset.setOffset(this,e,t)});var t,n,r,i,o=this[0];if(o)return o.getClientRects().length?(r=o.getBoundingClientRect(),t=o.ownerDocument,n=t.documentElement,i=t.defaultView,{top:r.top+i.pageYOffset-n.clientTop,left:r.left+i.pageXOffset-n.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n=this[0],r={top:0,left:0};return"fixed"===he.css(n,"position")?t=n.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),i(e[0],"html")||(r=e.offset()),r={top:r.top+he.css(e[0],"borderTopWidth",!0),left:r.left+he.css(e[0],"borderLeftWidth",!0)}),{top:t.top-r.top-he.css(n,"marginTop",!0),left:t.left-r.left-he.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===he.css(e,"position");)e=e.offsetParent;return e||Ye})}}),he.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;he.fn[e]=function(r){return Le(this,function(e,r,i){var o;if(he.isWindow(e)?o=e:9===e.nodeType&&(o=e.defaultView),void 0===i)return o?o[t]:e[r];o?o.scrollTo(n?o.pageXOffset:i,n?i:o.pageYOffset):e[r]=i},e,r,arguments.length)}}),he.each(["top","left"],function(e,t){he.cssHooks[t]=H(de.pixelPosition,function(e,n){if(n)return n=L(e,t),ot.test(n)?he(e).position()[t]+"px":n})}),he.each({Height:"height",Width:"width"},function(e,t){he.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){he.fn[r]=function(i,o){var a=arguments.length&&(n||"boolean"!=typeof i),s=n||(!0===i||!0===o?"margin":"border");return Le(this,function(t,n,i){var o;return he.isWindow(t)?0===r.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(o=t.documentElement,Math.max(t.body["scroll"+e],o["scroll"+e],t.body["offset"+e],o["offset"+e],o["client"+e])):void 0===i?he.css(t,n,s):he.style(t,n,i,s)},t,a?i:void 0,a)}})}),he.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),he.holdReady=function(e){e?he.readyWait++:he.ready(!0)},he.isArray=Array.isArray,he.parseJSON=JSON.parse,he.nodeName=i,"function"==typeof define&&define.amd&&define("jquery",[],function(){return he});var Xt=e.jQuery,Ut=e.$;return he.noConflict=function(t){return e.$===he&&(e.$=Ut),t&&e.jQuery===he&&(e.jQuery=Xt),he},t||(e.jQuery=e.$=he),he}); })(this);
+(function (window, undefined) { !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(e,t,n){t=t||ae;var r,i=t.createElement("script");if(i.text=e,n)for(r in be)n[r]&&(i[r]=n[r]);t.head.appendChild(i).parentNode.removeChild(i)}function r(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?pe[de.call(e)]||"object":typeof e}function i(e){var t=!!e&&"length"in e&&e.length,n=r(e);return!me(e)&&!xe(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function o(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}function a(e,t,n){return me(t)?we.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?we.grep(e,function(e){return e===t!==n}):"string"!=typeof t?we.grep(e,function(e){return fe.call(t,e)>-1!==n}):we.filter(t,e,n)}function s(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function u(e){var t={};return we.each(e.match(Le)||[],function(e,n){t[n]=!0}),t}function l(e){return e}function c(e){throw e}function f(e,t,n,r){var i;try{e&&me(i=e.promise)?i.call(e).done(t).fail(n):e&&me(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}function p(){ae.removeEventListener("DOMContentLoaded",p),e.removeEventListener("load",p),we.ready()}function d(e,t){return t.toUpperCase()}function h(e){return e.replace(Me,"ms-").replace(Re,d)}function g(){this.expando=we.expando+g.uid++}function v(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Be.test(e)?JSON.parse(e):e)}function y(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(Fe,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n=v(n)}catch(e){}$e.set(e,t,n)}else n=void 0;return n}function m(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return we.css(e,t,"")},u=s(),l=n&&n[3]||(we.cssNumber[t]?"":"px"),c=(we.cssNumber[t]||"px"!==l&&+u)&&ze.exec(we.css(e,t));if(c&&c[3]!==l){for(u/=2,l=l||c[3],c=+u||1;a--;)we.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,we.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function x(e){var t,n=e.ownerDocument,r=e.nodeName,i=Ge[r];return i||(t=n.body.appendChild(n.createElement(r)),i=we.css(t,"display"),t.parentNode.removeChild(t),"none"===i&&(i="block"),Ge[r]=i,i)}function b(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)r=e[o],r.style&&(n=r.style.display,t?("none"===n&&(i[o]=We.get(r,"display")||null,i[o]||(r.style.display="")),""===r.style.display&&Ue(r)&&(i[o]=x(r))):"none"!==n&&(i[o]="none",We.set(r,"display",n)));for(o=0;o<a;o++)null!=i[o]&&(e[o].style.display=i[o]);return e}function w(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&o(e,t)?we.merge([e],n):n}function T(e,t){for(var n=0,r=e.length;n<r;n++)We.set(e[n],"globalEval",!t||We.get(t[n],"globalEval"))}function C(e,t,n,i,o){for(var a,s,u,l,c,f,p=t.createDocumentFragment(),d=[],h=0,g=e.length;h<g;h++)if((a=e[h])||0===a)if("object"===r(a))we.merge(d,a.nodeType?[a]:a);else if(Ze.test(a)){for(s=s||p.appendChild(t.createElement("div")),u=(Qe.exec(a)||["",""])[1].toLowerCase(),l=Ke[u]||Ke._default,s.innerHTML=l[1]+we.htmlPrefilter(a)+l[2],f=l[0];f--;)s=s.lastChild;we.merge(d,s.childNodes),s=p.firstChild,s.textContent=""}else d.push(t.createTextNode(a));for(p.textContent="",h=0;a=d[h++];)if(i&&we.inArray(a,i)>-1)o&&o.push(a);else if(c=we.contains(a.ownerDocument,a),s=w(p.appendChild(a),"script"),c&&T(s),n)for(f=0;a=s[f++];)Je.test(a.type||"")&&n.push(a);return p}function E(){return!0}function k(){return!1}function S(){try{return ae.activeElement}catch(e){}}function D(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)D(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=k;else if(!i)return e;return 1===o&&(a=i,i=function(e){return we().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=we.guid++)),e.each(function(){we.event.add(this,t,i,r,n)})}function N(e,t){return o(e,"table")&&o(11!==t.nodeType?t:t.firstChild,"tr")?we(e).children("tbody")[0]||e:e}function A(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function j(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function q(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(We.hasData(e)&&(o=We.access(e),a=We.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n<r;n++)we.event.add(t,i,l[i][n])}$e.hasData(e)&&(s=$e.access(e),u=we.extend({},s),$e.set(t,u))}}function L(e,t){var n=t.nodeName.toLowerCase();"input"===n&&Ye.test(e.type)?t.checked=e.checked:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}function H(e,t,r,i){t=le.apply([],t);var o,a,s,u,l,c,f=0,p=e.length,d=p-1,h=t[0],g=me(h);if(g||p>1&&"string"==typeof h&&!ye.checkClone&&at.test(h))return e.each(function(n){var o=e.eq(n);g&&(t[0]=h.call(this,n,o.html())),H(o,t,r,i)});if(p&&(o=C(t,e[0].ownerDocument,!1,e,i),a=o.firstChild,1===o.childNodes.length&&(o=a),a||i)){for(s=we.map(w(o,"script"),A),u=s.length;f<p;f++)l=o,f!==d&&(l=we.clone(l,!0,!0),u&&we.merge(s,w(l,"script"))),r.call(e[f],l,f);if(u)for(c=s[s.length-1].ownerDocument,we.map(s,j),f=0;f<u;f++)l=s[f],Je.test(l.type||"")&&!We.access(l,"globalEval")&&we.contains(c,l)&&(l.src&&"module"!==(l.type||"").toLowerCase()?we._evalUrl&&we._evalUrl(l.src):n(l.textContent.replace(st,""),c,l))}return e}function O(e,t,n){for(var r,i=t?we.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||we.cleanData(w(r)),r.parentNode&&(n&&we.contains(r.ownerDocument,r)&&T(w(r,"script")),r.parentNode.removeChild(r));return e}function P(e,t,n){var r,i,o,a,s=e.style;return n=n||lt(e),n&&(a=n.getPropertyValue(t)||n[t],""!==a||we.contains(e.ownerDocument,e)||(a=we.style(e,t)),!ye.pixelBoxStyles()&&ut.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function M(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function R(e){if(e in vt)return e;for(var t=e[0].toUpperCase()+e.slice(1),n=gt.length;n--;)if((e=gt[n]+t)in vt)return e}function I(e){var t=we.cssProps[e];return t||(t=we.cssProps[e]=R(e)||e),t}function W(e,t,n){var r=ze.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function $(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=we.css(e,n+Xe[a],!0,i)),r?("content"===n&&(u-=we.css(e,"padding"+Xe[a],!0,i)),"margin"!==n&&(u-=we.css(e,"border"+Xe[a]+"Width",!0,i))):(u+=we.css(e,"padding"+Xe[a],!0,i),"padding"!==n?u+=we.css(e,"border"+Xe[a]+"Width",!0,i):s+=we.css(e,"border"+Xe[a]+"Width",!0,i));return!r&&o>=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function B(e,t,n){var r=lt(e),i=P(e,t,r),o="border-box"===we.css(e,"boxSizing",!1,r),a=o;if(ut.test(i)){if(!n)return i;i="auto"}return a=a&&(ye.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===we.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+$(e,t,n||(o?"border":"content"),a,r,i)+"px"}function F(e,t,n,r,i){return new F.prototype.init(e,t,n,r,i)}function _(){mt&&(!1===ae.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(_):e.setTimeout(_,we.fx.interval),we.fx.tick())}function z(){return e.setTimeout(function(){yt=void 0}),yt=Date.now()}function X(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)n=Xe[r],i["margin"+n]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function U(e,t,n){for(var r,i=(Y.tweeners[t]||[]).concat(Y.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function V(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&Ue(e),v=We.get(e,"fxshow");n.queue||(a=we._queueHooks(e,"fx"),null==a.unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,we.queue(e,"fx").length||a.empty.fire()})}));for(r in t)if(i=t[r],xt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||we.style(e,r)}if((u=!we.isEmptyObject(t))||!we.isEmptyObject(d)){f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],l=v&&v.display,null==l&&(l=We.get(e,"display")),c=we.css(e,"display"),"none"===c&&(l?c=l:(b([e],!0),l=e.style.display||l,c=we.css(e,"display"),b([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===we.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1;for(r in d)u||(v?"hidden"in v&&(g=v.hidden):v=We.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&b([e],!0),p.done(function(){g||b([e]),We.remove(e,"fxshow");for(r in d)we.style(e,r,d[r])})),u=U(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}}function G(e,t){var n,r,i,o,a;for(n in e)if(r=h(n),i=t[r],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=we.cssHooks[r])&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function Y(e,t,n){var r,i,o=0,a=Y.prefilters.length,s=we.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=yt||z(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;a<u;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),o<1&&u?n:(u||s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:we.extend({},t),opts:we.extend(!0,{specialEasing:{},easing:we.easing._default},n),originalProperties:t,originalOptions:n,startTime:yt||z(),duration:n.duration,tweens:[],createTween:function(t,n){var r=we.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;n<r;n++)l.tweens[n].run(1);return t?(s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l,t])):s.rejectWith(e,[l,t]),this}}),c=l.props;for(G(c,l.opts.specialEasing);o<a;o++)if(r=Y.prefilters[o].call(l,e,c,l.opts))return me(r.stop)&&(we._queueHooks(l.elem,l.opts.queue).stop=r.stop.bind(r)),r;return we.map(c,U,l),me(l.opts.start)&&l.opts.start.call(e,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),we.fx.timer(we.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l}function Q(e){return(e.match(Le)||[]).join(" ")}function J(e){return e.getAttribute&&e.getAttribute("class")||""}function K(e){return Array.isArray(e)?e:"string"==typeof e?e.match(Le)||[]:[]}function Z(e,t,n,i){var o;if(Array.isArray(t))we.each(t,function(t,r){n||qt.test(e)?i(e,r):Z(e+"["+("object"==typeof r&&null!=r?t:"")+"]",r,n,i)});else if(n||"object"!==r(t))i(e,t);else for(o in t)Z(e+"["+o+"]",t[o],n,i)}function ee(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(Le)||[];if(me(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function te(e,t,n,r){function i(s){var u;return o[s]=!0,we.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||a||o[l]?a?!(u=l):void 0:(t.dataTypes.unshift(l),i(l),!1)}),u}var o={},a=e===_t;return i(t.dataTypes[0])||!o["*"]&&i("*")}function ne(e,t){var n,r,i=we.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&we.extend(!0,e,r),e}function re(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function ie(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if(s=i.split(" "),s[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}var oe=[],ae=e.document,se=Object.getPrototypeOf,ue=oe.slice,le=oe.concat,ce=oe.push,fe=oe.indexOf,pe={},de=pe.toString,he=pe.hasOwnProperty,ge=he.toString,ve=ge.call(Object),ye={},me=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},xe=function(e){return null!=e&&e===e.window},be={type:!0,src:!0,noModule:!0},we=function(e,t){return new we.fn.init(e,t)},Te=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;we.fn=we.prototype={jquery:"3.3.1",constructor:we,length:0,toArray:function(){return ue.call(this)},get:function(e){return null==e?ue.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=we.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return we.each(this,e)},map:function(e){return this.pushStack(we.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(ue.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:ce,sort:oe.sort,splice:oe.splice},we.extend=we.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||me(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],r=e[t],a!==r&&(l&&r&&(we.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&we.isPlainObject(n)?n:{},a[t]=we.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},we.extend({expando:"jQuery"+("3.3.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==de.call(e))&&(!(t=se(e))||"function"==typeof(n=he.call(t,"constructor")&&t.constructor)&&ge.call(n)===ve)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e){n(e)},each:function(e,t){var n,r=0;if(i(e))for(n=e.length;r<n&&!1!==t.call(e[r],r,e[r]);r++);else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(Te,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(i(Object(e))?we.merge(n,"string"==typeof e?[e]:e):ce.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:fe.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,o,a=0,s=[];if(i(e))for(r=e.length;a<r;a++)null!=(o=t(e[a],a,n))&&s.push(o);else for(a in e)null!=(o=t(e[a],a,n))&&s.push(o);return le.apply([],s)},guid:1,support:ye}),"function"==typeof Symbol&&(we.fn[Symbol.iterator]=oe[Symbol.iterator]),we.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){pe["[object "+t+"]"]=t.toLowerCase()});var Ce=function(e){function t(e,t,n,r){var i,o,a,s,u,c,p,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!r&&((t?t.ownerDocument||t:W)!==q&&j(t),t=t||q,H)){if(11!==h&&(u=ge.exec(e)))if(i=u[1]){if(9===h){if(!(a=t.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(d&&(a=d.getElementById(i))&&R(t,a)&&a.id===i)return n.push(a),n}else{if(u[2])return Q.apply(n,t.getElementsByTagName(e)),n;if((i=u[3])&&b.getElementsByClassName&&t.getElementsByClassName)return Q.apply(n,t.getElementsByClassName(i)),n}if(b.qsa&&!z[e+" "]&&(!O||!O.test(e))){if(1!==h)d=t,p=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(xe,be):t.setAttribute("id",s=I),c=E(e),o=c.length;o--;)c[o]="#"+s+" "+f(c[o]);p=c.join(","),d=ve.test(e)&&l(t.parentNode)||t}if(p)try{return Q.apply(n,d.querySelectorAll(p)),n}catch(e){}finally{s===I&&t.removeAttribute("id")}}}return S(e.replace(oe,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>w.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[I]=!0,e}function i(e){var t=q.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)w.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&Te(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function l(e){return e&&void 0!==e.getElementsByTagName&&e}function c(){}function f(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=B++;return t.first?function(t,n,i){for(;t=t[r];)if(1===t.nodeType||a)return e(t,n,i);return!1}:function(t,n,u){var l,c,f,p=[$,s];if(u){for(;t=t[r];)if((1===t.nodeType||a)&&e(t,n,u))return!0}else for(;t=t[r];)if(1===t.nodeType||a)if(f=t[I]||(t[I]={}),c=f[t.uniqueID]||(f[t.uniqueID]={}),i&&i===t.nodeName.toLowerCase())t=t[r]||t;else{if((l=c[o])&&l[0]===$&&l[1]===s)return p[2]=l[2];if(c[o]=p,p[2]=e(t,n,u))return!0}return!1}}function d(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function h(e,n,r){for(var i=0,o=n.length;i<o;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function v(e,t,n,i,o,a){return i&&!i[I]&&(i=v(i)),o&&!o[I]&&(o=v(o,a)),r(function(r,a,s,u){var l,c,f,p=[],d=[],v=a.length,y=r||h(t||"*",s.nodeType?[s]:s,[]),m=!e||!r&&t?y:g(y,p,e,s,u),x=n?o||(r?e:v||i)?[]:a:m;if(n&&n(m,x,s,u),i)for(l=g(x,d),i(l,[],s,u),c=l.length;c--;)(f=l[c])&&(x[d[c]]=!(m[d[c]]=f));if(r){if(o||e){if(o){for(l=[],c=x.length;c--;)(f=x[c])&&l.push(m[c]=f);o(null,x=[],l,u)}for(c=x.length;c--;)(f=x[c])&&(l=o?K(r,f):p[c])>-1&&(r[l]=!(a[l]=f))}}else x=g(x===a?x.splice(v,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function y(e){for(var t,n,r,i=e.length,o=w.relative[e[0].type],a=o||w.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return K(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==D)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s<i;s++)if(n=w.relative[e[s].type])c=[p(d(c),n)];else{if(n=w.filter[e[s].type].apply(null,e[s].matches),n[I]){for(r=++s;r<i&&!w.relative[e[r].type];r++);return v(s>1&&d(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(oe,"$1"),n,s<r&&y(e.slice(s,r)),r<i&&y(e=e.slice(r)),r<i&&f(e))}c.push(n)}return d(c)}function m(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,u,l){var c,f,p,d=0,h="0",v=r&&[],y=[],m=D,x=r||o&&w.find.TAG("*",l),b=$+=null==m?1:Math.random()||.1,T=x.length;for(l&&(D=a===q||a||l);h!==T&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===q||(j(c),s=!H);p=e[f++];)if(p(c,a||q,s)){u.push(c);break}l&&($=b)}i&&((c=!p&&c)&&d--,r&&v.push(c))}if(d+=h,i&&h!==d){for(f=0;p=n[f++];)p(v,y,a,s);if(r){if(d>0)for(;h--;)v[h]||y[h]||(y[h]=G.call(u));y=g(y)}Q.apply(u,y),l&&!r&&y.length>0&&d+n.length>1&&t.uniqueSort(u)}return l&&($=b,D=m),v};return i?r(a):a}var x,b,w,T,C,E,k,S,D,N,A,j,q,L,H,O,P,M,R,I="sizzle"+1*new Date,W=e.document,$=0,B=0,F=n(),_=n(),z=n(),X=function(e,t){return e===t&&(A=!0),0},U={}.hasOwnProperty,V=[],G=V.pop,Y=V.push,Q=V.push,J=V.slice,K=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},Z="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ee="[\\x20\\t\\r\\n\\f]",te="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",ne="\\["+ee+"*("+te+")(?:"+ee+"*([*^$|!~]?=)"+ee+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+te+"))|)"+ee+"*\\]",re=":("+te+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ne+")*)|.*)\\)|)",ie=new RegExp(ee+"+","g"),oe=new RegExp("^"+ee+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ee+"+$","g"),ae=new RegExp("^"+ee+"*,"+ee+"*"),se=new RegExp("^"+ee+"*([>+~]|"+ee+")"+ee+"*"),ue=new RegExp("="+ee+"*([^\\]'\"]*?)"+ee+"*\\]","g"),le=new RegExp(re),ce=new RegExp("^"+te+"$"),fe={ID:new RegExp("^#("+te+")"),CLASS:new RegExp("^\\.("+te+")"),TAG:new RegExp("^("+te+"|[*])"),ATTR:new RegExp("^"+ne),PSEUDO:new RegExp("^"+re),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ee+"*(even|odd|(([+-]|)(\\d*)n|)"+ee+"*(?:([+-]|)"+ee+"*(\\d+)|))"+ee+"*\\)|)","i"),bool:new RegExp("^(?:"+Z+")$","i"),needsContext:new RegExp("^"+ee+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ee+"*((?:-\\d)?\\d*)"+ee+"*\\)|)(?=[^-]|$)","i")},pe=/^(?:input|select|textarea|button)$/i,de=/^h\d$/i,he=/^[^{]+\{\s*\[native \w/,ge=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,ye=new RegExp("\\\\([\\da-f]{1,6}"+ee+"?|("+ee+")|.)","ig"),me=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},xe=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,be=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},we=function(){j()},Te=p(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{Q.apply(V=J.call(W.childNodes),W.childNodes),V[W.childNodes.length].nodeType}catch(e){Q={apply:V.length?function(e,t){Y.apply(e,J.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}b=t.support={},C=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},j=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:W;return r!==q&&9===r.nodeType&&r.documentElement?(q=r,L=q.documentElement,H=!C(q),W!==q&&(n=q.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",we,!1):n.attachEvent&&n.attachEvent("onunload",we)),b.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),b.getElementsByTagName=i(function(e){return e.appendChild(q.createComment("")),!e.getElementsByTagName("*").length}),b.getElementsByClassName=he.test(q.getElementsByClassName),b.getById=i(function(e){return L.appendChild(e).id=I,!q.getElementsByName||!q.getElementsByName(I).length}),b.getById?(w.filter.ID=function(e){var t=e.replace(ye,me);return function(e){return e.getAttribute("id")===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n=t.getElementById(e);return n?[n]:[]}}):(w.filter.ID=function(e){var t=e.replace(ye,me);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),w.find.TAG=b.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):b.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},w.find.CLASS=b.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&H)return t.getElementsByClassName(e)},P=[],O=[],(b.qsa=he.test(q.querySelectorAll))&&(i(function(e){L.appendChild(e).innerHTML="<a id='"+I+"'></a><select id='"+I+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&O.push("[*^$]="+ee+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||O.push("\\["+ee+"*(?:value|"+Z+")"),e.querySelectorAll("[id~="+I+"-]").length||O.push("~="),e.querySelectorAll(":checked").length||O.push(":checked"),e.querySelectorAll("a#"+I+"+*").length||O.push(".#.+[+~]")}),i(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=q.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&O.push("name"+ee+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&O.push(":enabled",":disabled"),L.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&O.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),O.push(",.*:")})),(b.matchesSelector=he.test(M=L.matches||L.webkitMatchesSelector||L.mozMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&i(function(e){b.disconnectedMatch=M.call(e,"*"),M.call(e,"[s!='']:x"),P.push("!=",re)}),O=O.length&&new RegExp(O.join("|")),P=P.length&&new RegExp(P.join("|")),t=he.test(L.compareDocumentPosition),R=t||he.test(L.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},X=t?function(e,t){if(e===t)return A=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!b.sortDetached&&t.compareDocumentPosition(e)===n?e===q||e.ownerDocument===W&&R(W,e)?-1:t===q||t.ownerDocument===W&&R(W,t)?1:N?K(N,e)-K(N,t):0:4&n?-1:1)}:function(e,t){if(e===t)return A=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===q?-1:t===q?1:i?-1:o?1:N?K(N,e)-K(N,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===W?-1:u[r]===W?1:0},q):q},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==q&&j(e),n=n.replace(ue,"='$1']"),b.matchesSelector&&H&&!z[n+" "]&&(!P||!P.test(n))&&(!O||!O.test(n)))try{var r=M.call(e,n);if(r||b.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return t(n,q,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==q&&j(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==q&&j(e);var n=w.attrHandle[t.toLowerCase()],r=n&&U.call(w.attrHandle,t.toLowerCase())?n(e,t,!H):void 0;return void 0!==r?r:b.attributes||!H?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.escape=function(e){return(e+"").replace(xe,be)},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(A=!b.detectDuplicates,N=!b.sortStable&&e.slice(0),e.sort(X),A){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return N=null,e},T=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=T(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=T(t);return n},w=t.selectors={cacheLength:50,createPseudo:r,match:fe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ye,me),e[3]=(e[3]||e[4]||e[5]||"").replace(ye,me),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return fe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&le.test(n)&&(t=E(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(ye,me).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=F[e+" "];return t||(t=new RegExp("(^|"+ee+")"+e+"("+ee+"|$)"))&&F(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ie," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,y=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(v){if(o){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){for(p=v,f=p[I]||(p[I]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===$&&l[1],x=d&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[$,d,x];break}}else if(m&&(p=t,f=p[I]||(p[I]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===$&&l[1],x=d),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==y:1!==p.nodeType)||!++x||(m&&(f=p[I]||(p[I]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),c[e]=[$,x]),p!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=w.pseudos[e]||w.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[I]?o(n):o.length>1?(i=[e,e,"",n],w.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=K(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(oe,"$1"));return i[I]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(ye,me),function(t){return(t.textContent||t.innerText||T(t)).indexOf(e)>-1}}),lang:r(function(e){return ce.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(ye,me).toLowerCase(),function(t){var n;do{if(n=H?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===L},focus:function(e){return e===q.activeElement&&(!q.hasFocus||q.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:s(!1),disabled:s(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},
+empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!w.pseudos.empty(e)},header:function(e){return de.test(e.nodeName)},input:function(e){return pe.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[n<0?n+t:n]}),even:u(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},w.pseudos.nth=w.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})w.pseudos[x]=function(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}(x);for(x in{submit:!0,reset:!0})w.pseudos[x]=function(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}(x);return c.prototype=w.filters=w.pseudos,w.setFilters=new c,E=t.tokenize=function(e,n){var r,i,o,a,s,u,l,c=_[e+" "];if(c)return n?0:c.slice(0);for(s=e,u=[],l=w.preFilter;s;){r&&!(i=ae.exec(s))||(i&&(s=s.slice(i[0].length)||s),u.push(o=[])),r=!1,(i=se.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(oe," ")}),s=s.slice(r.length));for(a in w.filter)!(i=fe[a].exec(s))||l[a]&&!(i=l[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):_(e,u).slice(0)},k=t.compile=function(e,t){var n,r=[],i=[],o=z[e+" "];if(!o){for(t||(t=E(e)),n=t.length;n--;)o=y(t[n]),o[I]?r.push(o):i.push(o);o=z(e,m(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,u,c="function"==typeof e&&e,p=!r&&E(e=c.selector||e);if(n=n||[],1===p.length){if(o=p[0]=p[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&H&&w.relative[o[1].type]){if(!(t=(w.find.ID(a.matches[0].replace(ye,me),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=fe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!w.relative[s=a.type]);)if((u=w.find[s])&&(r=u(a.matches[0].replace(ye,me),ve.test(o[0].type)&&l(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&f(o)))return Q.apply(n,r),n;break}}return(c||k(e,p))(r,t,!H,n,!t||ve.test(e)&&l(t.parentNode)||t),n},b.sortStable=I.split("").sort(X).join("")===I,b.detectDuplicates=!!A,j(),b.sortDetached=i(function(e){return 1&e.compareDocumentPosition(q.createElement("fieldset"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),b.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(Z,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);we.find=Ce,we.expr=Ce.selectors,we.expr[":"]=we.expr.pseudos,we.uniqueSort=we.unique=Ce.uniqueSort,we.text=Ce.getText,we.isXMLDoc=Ce.isXML,we.contains=Ce.contains,we.escapeSelector=Ce.escape;var Ee=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&we(e).is(n))break;r.push(e)}return r},ke=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},Se=we.expr.match.needsContext,De=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;we.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?we.find.matchesSelector(r,e)?[r]:[]:we.find.matches(e,we.grep(t,function(e){return 1===e.nodeType}))},we.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(we(e).filter(function(){for(t=0;t<r;t++)if(we.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)we.find(e,i[t],n);return r>1?we.uniqueSort(n):n},filter:function(e){return this.pushStack(a(this,e||[],!1))},not:function(e){return this.pushStack(a(this,e||[],!0))},is:function(e){return!!a(this,"string"==typeof e&&Se.test(e)?we(e):e||[],!1).length}});var Ne,Ae=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(we.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ne,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:Ae.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof we?t[0]:t,we.merge(this,we.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:ae,!0)),De.test(r[1])&&we.isPlainObject(t))for(r in t)me(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=ae.getElementById(r[2]),i&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):me(e)?void 0!==n.ready?n.ready(e):e(we):we.makeArray(e,this)}).prototype=we.fn,Ne=we(ae);var je=/^(?:parents|prev(?:Until|All))/,qe={children:!0,contents:!0,next:!0,prev:!0};we.fn.extend({has:function(e){var t=we(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(we.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&we(e);if(!Se.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&we.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?we.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?fe.call(we(e),this[0]):fe.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(we.uniqueSort(we.merge(this.get(),we(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),we.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return Ee(e,"parentNode")},parentsUntil:function(e,t,n){return Ee(e,"parentNode",n)},next:function(e){return s(e,"nextSibling")},prev:function(e){return s(e,"previousSibling")},nextAll:function(e){return Ee(e,"nextSibling")},prevAll:function(e){return Ee(e,"previousSibling")},nextUntil:function(e,t,n){return Ee(e,"nextSibling",n)},prevUntil:function(e,t,n){return Ee(e,"previousSibling",n)},siblings:function(e){return ke((e.parentNode||{}).firstChild,e)},children:function(e){return ke(e.firstChild)},contents:function(e){return o(e,"iframe")?e.contentDocument:(o(e,"template")&&(e=e.content||e),we.merge([],e.childNodes))}},function(e,t){we.fn[e]=function(n,r){var i=we.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=we.filter(r,i)),this.length>1&&(qe[e]||we.uniqueSort(i),je.test(e)&&i.reverse()),this.pushStack(i)}});var Le=/[^\x20\t\r\n\f]+/g;we.Callbacks=function(e){e="string"==typeof e?u(e):we.extend({},e);var t,n,i,o,a=[],s=[],l=-1,c=function(){for(o=o||e.once,i=t=!0;s.length;l=-1)for(n=s.shift();++l<a.length;)!1===a[l].apply(n[0],n[1])&&e.stopOnFalse&&(l=a.length,n=!1);e.memory||(n=!1),t=!1,o&&(a=n?[]:"")},f={add:function(){return a&&(n&&!t&&(l=a.length-1,s.push(n)),function t(n){we.each(n,function(n,i){me(i)?e.unique&&f.has(i)||a.push(i):i&&i.length&&"string"!==r(i)&&t(i)})}(arguments),n&&!t&&c()),this},remove:function(){return we.each(arguments,function(e,t){for(var n;(n=we.inArray(t,a,n))>-1;)a.splice(n,1),n<=l&&l--}),this},has:function(e){return e?we.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return o=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return o=s=[],n||t||(a=n=""),this},locked:function(){return!!o},fireWith:function(e,n){return o||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!i}};return f},we.extend({Deferred:function(t){var n=[["notify","progress",we.Callbacks("memory"),we.Callbacks("memory"),2],["resolve","done",we.Callbacks("once memory"),we.Callbacks("once memory"),0,"resolved"],["reject","fail",we.Callbacks("once memory"),we.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return we.Deferred(function(t){we.each(n,function(n,r){var i=me(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&me(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){function o(t,n,r,i){return function(){var s=this,u=arguments,f=function(){var e,f;if(!(t<a)){if((e=r.apply(s,u))===n.promise())throw new TypeError("Thenable self-resolution");f=e&&("object"==typeof e||"function"==typeof e)&&e.then,me(f)?i?f.call(e,o(a,n,l,i),o(a,n,c,i)):(a++,f.call(e,o(a,n,l,i),o(a,n,c,i),o(a,n,l,n.notifyWith))):(r!==l&&(s=void 0,u=[e]),(i||n.resolveWith)(s,u))}},p=i?f:function(){try{f()}catch(e){we.Deferred.exceptionHook&&we.Deferred.exceptionHook(e,p.stackTrace),t+1>=a&&(r!==c&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?p():(we.Deferred.getStackHook&&(p.stackTrace=we.Deferred.getStackHook()),e.setTimeout(p))}}var a=0;return we.Deferred(function(e){n[0][3].add(o(0,e,me(i)?i:l,e.notifyWith)),n[1][3].add(o(0,e,me(t)?t:l)),n[2][3].add(o(0,e,me(r)?r:c))}).promise()},promise:function(e){return null!=e?we.extend(e,i):i}},o={};return we.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=ue.call(arguments),o=we.Deferred(),a=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?ue.call(arguments):n,--t||o.resolveWith(r,i)}};if(t<=1&&(f(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||me(i[n]&&i[n].then)))return o.then();for(;n--;)f(i[n],a(n),o.reject);return o.promise()}});var He=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;we.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&He.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},we.readyException=function(t){e.setTimeout(function(){throw t})};var Oe=we.Deferred();we.fn.ready=function(e){return Oe.then(e).catch(function(e){we.readyException(e)}),this},we.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--we.readyWait:we.isReady)||(we.isReady=!0,!0!==e&&--we.readyWait>0||Oe.resolveWith(ae,[we]))}}),we.ready.then=Oe.then,"complete"===ae.readyState||"loading"!==ae.readyState&&!ae.documentElement.doScroll?e.setTimeout(we.ready):(ae.addEventListener("DOMContentLoaded",p),e.addEventListener("load",p));var Pe=function(e,t,n,i,o,a,s){var u=0,l=e.length,c=null==n;if("object"===r(n)){o=!0;for(u in n)Pe(e,t,u,n[u],!0,a,s)}else if(void 0!==i&&(o=!0,me(i)||(s=!0),c&&(s?(t.call(e,i),t=null):(c=t,t=function(e,t,n){return c.call(we(e),n)})),t))for(;u<l;u++)t(e[u],n,s?i:i.call(e[u],u,t(e[u],n)));return o?e:c?t.call(e):l?t(e[0],n):a},Me=/^-ms-/,Re=/-([a-z])/g,Ie=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};g.uid=1,g.prototype={cache:function(e){var t=e[this.expando];return t||(t={},Ie(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[h(t)]=n;else for(r in t)i[h(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][h(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){Array.isArray(t)?t=t.map(h):(t=h(t),t=t in r?[t]:t.match(Le)||[]),n=t.length;for(;n--;)delete r[t[n]]}(void 0===t||we.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!we.isEmptyObject(t)}};var We=new g,$e=new g,Be=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Fe=/[A-Z]/g;we.extend({hasData:function(e){return $e.hasData(e)||We.hasData(e)},data:function(e,t,n){return $e.access(e,t,n)},removeData:function(e,t){$e.remove(e,t)},_data:function(e,t,n){return We.access(e,t,n)},_removeData:function(e,t){We.remove(e,t)}}),we.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=$e.get(o),1===o.nodeType&&!We.get(o,"hasDataAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=h(r.slice(5)),y(o,r,i[r])));We.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){$e.set(this,e)}):Pe(this,function(t){var n;if(o&&void 0===t){if(void 0!==(n=$e.get(o,e)))return n;if(void 0!==(n=y(o,e)))return n}else this.each(function(){$e.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){$e.remove(this,e)})}}),we.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=We.get(e,t),n&&(!r||Array.isArray(n)?r=We.access(e,t,we.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=we.queue(e,t),r=n.length,i=n.shift(),o=we._queueHooks(e,t),a=function(){we.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return We.get(e,n)||We.access(e,n,{empty:we.Callbacks("once memory").add(function(){We.remove(e,[t+"queue",n])})})}}),we.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?we.queue(this[0],e):void 0===t?this:this.each(function(){var n=we.queue(this,e,t);we._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&we.dequeue(this,e)})},dequeue:function(e){return this.each(function(){we.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=we.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)(n=We.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var _e=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ze=new RegExp("^(?:([+-])=|)("+_e+")([a-z%]*)$","i"),Xe=["Top","Right","Bottom","Left"],Ue=function(e,t){return e=t||e,"none"===e.style.display||""===e.style.display&&we.contains(e.ownerDocument,e)&&"none"===we.css(e,"display")},Ve=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i},Ge={};we.fn.extend({show:function(){return b(this,!0)},hide:function(){return b(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Ue(this)?we(this).show():we(this).hide()})}});var Ye=/^(?:checkbox|radio)$/i,Qe=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,Je=/^$|^module$|\/(?:java|ecma)script/i,Ke={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};Ke.optgroup=Ke.option,Ke.tbody=Ke.tfoot=Ke.colgroup=Ke.caption=Ke.thead,Ke.th=Ke.td;var Ze=/<|&#?\w+;/;!function(){var e=ae.createDocumentFragment(),t=e.appendChild(ae.createElement("div")),n=ae.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),ye.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="<textarea>x</textarea>",ye.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var et=ae.documentElement,tt=/^key/,nt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,rt=/^([^.]*)(?:\.(.+)|)/;we.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=We.get(e);if(v)for(n.handler&&(o=n,n=o.handler,i=o.selector),i&&we.find.matchesSelector(et,i),n.guid||(n.guid=we.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==we&&we.event.triggered!==t.type?we.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(Le)||[""],l=t.length;l--;)s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d&&(f=we.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=we.event.special[d]||{},c=we.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&we.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||(p=u[d]=[],p.delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),we.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=We.hasData(e)&&We.get(e);if(v&&(u=v.events)){for(t=(t||"").match(Le)||[""],l=t.length;l--;)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){for(f=we.event.special[d]||{},d=(r?f.delegateType:f.bindType)||d,p=u[d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||we.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)we.event.remove(e,d+t[l],n,r,!0);we.isEmptyObject(u)&&We.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=we.event.fix(e),u=new Array(arguments.length),l=(We.get(this,"events")||{})[s.type]||[],c=we.event.special[s.type]||{};for(u[0]=s,t=1;t<arguments.length;t++)u[t]=arguments[t];if(s.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,s)){for(a=we.event.handlers.call(this,s,l),t=0;(i=a[t++])&&!s.isPropagationStopped();)for(s.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!s.isImmediatePropagationStopped();)s.rnamespace&&!s.rnamespace.test(o.namespace)||(s.handleObj=o,s.data=o.data,void 0!==(r=((we.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,u))&&!1===(s.result=r)&&(s.preventDefault(),s.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,s),s.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&e.button>=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)r=t[n],i=r.selector+" ",void 0===a[i]&&(a[i]=r.needsContext?we(i,this).index(l)>-1:we.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(e,t){Object.defineProperty(we.Event.prototype,e,{enumerable:!0,configurable:!0,get:me(t)?function(){if(this.originalEvent)return t(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[e]},set:function(t){Object.defineProperty(this,e,{enumerable:!0,configurable:!0,writable:!0,value:t})}})},fix:function(e){return e[we.expando]?e:new we.Event(e)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==S()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===S()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&o(this,"input"))return this.click(),!1},_default:function(e){return o(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},we.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},we.Event=function(e,t){if(!(this instanceof we.Event))return new we.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?E:k,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&we.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[we.expando]=!0},we.Event.prototype={constructor:we.Event,isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=E,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=E,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=E,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},we.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,char:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&tt.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&nt.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},we.event.addProp),we.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){we.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return i&&(i===r||we.contains(r,i))||(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),we.fn.extend({on:function(e,t,n,r){return D(this,e,t,n,r)},one:function(e,t,n,r){return D(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,we(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=k),this.each(function(){we.event.remove(this,e,n,t)})}});var it=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,ot=/<script|<style|<link/i,at=/checked\s*(?:[^=]|=\s*.checked.)/i,st=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;we.extend({htmlPrefilter:function(e){return e.replace(it,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=we.contains(e.ownerDocument,e);if(!(ye.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||we.isXMLDoc(e)))for(a=w(s),o=w(e),r=0,i=o.length;r<i;r++)L(o[r],a[r]);if(t)if(n)for(o=o||w(e),a=a||w(s),r=0,i=o.length;r<i;r++)q(o[r],a[r]);else q(e,s);return a=w(s,"script"),a.length>0&&T(a,!u&&w(e,"script")),s},cleanData:function(e){for(var t,n,r,i=we.event.special,o=0;void 0!==(n=e[o]);o++)if(Ie(n)){if(t=n[We.expando]){if(t.events)for(r in t.events)i[r]?we.event.remove(n,r):we.removeEvent(n,r,t.handle);n[We.expando]=void 0}n[$e.expando]&&(n[$e.expando]=void 0)}}}),we.fn.extend({detach:function(e){return O(this,e,!0)},remove:function(e){return O(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?we.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return H(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){N(this,e).appendChild(e)}})},prepend:function(){return H(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=N(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return H(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return H(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(we.cleanData(w(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return we.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ot.test(e)&&!Ke[(Qe.exec(e)||["",""])[1].toLowerCase()]){e=we.htmlPrefilter(e);try{for(;n<r;n++)t=this[n]||{},1===t.nodeType&&(we.cleanData(w(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return H(this,arguments,function(t){var n=this.parentNode;we.inArray(this,e)<0&&(we.cleanData(w(this)),n&&n.replaceChild(t,this))},e)}}),we.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){we.fn[e]=function(e){for(var n,r=[],i=we(e),o=i.length-1,a=0;a<=o;a++)n=a===o?this:this.clone(!0),we(i[a])[t](n),ce.apply(r,n.get());return this.pushStack(r)}});var ut=new RegExp("^("+_e+")(?!px)[a-z%]+$","i"),lt=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},ct=new RegExp(Xe.join("|"),"i");!function(){function t(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",et.appendChild(u).appendChild(l);var t=e.getComputedStyle(l);r="1%"!==t.top,s=12===n(t.marginLeft),l.style.right="60%",a=36===n(t.right),i=36===n(t.width),l.style.position="absolute",o=36===l.offsetWidth||"absolute",et.removeChild(u),l=null}}function n(e){return Math.round(parseFloat(e))}var r,i,o,a,s,u=ae.createElement("div"),l=ae.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",ye.clearCloneStyle="content-box"===l.style.backgroundClip,we.extend(ye,{boxSizingReliable:function(){return t(),i},pixelBoxStyles:function(){return t(),a},pixelPosition:function(){return t(),r},reliableMarginLeft:function(){return t(),s},scrollboxSize:function(){return t(),o}}))}();var ft=/^(none|table(?!-c[ea]).+)/,pt=/^--/,dt={position:"absolute",visibility:"hidden",display:"block"},ht={letterSpacing:"0",fontWeight:"400"},gt=["Webkit","Moz","ms"],vt=ae.createElement("div").style;we.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=P(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=h(t),u=pt.test(t),l=e.style;if(u||(t=I(s)),a=we.cssHooks[t]||we.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];o=typeof n,"string"===o&&(i=ze.exec(n))&&i[1]&&(n=m(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(we.cssNumber[s]?"":"px")),ye.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=h(t);return pt.test(t)||(t=I(s)),a=we.cssHooks[t]||we.cssHooks[s],a&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=P(e,t,r)),"normal"===i&&t in ht&&(i=ht[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),we.each(["height","width"],function(e,t){we.cssHooks[t]={get:function(e,n,r){if(n)return!ft.test(we.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?B(e,t,r):Ve(e,dt,function(){return B(e,t,r)})},set:function(e,n,r){var i,o=lt(e),a="border-box"===we.css(e,"boxSizing",!1,o),s=r&&$(e,t,r,a,o);return a&&ye.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-$(e,t,"border",!1,o)-.5)),s&&(i=ze.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=we.css(e,t)),W(e,n,s)}}}),we.cssHooks.marginLeft=M(ye.reliableMarginLeft,function(e,t){if(t)return(parseFloat(P(e,"marginLeft"))||e.getBoundingClientRect().left-Ve(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),we.each({margin:"",padding:"",border:"Width"},function(e,t){we.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Xe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(we.cssHooks[e+t].set=W)}),we.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=lt(e),i=t.length;a<i;a++)o[t[a]]=we.css(e,t[a],!1,r);return o}return void 0!==n?we.style(e,t,n):we.css(e,t)},e,t,arguments.length>1)}}),we.Tween=F,F.prototype={constructor:F,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||we.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(we.cssNumber[n]?"":"px")},cur:function(){var e=F.propHooks[this.prop];return e&&e.get?e.get(this):F.propHooks._default.get(this)},run:function(e){var t,n=F.propHooks[this.prop];return this.options.duration?this.pos=t=we.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):F.propHooks._default.set(this),this}},F.prototype.init.prototype=F.prototype,F.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=we.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){we.fx.step[e.prop]?we.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[we.cssProps[e.prop]]&&!we.cssHooks[e.prop]?e.elem[e.prop]=e.now:we.style(e.elem,e.prop,e.now+e.unit)}}},F.propHooks.scrollTop=F.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},we.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},we.fx=F.prototype.init,we.fx.step={};var yt,mt,xt=/^(?:toggle|show|hide)$/,bt=/queueHooks$/;we.Animation=we.extend(Y,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return m(n.elem,e,ze.exec(t),n),n}]},tweener:function(e,t){me(e)?(t=e,e=["*"]):e=e.match(Le);for(var n,r=0,i=e.length;r<i;r++)n=e[r],Y.tweeners[n]=Y.tweeners[n]||[],Y.tweeners[n].unshift(t)},prefilters:[V],prefilter:function(e,t){t?Y.prefilters.unshift(e):Y.prefilters.push(e)}}),we.speed=function(e,t,n){var r=e&&"object"==typeof e?we.extend({},e):{complete:n||!n&&t||me(e)&&e,duration:e,easing:n&&t||t&&!me(t)&&t};return we.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in we.fx.speeds?r.duration=we.fx.speeds[r.duration]:r.duration=we.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){me(r.old)&&r.old.call(this),r.queue&&we.dequeue(this,r.queue)},r},we.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Ue).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=we.isEmptyObject(e),o=we.speed(t,n,r),a=function(){var t=Y(this,we.extend({},e),o);(i||We.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&!1!==e&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=we.timers,a=We.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&bt.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));!t&&n||we.dequeue(this,e)})},finish:function(e){return!1!==e&&(e=e||"fx"),this.each(function(){
+var t,n=We.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=we.timers,a=r?r.length:0;for(n.finish=!0,we.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;t<a;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),we.each(["toggle","show","hide"],function(e,t){var n=we.fn[t];we.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(X(t,!0),e,r,i)}}),we.each({slideDown:X("show"),slideUp:X("hide"),slideToggle:X("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){we.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),we.timers=[],we.fx.tick=function(){var e,t=0,n=we.timers;for(yt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||we.fx.stop(),yt=void 0},we.fx.timer=function(e){we.timers.push(e),we.fx.start()},we.fx.interval=13,we.fx.start=function(){mt||(mt=!0,_())},we.fx.stop=function(){mt=null},we.fx.speeds={slow:600,fast:200,_default:400},we.fn.delay=function(t,n){return t=we.fx?we.fx.speeds[t]||t:t,n=n||"fx",this.queue(n,function(n,r){var i=e.setTimeout(n,t);r.stop=function(){e.clearTimeout(i)}})},function(){var e=ae.createElement("input"),t=ae.createElement("select"),n=t.appendChild(ae.createElement("option"));e.type="checkbox",ye.checkOn=""!==e.value,ye.optSelected=n.selected,e=ae.createElement("input"),e.value="t",e.type="radio",ye.radioValue="t"===e.value}();var wt,Tt=we.expr.attrHandle;we.fn.extend({attr:function(e,t){return Pe(this,we.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){we.removeAttr(this,e)})}}),we.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?we.prop(e,t,n):(1===o&&we.isXMLDoc(e)||(i=we.attrHooks[t.toLowerCase()]||(we.expr.match.bool.test(t)?wt:void 0)),void 0!==n?null===n?void we.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=we.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!ye.radioValue&&"radio"===t&&o(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(Le);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),wt={set:function(e,t,n){return!1===t?we.removeAttr(e,n):e.setAttribute(n,n),n}},we.each(we.expr.match.bool.source.match(/\w+/g),function(e,t){var n=Tt[t]||we.find.attr;Tt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=Tt[a],Tt[a]=i,i=null!=n(e,t,r)?a:null,Tt[a]=o),i}});var Ct=/^(?:input|select|textarea|button)$/i,Et=/^(?:a|area)$/i;we.fn.extend({prop:function(e,t){return Pe(this,we.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[we.propFix[e]||e]})}}),we.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&we.isXMLDoc(e)||(t=we.propFix[t]||t,i=we.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=we.find.attr(e,"tabindex");return t?parseInt(t,10):Ct.test(e.nodeName)||Et.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),ye.optSelected||(we.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),we.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){we.propFix[this.toLowerCase()]=this}),we.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(me(e))return this.each(function(t){we(this).addClass(e.call(this,t,J(this)))});if(t=K(e),t.length)for(;n=this[u++];)if(i=J(n),r=1===n.nodeType&&" "+Q(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=Q(r),i!==s&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(me(e))return this.each(function(t){we(this).removeClass(e.call(this,t,J(this)))});if(!arguments.length)return this.attr("class","");if(t=K(e),t.length)for(;n=this[u++];)if(i=J(n),r=1===n.nodeType&&" "+Q(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=Q(r),i!==s&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):me(e)?this.each(function(n){we(this).toggleClass(e.call(this,n,J(this),t),t)}):this.each(function(){var t,i,o,a;if(r)for(i=0,o=we(this),a=K(e);t=a[i++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||(t=J(this),t&&We.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":We.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+Q(J(n))+" ").indexOf(t)>-1)return!0;return!1}});var kt=/\r/g;we.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=me(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,we(this).val()):e,null==i?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=we.map(i,function(e){return null==e?"":e+""})),(t=we.valHooks[this.type]||we.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=we.valHooks[i.type]||we.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(kt,""):null==n?"":n)}}}),we.extend({valHooks:{option:{get:function(e){var t=we.find.attr(e,"value");return null!=t?t:Q(we.text(e))}},select:{get:function(e){var t,n,r,i=e.options,a=e.selectedIndex,s="select-one"===e.type,u=s?null:[],l=s?a+1:i.length;for(r=a<0?l:s?a:0;r<l;r++)if(n=i[r],(n.selected||r===a)&&!n.disabled&&(!n.parentNode.disabled||!o(n.parentNode,"optgroup"))){if(t=we(n).val(),s)return t;u.push(t)}return u},set:function(e,t){for(var n,r,i=e.options,o=we.makeArray(t),a=i.length;a--;)r=i[a],(r.selected=we.inArray(we.valHooks.option.get(r),o)>-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),we.each(["radio","checkbox"],function(){we.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=we.inArray(we(e).val(),t)>-1}},ye.checkOn||(we.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),ye.focusin="onfocusin"in e;var St=/^(?:focusinfocus|focusoutblur)$/,Dt=function(e){e.stopPropagation()};we.extend(we.event,{trigger:function(t,n,r,i){var o,a,s,u,l,c,f,p,d=[r||ae],h=he.call(t,"type")?t.type:t,g=he.call(t,"namespace")?t.namespace.split("."):[];if(a=p=s=r=r||ae,3!==r.nodeType&&8!==r.nodeType&&!St.test(h+we.event.triggered)&&(h.indexOf(".")>-1&&(g=h.split("."),h=g.shift(),g.sort()),l=h.indexOf(":")<0&&"on"+h,t=t[we.expando]?t:new we.Event(h,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:we.makeArray(n,[t]),f=we.event.special[h]||{},i||!f.trigger||!1!==f.trigger.apply(r,n))){if(!i&&!f.noBubble&&!xe(r)){for(u=f.delegateType||h,St.test(u+h)||(a=a.parentNode);a;a=a.parentNode)d.push(a),s=a;s===(r.ownerDocument||ae)&&d.push(s.defaultView||s.parentWindow||e)}for(o=0;(a=d[o++])&&!t.isPropagationStopped();)p=a,t.type=o>1?u:f.bindType||h,c=(We.get(a,"events")||{})[t.type]&&We.get(a,"handle"),c&&c.apply(a,n),(c=l&&a[l])&&c.apply&&Ie(a)&&(t.result=c.apply(a,n),!1===t.result&&t.preventDefault());return t.type=h,i||t.isDefaultPrevented()||f._default&&!1!==f._default.apply(d.pop(),n)||!Ie(r)||l&&me(r[h])&&!xe(r)&&(s=r[l],s&&(r[l]=null),we.event.triggered=h,t.isPropagationStopped()&&p.addEventListener(h,Dt),r[h](),t.isPropagationStopped()&&p.removeEventListener(h,Dt),we.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=we.extend(new we.Event,n,{type:e,isSimulated:!0});we.event.trigger(r,null,t)}}),we.fn.extend({trigger:function(e,t){return this.each(function(){we.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return we.event.trigger(e,t,n,!0)}}),ye.focusin||we.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){we.event.simulate(t,e.target,we.event.fix(e))};we.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=We.access(r,t);i||r.addEventListener(e,n,!0),We.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=We.access(r,t)-1;i?We.access(r,t,i):(r.removeEventListener(e,n,!0),We.remove(r,t))}}});var Nt=e.location,At=Date.now(),jt=/\?/;we.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||we.error("Invalid XML: "+t),n};var qt=/\[\]$/,Lt=/\r?\n/g,Ht=/^(?:submit|button|image|reset|file)$/i,Ot=/^(?:input|select|textarea|keygen)/i;we.param=function(e,t){var n,r=[],i=function(e,t){var n=me(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!we.isPlainObject(e))we.each(e,function(){i(this.name,this.value)});else for(n in e)Z(n,e[n],t,i);return r.join("&")},we.fn.extend({serialize:function(){return we.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=we.prop(this,"elements");return e?we.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!we(this).is(":disabled")&&Ot.test(this.nodeName)&&!Ht.test(e)&&(this.checked||!Ye.test(e))}).map(function(e,t){var n=we(this).val();return null==n?null:Array.isArray(n)?we.map(n,function(e){return{name:t.name,value:e.replace(Lt,"\r\n")}}):{name:t.name,value:n.replace(Lt,"\r\n")}}).get()}});var Pt=/%20/g,Mt=/#.*$/,Rt=/([?&])_=[^&]*/,It=/^(.*?):[ \t]*([^\r\n]*)$/gm,Wt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,$t=/^(?:GET|HEAD)$/,Bt=/^\/\//,Ft={},_t={},zt="*/".concat("*"),Xt=ae.createElement("a");Xt.href=Nt.href,we.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Nt.href,type:"GET",isLocal:Wt.test(Nt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":we.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ne(ne(e,we.ajaxSettings),t):ne(we.ajaxSettings,e)},ajaxPrefilter:ee(Ft),ajaxTransport:ee(_t),ajax:function(t,n){function r(t,n,r,s){var l,p,d,b,w,T=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",C.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=re(h,C,r)),b=ie(h,b,C,l),l?(h.ifModified&&(w=C.getResponseHeader("Last-Modified"),w&&(we.lastModified[o]=w),(w=C.getResponseHeader("etag"))&&(we.etag[o]=w)),204===t||"HEAD"===h.type?T="nocontent":304===t?T="notmodified":(T=b.state,p=b.data,d=b.error,l=!d)):(d=T,!t&&T||(T="error",t<0&&(t=0))),C.status=t,C.statusText=(n||T)+"",l?y.resolveWith(g,[p,T,C]):y.rejectWith(g,[C,T,d]),C.statusCode(x),x=void 0,f&&v.trigger(l?"ajaxSuccess":"ajaxError",[C,h,l?p:d]),m.fireWith(g,[C,T]),f&&(v.trigger("ajaxComplete",[C,h]),--we.active||we.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=we.ajaxSetup({},n),g=h.context||h,v=h.context&&(g.nodeType||g.jquery)?we(g):we.event,y=we.Deferred(),m=we.Callbacks("once memory"),x=h.statusCode||{},b={},w={},T="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=It.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)C.always(e[C.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(y.promise(C),h.url=((t||h.url||Nt.href)+"").replace(Bt,Nt.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(Le)||[""],null==h.crossDomain){l=ae.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Xt.protocol+"//"+Xt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=we.param(h.data,h.traditional)),te(Ft,h,n,C),c)return C;f=we.event&&h.global,f&&0==we.active++&&we.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!$t.test(h.type),o=h.url.replace(Mt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(Pt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(jt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Rt,"$1"),d=(jt.test(o)?"&":"?")+"_="+At+++d),h.url=o+d),h.ifModified&&(we.lastModified[o]&&C.setRequestHeader("If-Modified-Since",we.lastModified[o]),we.etag[o]&&C.setRequestHeader("If-None-Match",we.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&C.setRequestHeader("Content-Type",h.contentType),C.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+zt+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)C.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,C,h)||c))return C.abort();if(T="abort",m.add(h.complete),C.done(h.success),C.fail(h.error),i=te(_t,h,n,C)){if(C.readyState=1,f&&v.trigger("ajaxSend",[C,h]),c)return C;h.async&&h.timeout>0&&(u=e.setTimeout(function(){C.abort("timeout")},h.timeout));try{c=!1,i.send(b,r)}catch(e){if(c)throw e;r(-1,e)}}else r(-1,"No Transport");return C},getJSON:function(e,t,n){return we.get(e,t,n,"json")},getScript:function(e,t){return we.get(e,void 0,t,"script")}}),we.each(["get","post"],function(e,t){we[t]=function(e,n,r,i){return me(n)&&(i=i||r,r=n,n=void 0),we.ajax(we.extend({url:e,type:t,dataType:i,data:n,success:r},we.isPlainObject(e)&&e))}}),we._evalUrl=function(e){return we.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},we.fn.extend({wrapAll:function(e){var t;return this[0]&&(me(e)&&(e=e.call(this[0])),t=we(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return me(e)?this.each(function(t){we(this).wrapInner(e.call(this,t))}):this.each(function(){var t=we(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=me(e);return this.each(function(n){we(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){we(this).replaceWith(this.childNodes)}),this}}),we.expr.pseudos.hidden=function(e){return!we.expr.pseudos.visible(e)},we.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},we.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Ut={0:200,1223:204},Vt=we.ajaxSettings.xhr();ye.cors=!!Vt&&"withCredentials"in Vt,ye.ajax=Vt=!!Vt,we.ajaxTransport(function(t){var n,r;if(ye.cors||Vt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Ut[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),we.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),we.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return we.globalEval(e),e}}}),we.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),we.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=we("<script>").prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),ae.head.appendChild(t[0])},abort:function(){n&&n()}}}});var Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;we.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||we.expando+"_"+At++;return this[e]=!0,e}}),we.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=!1!==t.jsonp&&(Yt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=me(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(Yt,"$1"+i):!1!==t.jsonp&&(t.url+=(jt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||we.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?we(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,Gt.push(i)),a&&me(o)&&o(a[0]),a=o=void 0}),"script"}),ye.createHTMLDocument=function(){var e=ae.implementation.createHTMLDocument("").body;return e.innerHTML="<form></form><form></form>",2===e.childNodes.length}(),we.parseHTML=function(e,t,n){if("string"!=typeof e)return[];"boolean"==typeof t&&(n=t,t=!1);var r,i,o;return t||(ye.createHTMLDocument?(t=ae.implementation.createHTMLDocument(""),r=t.createElement("base"),r.href=ae.location.href,t.head.appendChild(r)):t=ae),i=De.exec(e),o=!n&&[],i?[t.createElement(i[1])]:(i=C([e],t,o),o&&o.length&&we(o).remove(),we.merge([],i.childNodes))},we.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=Q(e.slice(s)),e=e.slice(0,s)),me(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&we.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?we("<div>").append(we.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},we.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){we.fn[t]=function(e){return this.on(t,e)}}),we.expr.pseudos.animated=function(e){return we.grep(we.timers,function(t){return e===t.elem}).length},we.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=we.css(e,"position"),f=we(e),p={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=we.css(e,"top"),u=we.css(e,"left"),l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),me(t)&&(t=t.call(e,n,we.extend({},s))),null!=t.top&&(p.top=t.top-s.top+a),null!=t.left&&(p.left=t.left-s.left+i),"using"in t?t.using.call(e,p):f.css(p)}},we.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){we.offset.setOffset(this,e,t)});var t,n,r=this[0];if(r)return r.getClientRects().length?(t=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===we.css(r,"position"))t=r.getBoundingClientRect();else{for(t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===we.css(e,"position");)e=e.parentNode;e&&e!==r&&1===e.nodeType&&(i=we(e).offset(),i.top+=we.css(e,"borderTopWidth",!0),i.left+=we.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-we.css(r,"marginTop",!0),left:t.left-i.left-we.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===we.css(e,"position");)e=e.offsetParent;return e||et})}}),we.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;we.fn[e]=function(r){return Pe(this,function(e,r,i){var o;if(xe(e)?o=e:9===e.nodeType&&(o=e.defaultView),void 0===i)return o?o[t]:e[r];o?o.scrollTo(n?o.pageXOffset:i,n?i:o.pageYOffset):e[r]=i},e,r,arguments.length)}}),we.each(["top","left"],function(e,t){we.cssHooks[t]=M(ye.pixelPosition,function(e,n){if(n)return n=P(e,t),ut.test(n)?we(e).position()[t]+"px":n})}),we.each({Height:"height",Width:"width"},function(e,t){we.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){we.fn[r]=function(i,o){var a=arguments.length&&(n||"boolean"!=typeof i),s=n||(!0===i||!0===o?"margin":"border");return Pe(this,function(t,n,i){var o;return xe(t)?0===r.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(o=t.documentElement,Math.max(t.body["scroll"+e],o["scroll"+e],t.body["offset"+e],o["offset"+e],o["client"+e])):void 0===i?we.css(t,n,s):we.style(t,n,i,s)},t,a?i:void 0,a)}})}),we.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){we.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),we.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),we.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),we.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),me(e))return r=ue.call(arguments,2),i=function(){return e.apply(t||this,r.concat(ue.call(arguments)))},i.guid=e.guid=e.guid||we.guid++,i},we.holdReady=function(e){e?we.readyWait++:we.ready(!0)},we.isArray=Array.isArray,we.parseJSON=JSON.parse,we.nodeName=o,we.isFunction=me,we.isWindow=xe,we.camelCase=h,we.type=r,we.now=Date.now,we.isNumeric=function(e){var t=we.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},"function"==typeof define&&define.amd&&define("jquery",[],function(){return we});var Qt=e.jQuery,Jt=e.$;return we.noConflict=function(t){return e.$===we&&(e.$=Jt),t&&e.jQuery===we&&(e.jQuery=Qt),we},t||(e.jQuery=e.$=we),we}); })(this);
// 3rdParty/jquery-ui.js
(function (window, undefined) { !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(t){t.ui=t.ui||{};var e=(t.ui.version="1.12.1",0),i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,o,n;for(n=0;null!=(o=i[n]);n++)try{s=t._data(o,"events"),s&&s.remove&&t(o).triggerHandler("remove")}catch(t){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var o,n,r,a={},h=e.split(".")[0];e=e.split(".")[1];var l=h+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][l.toLowerCase()]=function(e){return!!t.data(e,l)},t[h]=t[h]||{},o=t[h][e],n=t[h][e]=function(t,e){if(!this._createWidget)return new n(t,e);arguments.length&&this._createWidget(t,e)},t.extend(n,o,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),r=new i,r.options=t.widget.extend({},r.options),t.each(s,function(e,s){if(!t.isFunction(s))return void(a[e]=s);a[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function o(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,n=this._superApply;return this._super=t,this._superApply=o,e=s.apply(this,arguments),this._super=i,this._superApply=n,e}}()}),n.prototype=t.widget.extend(r,{widgetEventPrefix:o?r.widgetEventPrefix||e:e},a,{constructor:n,namespace:h,widgetName:e,widgetFullName:l}),o?(t.each(o._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,n,i._proto)}),delete o._childConstructors):i._childConstructors.push(n),t.widget.bridge(e,n),n},t.widget.extend=function(e){for(var s,o,n=i.call(arguments,1),r=0,a=n.length;r<a;r++)for(s in n[r])o=n[r][s],n[r].hasOwnProperty(s)&&void 0!==o&&(t.isPlainObject(o)?e[s]=t.isPlainObject(e[s])?t.widget.extend({},e[s],o):t.widget.extend({},o):e[s]=o);return e},t.widget.bridge=function(e,s){var o=s.prototype.widgetFullName||e;t.fn[e]=function(n){var r="string"==typeof n,a=i.call(arguments,1),h=this;return r?this.length||"instance"!==n?this.each(function(){var i,s=t.data(this,o);return"instance"===n?(h=s,!1):s?t.isFunction(s[n])&&"_"!==n.charAt(0)?(i=s[n].apply(s,a),i!==s&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+n+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; attempted to call method '"+n+"'")}):h=void 0:(a.length&&(n=t.widget.extend.apply(null,[n].concat(a))),this.each(function(){var e=t.data(this,o);e?(e.option(n||{}),e._init&&e._init()):t.data(this,o,new s(n,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,o,n,r=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(r={},s=e.split("."),e=s.shift(),s.length){for(o=r[e]=t.widget.extend({},this.options[e]),n=0;n<s.length-1;n++)o[s[n]]=o[s[n]]||{},o=o[s[n]];if(e=s.pop(),1===arguments.length)return void 0===o[e]?null:o[e];o[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];r[e]=i}return this._setOptions(r),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,o;for(i in e)o=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&o&&o.length&&(s=t(o.get()),this._removeClass(o,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,n){var r,a;for(a=0;a<i.length;a++)r=o.classesElementLookup[i[a]]||t(),r=t(e.add?t.unique(r.get().concat(e.element.get())):r.not(e.element).get()),o.classesElementLookup[i[a]]=r,s.push(i[a]),n&&e.classes[i[a]]&&s.push(e.classes[i[a]])}var s=[],o=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,o){-1!==t.inArray(e.target,o)&&(i.classesElementLookup[s]=t(o.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var o="string"==typeof t||null===t,n={extra:o?e:i,keys:o?t:e,element:o?this.element:t,add:s};return n.element.toggleClass(this._classes(n),s),this},_on:function(e,i,s){var o,n=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=o=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,o=this.widget()),t.each(s,function(s,r){function a(){if(e||!0!==n.options.disabled&&!t(this).hasClass("ui-state-disabled"))return("string"==typeof r?n[r]:r).apply(n,arguments)}"string"!=typeof r&&(a.guid=r.guid=r.guid||a.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+n.eventNamespace,c=h[2];c?o.on(l,c,a):i.on(l,a)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var o,n,r=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],n=i.originalEvent)for(o in n)o in i||(i[o]=n[o]);return this.element.trigger(i,s),!(t.isFunction(r)&&!1===r.apply(this.element[0],[i].concat(s))||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,o,n){"string"==typeof o&&(o={effect:o});var r,a=o?!0===o||"number"==typeof o?i:o.effect||i:e;o=o||{},"number"==typeof o&&(o={duration:o}),r=!t.isEmptyObject(o),o.complete=n,o.delay&&s.delay(o.delay),r&&t.effects&&t.effects.effect[a]?s[e](o):a!==e&&s[a]?s[a](o.duration,o.easing,n):s.queue(function(i){t(this)[e](),n&&n.call(s[0]),i()})}});t.widget;!function(){function e(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var o,n=Math.max,r=Math.abs,a=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==o)return o;var e,i,s=t("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),n=s.children()[0];return t("body").append(s),e=n.offsetWidth,s.css("overflow","scroll"),i=n.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),o=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),o="scroll"===i||"auto"===i&&e.width<e.element[0].scrollWidth;return{width:"scroll"===s||"auto"===s&&e.height<e.element[0].scrollHeight?t.position.scrollbarWidth():0,height:o?t.position.scrollbarWidth():0}},getWithinInfo:function(e){var i=t(e||window),s=t.isWindow(i[0]),o=!!i[0]&&9===i[0].nodeType;return{element:i,isWindow:s,isDocument:o,offset:s||o?{left:0,top:0}:t(e).offset(),scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:i.outerWidth(),height:i.outerHeight()}}},t.fn.position=function(o){if(!o||!o.of)return f.apply(this,arguments);o=t.extend({},o);var p,u,d,g,m,v,_=t(o.of),b=t.position.getWithinInfo(o.within),w=t.position.getScrollInfo(b),y=(o.collision||"flip").split(" "),P={};return v=s(_),_[0].preventDefault&&(o.at="left top"),u=v.width,d=v.height,g=v.offset,m=t.extend({},g),t.each(["my","at"],function(){var t,e,i=(o[this]||"").split(" ");1===i.length&&(i=a.test(i[0])?i.concat(["center"]):h.test(i[0])?["center"].concat(i):["center","center"]),i[0]=a.test(i[0])?i[0]:"center",i[1]=h.test(i[1])?i[1]:"center",t=l.exec(i[0]),e=l.exec(i[1]),P[this]=[t?t[0]:0,e?e[0]:0],o[this]=[c.exec(i[0])[0],c.exec(i[1])[0]]}),1===y.length&&(y[1]=y[0]),"right"===o.at[0]?m.left+=u:"center"===o.at[0]&&(m.left+=u/2),"bottom"===o.at[1]?m.top+=d:"center"===o.at[1]&&(m.top+=d/2),p=e(P.at,u,d),m.left+=p[0],m.top+=p[1],this.each(function(){var s,a,h=t(this),l=h.outerWidth(),c=h.outerHeight(),f=i(this,"marginLeft"),v=i(this,"marginTop"),x=l+f+i(this,"marginRight")+w.width,C=c+v+i(this,"marginBottom")+w.height,z=t.extend({},m),H=e(P.my,h.outerWidth(),h.outerHeight());"right"===o.my[0]?z.left-=l:"center"===o.my[0]&&(z.left-=l/2),"bottom"===o.my[1]?z.top-=c:"center"===o.my[1]&&(z.top-=c/2),z.left+=H[0],z.top+=H[1],s={marginLeft:f,marginTop:v},t.each(["left","top"],function(e,i){t.ui.position[y[e]]&&t.ui.position[y[e]][i](z,{targetWidth:u,targetHeight:d,elemWidth:l,elemHeight:c,collisionPosition:s,collisionWidth:x,collisionHeight:C,offset:[p[0]+H[0],p[1]+H[1]],my:o.my,at:o.at,within:b,elem:h})}),o.using&&(a=function(t){var e=g.left-z.left,i=e+u-l,s=g.top-z.top,a=s+d-c,p={target:{element:_,left:g.left,top:g.top,width:u,height:d},element:{element:h,left:z.left,top:z.top,width:l,height:c},horizontal:i<0?"left":e>0?"right":"center",vertical:a<0?"top":s>0?"bottom":"middle"};u<l&&r(e+i)<u&&(p.horizontal="center"),d<c&&r(s+a)<d&&(p.vertical="middle"),n(r(e),r(i))>n(r(s),r(a))?p.important="horizontal":p.important="vertical",o.using.call(this,t,p)}),h.offset(t.extend(z,{using:a}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,o=s.isWindow?s.scrollLeft:s.offset.left,r=s.width,a=t.left-e.collisionPosition.marginLeft,h=o-a,l=a+e.collisionWidth-r-o;e.collisionWidth>r?h>0&&l<=0?(i=t.left+h+e.collisionWidth-r-o,t.left+=h-i):t.left=l>0&&h<=0?o:h>l?o+r-e.collisionWidth:o:h>0?t.left+=h:l>0?t.left-=l:t.left=n(t.left-a,t.left)},top:function(t,e){var i,s=e.within,o=s.isWindow?s.scrollTop:s.offset.top,r=e.within.height,a=t.top-e.collisionPosition.marginTop,h=o-a,l=a+e.collisionHeight-r-o;e.collisionHeight>r?h>0&&l<=0?(i=t.top+h+e.collisionHeight-r-o,t.top+=h-i):t.top=l>0&&h<=0?o:h>l?o+r-e.collisionHeight:o:h>0?t.top+=h:l>0?t.top-=l:t.top=n(t.top-a,t.top)}},flip:{left:function(t,e){var i,s,o=e.within,n=o.offset.left+o.scrollLeft,a=o.width,h=o.isWindow?o.scrollLeft:o.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,p=l+e.collisionWidth-a-h,f="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,u="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,d=-2*e.offset[0];c<0?((i=t.left+f+u+d+e.collisionWidth-a-n)<0||i<r(c))&&(t.left+=f+u+d):p>0&&((s=t.left-e.collisionPosition.marginLeft+f+u+d-h)>0||r(s)<p)&&(t.left+=f+u+d)},top:function(t,e){var i,s,o=e.within,n=o.offset.top+o.scrollTop,a=o.height,h=o.isWindow?o.scrollTop:o.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,p=l+e.collisionHeight-a-h,f="top"===e.my[1],u=f?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,d="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];c<0?((s=t.top+u+d+g+e.collisionHeight-a-n)<0||s<r(c))&&(t.top+=u+d+g):p>0&&((i=t.top-e.collisionPosition.marginTop+u+d+g-h)>0||r(i)<p)&&(t.top+=u+d+g)}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}();var s=(t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,o=e?/(auto|scroll|hidden)/:/(auto|scroll)/,n=this.parents().filter(function(){var e=t(this);return(!s||"static"!==e.css("position"))&&o.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&n.length?n:t(this[0].ownerDocument||document)},t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),!1);t(document).on("mouseup",function(){s=!1});t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){if(!0===t.data(i.target,e.widgetName+".preventClickEvent"))return t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,o=1===e.which,n=!("string"!=typeof this.options.cancel||!e.target.nodeName)&&t(e.target).closest(this.options.cancel).length;return!(o&&!n&&this._mouseCapture(e))||(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=!1!==this._mouseStart(e),!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0))}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||document.documentMode<9)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=!1!==this._mouseStart(this._mouseDownEvent,e),this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var o,n=t.ui[e].prototype;for(o in s)n.plugins[o]=n.plugins[o]||[],n.plugins[o].push([i,s[o]])},call:function(t,e,i,s){var o,n=t.plugins[e];if(n&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(o=0;o<n.length;o++)t.options[n[o][0]]&&n[o][1].apply(t.element,i)}},t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.ui.safeBlur=function(e){e&&"body"!==e.nodeName.toLowerCase()&&t(e).trigger("blur")};t.widget("ui.draggable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this._addClass("ui-draggable"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){if((this.helper||this.element).is(".ui-draggable-dragging"))return void(this.destroyOnClear=!0);this._removeHandleClassName(),this._mouseDestroy()},_mouseCapture:function(e){var i=this.options;return!(this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0)&&(this.handle=this._getHandle(e),!!this.handle&&(this._blurActiveElement(e),this._blockFrames(!0===i.iframeFix?"iframe":i.iframeFix),!0))},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t("<div>").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=t.ui.safeActiveElement(this.document[0]);t(e.target).closest(i).length||t.ui.safeBlur(i)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this._addClass(this.helper,"ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===t(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),!1===this._trigger("start",e)?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(!1===this._trigger("drag",e,s))return this._mouseUp(new t.Event("mouseup",e)),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||!0===this.options.revert||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){!1!==i._trigger("stop",e)&&i._clear()}):!1!==this._trigger("stop",e)&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.trigger("focus"),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp(new t.Event("mouseup",{target:this.element[0]})):this._clear(),this},_getHandle:function(e){return!this.options.handle||!!t(e.target).closest(this.element.find(this.options.handle)).length},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),o=s?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return o.parents("body").length||o.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&o[0]===this.element[0]&&this._setPositionRelative(),o[0]===this.element[0]||/(fixed|absolute)/.test(o.css("position"))||o.css("position","absolute"),o},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,o=this.options,n=this.document[0];return this.relativeContainer=null,o.containment?"window"===o.containment?void(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||n.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]):"document"===o.containment?void(this.containment=[0,0,t(n).width()-this.helperProportions.width-this.margins.left,(t(n).height()||n.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]):o.containment.constructor===Array?void(this.containment=o.containment):("parent"===o.containment&&(o.containment=this.helper[0].parentNode),i=t(o.containment),void((s=i[0])&&(e=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i))):void(this.containment=null)},_convertPositionTo:function(t,e){e||(e=this.position);var i="absolute"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,o,n,r=this.options,a=this._isRootNode(this.scrollParent[0]),h=t.pageX,l=t.pageY;return a&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),t.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),t.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),r.grid&&(o=r.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/r.grid[1])*r.grid[1]:this.originalPageY,l=i?o-this.offset.click.top>=i[1]||o-this.offset.click.top>i[3]?o:o-this.offset.click.top>=i[1]?o-r.grid[1]:o+r.grid[1]:o,n=r.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/r.grid[0])*r.grid[0]:this.originalPageX,h=i?n-this.offset.click.left>=i[0]||n-this.offset.click.left>i[2]?n:n-this.offset.click.left>=i[0]?n-r.grid[0]:n+r.grid[0]:n),"y"===r.axis&&(h=this.originalPageX),"x"===r.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:a?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:a?0:this.offset.scroll.left)}},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i,s){var o=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",e,o))})},stop:function(e,i,s){var o=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,o))})},drag:function(e,i,s){t.each(s.sortables,function(){var o=!1,n=this;n.positionAbs=s.positionAbs,n.helperProportions=s.helperProportions,n.offset.click=s.offset.click,n._intersectsWith(n.containerCache)&&(o=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==n&&this._intersectsWith(this.containerCache)&&t.contains(n.element[0],this.element[0])&&(o=!1),o})),o?(n.isOver||(n.isOver=1,s._parent=i.helper.parent(),n.currentItem=i.helper.appendTo(n.element).data("ui-sortable-item",!0),n.options._helper=n.options.helper,n.options.helper=function(){return i.helper[0]},e.target=n.currentItem[0],n._mouseCapture(e,!0),n._mouseStart(e,!0,!0),n.offset.click.top=s.offset.click.top,n.offset.click.left=s.offset.click.left,n.offset.parent.left-=s.offset.parent.left-n.offset.parent.left,
(function (window, undefined) { !function(){var e=!1,t=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){},Class.extend=function(n){function r(){!e&&this.init&&this.init.apply(this,arguments)}var i=this.prototype;e=!0;var o=new this;e=!1;for(var a in n)o[a]="function"==typeof n[a]&&"function"==typeof i[a]&&t.test(n[a])?function(e,t){return function(){var n=this._super;this._super=i[e];var r=t.apply(this,arguments);return this._super=n,r}}(a,n[a]):n[a];return r.prototype=o,r.prototype.constructor=r,r.extend=arguments.callee,r}}(),function(e,t){"use strict";function n(){}function r(e,t){if(e){"object"==typeof e&&(e=[].slice.call(e));for(var n=0,r=e.length;n<r;n++)t.call(e,e[n],n)}}function i(e,n){var r=Object.prototype.toString.call(n).slice(8,-1);return n!==t&&null!==n&&r===e}function o(e){return i("Function",e)}function a(e){return i("Array",e)}function l(e){var t=e.split("/"),n=t[t.length-1],r=n.indexOf("?");return-1!==r?n.substring(0,r):n}function u(e){e=e||n,e._done||(e(),e._done=1)}function c(e,t,r,i){var o="object"==typeof e?e:{test:e,success:!!t&&(a(t)?t:[t]),failure:!!r&&(a(r)?r:[r]),callback:i||n},l=!!o.test;return l&&o.success?(o.success.push(o.callback),N.load.apply(null,o.success)):l||!o.failure?i():(o.failure.push(o.callback),N.load.apply(null,o.failure)),N}function s(e){var t,n,r={};if("object"==typeof e)for(t in e)!e[t]||(r={name:t,url:e[t]});else r={name:l(e),url:e};return(n=S[r.name])&&n.url===r.url?n:(S[r.name]=r,r)}function p(e){e=e||S;for(var t in e)if(e.hasOwnProperty(t)&&e[t].state!==F)return!1;return!0}function d(e){e.state=P,r(e.onpreload,function(e){e.call()})}function f(e){e.state===t&&(e.state=I,e.onpreload=[],b({url:e.url,type:"cache"},function(){d(e)}))}function h(){var e=arguments,t=e[e.length-1],n=[].slice.call(e,1),i=n[0];return o(t)||(t=null),a(e[0])?(e[0].push(t),N.load.apply(null,e[0]),N):(i?(r(n,function(e){o(e)||!e||f(s(e))}),v(s(e[0]),o(i)?i:function(){N.load.apply(null,n)})):v(s(e[0])),N)}function g(){var e=arguments,t=e[e.length-1],n={};return o(t)||(t=null),a(e[0])?(e[0].push(t),N.load.apply(null,e[0]),N):(r(e,function(e){e!==t&&(e=s(e),n[e.name]=e)}),r(e,function(e){e!==t&&(e=s(e),v(e,function(){p(n)&&u(t)}))}),N)}function v(e,t){return t=t||n,e.state===F?void t():e.state===D?void N.ready(e.name,t):e.state===I?void e.onpreload.push(function(){v(e,t)}):(e.state=D,void b(e,function(){e.state=F,t(),r(k[e.name],function(e){u(e)}),O&&p()&&r(k.ALL,function(e){u(e)})}))}function m(e){e=e||"";var t=e.split("?")[0].split(".");return t[t.length-1].toLowerCase()}function b(t,r){function i(t){t=t||e.event,l.onload=l.onreadystatechange=l.onerror=null,r()}function o(n){n=n||e.event,("load"===n.type||/loaded|complete/.test(l.readyState)&&(!x.documentMode||x.documentMode<9))&&(e.clearTimeout(t.errorTimeout),e.clearTimeout(t.cssTimeout),l.onload=l.onreadystatechange=l.onerror=null,r())}function a(){if(t.state!==F&&t.cssRetries<=20){for(var n=0,r=x.styleSheets.length;n<r;n++)if(x.styleSheets[n].href===l.href)return void o({type:"load"});t.cssRetries++,t.cssTimeout=e.setTimeout(a,250)}}var l,u,c;r=r||n,u=m(t.url),"css"===u?(l=x.createElement("link"),l.type="text/"+(t.type||"css"),l.rel="stylesheet",l.href=t.url,t.cssRetries=0,t.cssTimeout=e.setTimeout(a,500)):(l=x.createElement("script"),l.type="text/"+(t.type||"javascript"),l.src=t.url),l.onload=l.onreadystatechange=o,l.onerror=i,l.async=!1,l.defer=!1,t.errorTimeout=e.setTimeout(function(){i({type:"timeout"})},7e3),c=x.head||x.getElementsByTagName("head")[0],c.insertBefore(l,c.lastChild)}function w(){for(var e,t=x.getElementsByTagName("script"),n=0,r=t.length;n<r;n++)if(!!(e=t[n].getAttribute("data-headjs-load")))return void N.load(e)}function y(e,t){var n,i,l;return e===x?(O?u(t):L.push(t),N):(o(e)&&(t=e,e="ALL"),a(e)?(n={},r(e,function(e){n[e]=S[e],N.ready(e,function(){p(n)&&u(t)})}),N):"string"==typeof e&&o(t)?(i=S[e])&&i.state===F||"ALL"===e&&p()&&O?(u(t),N):(l=k[e],l?l.push(t):l=k[e]=[t],N):N)}function T(){if(!x.body)return e.clearTimeout(N.readyTimeout),void(N.readyTimeout=e.setTimeout(T,50));O||(O=!0,w(),r(L,function(e){u(e)}))}function E(){x.addEventListener?(x.removeEventListener("DOMContentLoaded",E,!1),T()):"complete"===x.readyState&&(x.detachEvent("onreadystatechange",E),T())}var O,A,x=e.document,L=[],k={},S={},C="async"in x.createElement("script")||"MozAppearance"in x.documentElement.style||e.opera,M=e.head_conf&&e.head_conf.head||"head",N=e[M]=e[M]||function(){N.ready.apply(null,arguments)},I=1,P=2,D=3,F=4;if("complete"===x.readyState)T();else if(x.addEventListener)x.addEventListener("DOMContentLoaded",E,!1),e.addEventListener("load",T,!1);else{x.attachEvent("onreadystatechange",E),e.attachEvent("onload",T),A=!1;try{A=!e.frameElement&&x.documentElement}catch(e){}A&&A.doScroll&&function t(){if(!O){try{A.doScroll("left")}catch(n){return e.clearTimeout(N.readyTimeout),void(N.readyTimeout=e.setTimeout(t,50))}T()}}()}N.load=N.js=C?g:h,N.test=c,N.ready=y,N.ready(x,function(){p()&&r(k.ALL,function(e){u(e)}),N.feature&&N.feature("domloaded",!0)})}(window),function(e){"function"==typeof define&&define.amd&&define.amd.jQuery?define(["jquery"],e):e(jQuery)}(function(e){function t(t){return!t||void 0!==t.allowPageScroll||void 0===t.swipe&&void 0===t.swipeStatus||(t.allowPageScroll=c),void 0!==t.click&&void 0===t.tap&&(t.tap=t.click),t||(t={}),t=e.extend({},e.fn.swipe.defaults,t),this.each(function(){var r=e(this),i=r.data(k);i||(i=new n(this,t),r.data(k,i))})}function n(t,n){function S(t){if(!(ce()||e(t.target).closest(n.excludedElements,Xe).length>0)){var r,i=t.originalEvent?t.originalEvent:t,o=A?i.touches[0]:i;return Qe=y,(A?Ye=i.touches.length:t.preventDefault(),Fe=0,ze=null,Ue=null,Re=0,_e=0,je=0,Ve=1,qe=0,Be=he(),He=me(),le(),!A||Ye===n.fingers||n.fingers===b||H()?(pe(0,o),We=Le(),2==Ye&&(pe(1,i.touches[1]),_e=je=ye(Be[0].start,Be[1].start)),(n.swipeStatus||n.pinchStatus)&&(r=F(i,Qe))):r=!1,!1===r)?(Qe=O,F(i,Qe),r):(n.hold&&(et=setTimeout(e.proxy(function(){Xe.trigger("hold",[i.target]),n.hold&&(r=n.hold.call(Xe,i,i.target))},this),n.longTapThreshold)),se(!0),null)}}function C(e){var t=e.originalEvent?e.originalEvent:e;if(Qe!==E&&Qe!==O&&!ue()){var r,i=A?t.touches[0]:t,o=de(i);if(Ze=Le(),A&&(Ye=t.touches.length),n.hold&&clearTimeout(et),Qe=T,2==Ye&&(0==_e?(pe(1,t.touches[1]),_e=je=ye(Be[0].start,Be[1].start)):(de(t.touches[1]),je=ye(Be[0].end,Be[1].end),Ue=Ee(Be[0].end,Be[1].end)),Ve=Te(_e,je),qe=Math.abs(_e-je)),Ye===n.fingers||n.fingers===b||!A||H()){if(ze=xe(o.start,o.end),q(e,ze),Fe=Oe(o.start,o.end),Re=we(),ge(ze,Fe),(n.swipeStatus||n.pinchStatus)&&(r=F(t,Qe)),!n.triggerOnTouchEnd||n.triggerOnTouchLeave){var a=!0;if(n.triggerOnTouchLeave){var l=ke(this);a=Se(o.end,l)}!n.triggerOnTouchEnd&&a?Qe=D(T):n.triggerOnTouchLeave&&!a&&(Qe=D(E)),Qe!=O&&Qe!=E||F(t,Qe)}}else Qe=O,F(t,Qe);!1===r&&(Qe=O,F(t,Qe))}}function M(e){var t=e.originalEvent;return A&&t.touches.length>0?(ae(),!0):(ue()&&(Ye=Ke),Ze=Le(),Re=we(),_()||!R()?(Qe=O,F(t,Qe)):n.triggerOnTouchEnd||0==n.triggerOnTouchEnd&&Qe===T?(e.preventDefault(),Qe=E,F(t,Qe)):!n.triggerOnTouchEnd&&G()?(Qe=E,z(t,Qe,f)):Qe===T&&(Qe=O,F(t,Qe)),se(!1),null)}function N(){Ye=0,Ze=0,We=0,_e=0,je=0,Ve=1,le(),se(!1)}function I(e){var t=e.originalEvent;n.triggerOnTouchLeave&&(Qe=D(E),F(t,Qe))}function P(){Xe.unbind(Me,S),Xe.unbind(De,N),Xe.unbind(Ne,C),Xe.unbind(Ie,M),Pe&&Xe.unbind(Pe,I),se(!1)}function D(e){var t=e,r=V(),i=R(),o=_();return!r||o?t=O:!i||e!=T||n.triggerOnTouchEnd&&!n.triggerOnTouchLeave?!i&&e==E&&n.triggerOnTouchLeave&&(t=O):t=E,t}function F(e,t){var n=void 0;return B()||Y()?n=z(e,t,p):(X()||H())&&!1!==n&&(n=z(e,t,d)),ie()&&!1!==n?n=z(e,t,h):oe()&&!1!==n?n=z(e,t,g):re()&&!1!==n&&(n=z(e,t,f)),t===O&&N(e),t===E&&(A?0==e.touches.length&&N(e):N(e)),n}function z(t,c,s){var v=void 0;if(s==p){if(Xe.trigger("swipeStatus",[c,ze||null,Fe||0,Re||0,Ye,Be]),n.swipeStatus&&!1===(v=n.swipeStatus.call(Xe,t,c,ze||null,Fe||0,Re||0,Ye,Be)))return!1;if(c==E&&Q()){if(Xe.trigger("swipe",[ze,Fe,Re,Ye,Be]),n.swipe&&!1===(v=n.swipe.call(Xe,t,ze,Fe,Re,Ye,Be)))return!1;switch(ze){case r:Xe.trigger("swipeLeft",[ze,Fe,Re,Ye,Be]),n.swipeLeft&&(v=n.swipeLeft.call(Xe,t,ze,Fe,Re,Ye,Be));break;case i:Xe.trigger("swipeRight",[ze,Fe,Re,Ye,Be]),n.swipeRight&&(v=n.swipeRight.call(Xe,t,ze,Fe,Re,Ye,Be));break;case o:Xe.trigger("swipeUp",[ze,Fe,Re,Ye,Be]),n.swipeUp&&(v=n.swipeUp.call(Xe,t,ze,Fe,Re,Ye,Be));break;case a:Xe.trigger("swipeDown",[ze,Fe,Re,Ye,Be]),n.swipeDown&&(v=n.swipeDown.call(Xe,t,ze,Fe,Re,Ye,Be))}}}if(s==d){if(Xe.trigger("pinchStatus",[c,Ue||null,qe||0,Re||0,Ye,Ve,Be]),n.pinchStatus&&!1===(v=n.pinchStatus.call(Xe,t,c,Ue||null,qe||0,Re||0,Ye,Ve,Be)))return!1;if(c==E&&U())switch(Ue){case l:Xe.trigger("pinchIn",[Ue||null,qe||0,Re||0,Ye,Ve,Be]),n.pinchIn&&(v=n.pinchIn.call(Xe,t,Ue||null,qe||0,Re||0,Ye,Ve,Be));break;case u:Xe.trigger("pinchOut",[Ue||null,qe||0,Re||0,Ye,Ve,Be]),n.pinchOut&&(v=n.pinchOut.call(Xe,t,Ue||null,qe||0,Re||0,Ye,Ve,Be))}}return s==f?c!==O&&c!==E||(clearTimeout(Je),clearTimeout(et),K()&&!ee()?($e=Le(),Je=setTimeout(e.proxy(function(){$e=null,Xe.trigger("tap",[t.target]),n.tap&&(v=n.tap.call(Xe,t,t.target))},this),n.doubleTapThreshold)):($e=null,Xe.trigger("tap",[t.target]),n.tap&&(v=n.tap.call(Xe,t,t.target)))):s==h?c!==O&&c!==E||(clearTimeout(Je),$e=null,Xe.trigger("doubletap",[t.target]),n.doubleTap&&(v=n.doubleTap.call(Xe,t,t.target))):s==g&&(c!==O&&c!==E||(clearTimeout(Je),$e=null,Xe.trigger("longtap",[t.target]),n.longTap&&(v=n.longTap.call(Xe,t,t.target)))),v}function R(){var e=!0;return null!==n.threshold&&(e=Fe>=n.threshold),e}function _(){var e=!1;return null!==n.cancelThreshold&&null!==ze&&(e=ve(ze)-Fe>=n.cancelThreshold),e}function j(){return null===n.pinchThreshold||qe>=n.pinchThreshold}function V(){return!n.maxTimeThreshold||!(Re>=n.maxTimeThreshold)}function q(e,t){if(n.allowPageScroll===c||H())e.preventDefault();else{var l=n.allowPageScroll===s;switch(t){case r:(n.swipeLeft&&l||!l&&n.allowPageScroll!=v)&&e.preventDefault();break;case i:(n.swipeRight&&l||!l&&n.allowPageScroll!=v)&&e.preventDefault();break;case o:(n.swipeUp&&l||!l&&n.allowPageScroll!=m)&&e.preventDefault();break;case a:(n.swipeDown&&l||!l&&n.allowPageScroll!=m)&&e.preventDefault()}}}function U(){var e=W(),t=Z(),n=j();return e&&t&&n}function H(){return!!(n.pinchStatus||n.pinchIn||n.pinchOut)}function X(){return!(!U()||!H())}function Q(){var e=V(),t=R(),n=W(),r=Z();return!_()&&r&&n&&t&&e}function Y(){return!!(n.swipe||n.swipeStatus||n.swipeLeft||n.swipeRight||n.swipeUp||n.swipeDown)}function B(){return!(!Q()||!Y())}function W(){return Ye===n.fingers||n.fingers===b||!A}function Z(){return 0!==Be[0].end.x}function G(){return!!n.tap}function K(){return!!n.doubleTap}function $(){return!!n.longTap}function J(){if(null==$e)return!1;var e=Le();return K()&&e-$e<=n.doubleTapThreshold}function ee(){return J()}function te(){return(1===Ye||!A)&&(isNaN(Fe)||Fe<n.threshold)}function ne(){return Re>n.longTapThreshold&&Fe<w}function re(){return!(!te()||!G())}function ie(){return!(!J()||!K())}function oe(){return!(!ne()||!$())}function ae(){Ge=Le(),Ke=event.touches.length+1}function le(){Ge=0,Ke=0}function ue(){var e=!1;if(Ge){Le()-Ge<=n.fingerReleaseThreshold&&(e=!0)}return e}function ce(){return!(!0!==Xe.data(k+"_intouch"))}function se(e){!0===e?(Xe.bind(Ne,C),Xe.bind(Ie,M),Pe&&Xe.bind(Pe,I)):(Xe.unbind(Ne,C,!1),Xe.unbind(Ie,M,!1),Pe&&Xe.unbind(Pe,I,!1)),Xe.data(k+"_intouch",!0===e)}function pe(e,t){var n=void 0!==t.identifier?t.identifier:0;return Be[e].identifier=n,Be[e].start.x=Be[e].end.x=t.pageX||t.clientX,Be[e].start.y=Be[e].end.y=t.pageY||t.clientY,Be[e]}function de(e){var t=void 0!==e.identifier?e.identifier:0,n=fe(t);return n.end.x=e.pageX||e.clientX,n.end.y=e.pageY||e.clientY,n}function fe(e){for(var t=0;t<Be.length;t++)if(Be[t].identifier==e)return Be[t]}function he(){for(var e=[],t=0;t<=5;t++)e.push({start:{x:0,y:0},end:{x:0,y:0},identifier:0});return e}function ge(e,t){t=Math.max(t,ve(e)),He[e].distance=t}function ve(e){if(He[e])return He[e].distance}function me(){var e={};return e[r]=be(r),e[i]=be(i),e[o]=be(o),e[a]=be(a),e}function be(e){return{direction:e,distance:0}}function we(){return Ze-We}function ye(e,t){var n=Math.abs(e.x-t.x),r=Math.abs(e.y-t.y);return Math.round(Math.sqrt(n*n+r*r))}function Te(e,t){return(t/e*1).toFixed(2)}function Ee(){return Ve<1?u:l}function Oe(e,t){return Math.round(Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2)))}function Ae(e,t){var n=e.x-t.x,r=t.y-e.y,i=Math.atan2(r,n),o=Math.round(180*i/Math.PI);return o<0&&(o=360-Math.abs(o)),o}function xe(e,t){var n=Ae(e,t);return n<=45&&n>=0?r:n<=360&&n>=315?r:n>=135&&n<=225?i:n>45&&n<135?a:o}function Le(){return(new Date).getTime()}function ke(t){t=e(t);var n=t.offset();return{left:n.left,right:n.left+t.outerWidth(),top:n.top,bottom:n.top+t.outerHeight()}}function Se(e,t){return e.x>t.left&&e.x<t.right&&e.y>t.top&&e.y<t.bottom}var Ce=A||L||!n.fallbackToMouseEvents,Me=Ce?L?x?"MSPointerDown":"pointerdown":"touchstart":"mousedown",Ne=Ce?L?x?"MSPointerMove":"pointermove":"touchmove":"mousemove",Ie=Ce?L?x?"MSPointerUp":"pointerup":"touchend":"mouseup",Pe=Ce?null:"mouseleave",De=L?x?"MSPointerCancel":"pointercancel":"touchcancel",Fe=0,ze=null,Re=0,_e=0,je=0,Ve=1,qe=0,Ue=0,He=null,Xe=e(t),Qe="start",Ye=0,Be=null,We=0,Ze=0,Ge=0,Ke=0,$e=0,Je=null,et=null;try{Xe.bind(Me,S),Xe.bind(De,N)}catch(t){e.error("events not supported "+Me+","+De+" on jQuery.swipe")}this.enable=function(){return Xe.bind(Me,S),Xe.bind(De,N),Xe},this.disable=function(){return P(),Xe},this.destroy=function(){return P(),Xe.data(k,null),Xe},this.option=function(t,r){if(void 0!==n[t]){if(void 0===r)return n[t];n[t]=r}else e.error("Option "+t+" does not exist on jQuery.swipe.options");return null}}var r="left",i="right",o="up",a="down",l="in",u="out",c="none",s="auto",p="swipe",d="pinch",f="tap",h="doubletap",g="longtap",v="horizontal",m="vertical",b="all",w=10,y="start",T="move",E="end",O="cancel",A="ontouchstart"in window,x=window.navigator.msPointerEnabled&&!window.navigator.pointerEnabled,L=window.navigator.pointerEnabled||window.navigator.msPointerEnabled,k="TouchSwipe",S={fingers:1,threshold:75,cancelThreshold:null,pinchThreshold:20,maxTimeThreshold:null,fingerReleaseThreshold:250,longTapThreshold:500,doubleTapThreshold:200,swipe:null,swipeLeft:null,swipeRight:null,swipeUp:null,swipeDown:null,swipeStatus:null,pinchIn:null,pinchOut:null,pinchStatus:null,click:null,tap:null,doubleTap:null,longTap:null,hold:null,triggerOnTouchEnd:!0,triggerOnTouchLeave:!1,allowPageScroll:"auto",fallbackToMouseEvents:!0,excludedElements:"label, button, input, select, textarea, a, .noSwipe"};e.fn.swipe=function(n){var r=e(this),i=r.data(k);if(i&&"string"==typeof n){if(i[n])return i[n].apply(this,Array.prototype.slice.call(arguments,1));e.error("Method "+n+" does not exist on jQuery.swipe")}else if(!(i||"object"!=typeof n&&n))return t.apply(this,arguments);return r},e.fn.swipe.defaults=S,e.fn.swipe.phases={PHASE_START:y,PHASE_MOVE:T,PHASE_END:E,PHASE_CANCEL:O},e.fn.swipe.directions={LEFT:r,RIGHT:i,UP:o,DOWN:a,IN:l,OUT:u},e.fn.swipe.pageScroll={NONE:c,HORIZONTAL:v,VERTICAL:m,AUTO:s},e.fn.swipe.fingers={ONE:1,TWO:2,THREE:3,ALL:b}}),function(e){(jQuery.browser=jQuery.browser||{}).mobile=/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(e)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(e.substr(0,4))}(navigator.userAgent||navigator.vendor||window.opera),function(e){var t={init:function(){var t=["paddingTop","paddingRight","paddingBottom","paddingLeft","fontSize","lineHeight","fontFamily","width","fontWeight","border-top-width","border-right-width","border-bottom-width","border-left-width","-moz-box-sizing","-webkit-box-sizing","box-sizing"];return this.each(function(){function n(){for(var e=0;e<t.length;e++)a.css(t[e],o.css(t[e]))}function r(){var e=o.val().replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/\n/g,"<br/>");a.html(e+" ").css({width:parseInt(o.width(),10)+"px"}),i()}function i(){var e=a.height(),t="hidden",n=l?e+s+u:e+s;n>d?(n=d,t="auto"):p>n&&(n=p),o.height()!==n&&o.css({overflow:t,height:n+"px"})}if("textarea"!==this.type)return!1;var o=e(this).css({resize:"none",overflow:"hidden"}),a=e("<div></div>").css({position:"absolute",display:"none","word-wrap":"break-word","white-space":"pre-wrap","border-style":"solid"}).appendTo(document.body);n();var l="border-box"==o.css("box-sizing")||"border-box"==o.css("-moz-box-sizing")||"border-box"==o.css("-webkit-box-sizing"),u=parseInt(o.css("border-top-width"))+parseInt(o.css("padding-top"))+parseInt(o.css("padding-bottom"))+parseInt(o.css("border-bottom-width")),c=parseInt(o.css("height"),10),s=parseInt(o.css("line-height"),10)||parseInt(o.css("font-size"),10),p=2*s>c?2*s:c,d=parseInt(o.css("max-height"),10)>-1?parseInt(o.css("max-height"),10):Number.MAX_VALUE;o.bind("keyup change cut paste",function(){r()}),e(window).bind("resize",function(){a.width()!==parseInt(o.width(),10)&&r()}),o.bind("blur",function(){i()}),o.bind("updateHeight",function(){n(),r()}),e(function(){r()})})}};e.fn.flexible=function(n){return t[n]?t[n].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof n&&n?void e.error("Method "+n+" does not exist on jQuery.flexible"):t.init.apply(this,arguments)}}(jQuery),function(e,t,n,r){"use strict";function i(e,t){for(var n=0,r=e.length;n<r;n++)g(e[n],t)}function o(e){for(var t,n=0,r=e.length;n<r;n++)t=e[n],E(t,_[l(t)])}function a(e){return function(t){ne(t)&&(g(t,e),i(t.querySelectorAll(j),e))}}function l(e){var t=e.getAttribute("is"),n=e.nodeName.toUpperCase(),r=q.call(R,t?D+t.toUpperCase():P+n);return t&&-1<r&&!u(n,t)?-1:r}function u(e,t){return-1<j.indexOf(e+'[is="'+t+'"]')}function c(e){var t=e.currentTarget,n=e.attrChange,r=e.attrName,i=e.target;he&&(!i||i===t)&&t.attributeChangedCallback&&"style"!==r&&t.attributeChangedCallback(r,n===e[k]?null:e.prevValue,n===e[C]?null:e.newValue)}function s(e){var t=a(e);return function(e){v.push(t,e.target)}}function p(e){fe&&(fe=!1,e.currentTarget.removeEventListener(N,p)),i((e.target||t).querySelectorAll(j),e.detail===x?x:A),te&&h()}function d(e,t){var n=this;oe.call(n,e,t),m.call(n,{target:n})}function f(e,t){$(e,t),y?y.observe(e,ue):(de&&(e.setAttribute=d,e[O]=w(e),e.addEventListener(I,m)),e.addEventListener(M,c)),e.createdCallback&&he&&(e.created=!0,e.createdCallback(),e.created=!1)}function h(){for(var e,t=0,n=re.length;t<n;t++)e=re[t],V.contains(e)||(re.splice(t,1),g(e,x))}function g(e,t){var n,r=l(e);-1<r&&(T(e,_[r]),r=0,t!==A||e[A]?t===x&&!e[x]&&(e[A]=!1,e[x]=!0,r=1):(e[x]=!1,e[A]=!0,r=1,te&&q.call(re,e)<0&&re.push(e)),r&&(n=e[t+"Callback"])&&n.call(e))}if(!(r in t)){var v,m,b,w,y,T,E,O="__"+r+(1e5*Math.random()>>0),A="attached",x="detached",L="extends",k="ADDITION",S="MODIFICATION",C="REMOVAL",M="DOMAttrModified",N="DOMContentLoaded",I="DOMSubtreeModified",P="<",D="=",F=/^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/,z=["ANNOTATION-XML","COLOR-PROFILE","FONT-FACE","FONT-FACE-SRC","FONT-FACE-URI","FONT-FACE-FORMAT","FONT-FACE-NAME","MISSING-GLYPH"],R=[],_=[],j="",V=t.documentElement,q=R.indexOf||function(e){for(var t=this.length;t--&&this[t]!==e;);return t},U=n.prototype,H=U.hasOwnProperty,X=U.isPrototypeOf,Q=n.defineProperty,Y=n.getOwnPropertyDescriptor,B=n.getOwnPropertyNames,W=n.getPrototypeOf,Z=n.setPrototypeOf,G=!!n.__proto__,K=n.create||function e(t){return t?(e.prototype=t,new e):this},$=Z||(G?function(e,t){return e.__proto__=t,e}:B&&Y?function(){function e(e,t){for(var n,r=B(t),i=0,o=r.length;i<o;i++)n=r[i],H.call(e,n)||Q(e,n,Y(t,n))}return function(t,n){do{e(t,n)}while((n=W(n))&&!X.call(n,t));return t}}():function(e,t){for(var n in t)e[n]=t[n];return e}),J=e.MutationObserver||e.WebKitMutationObserver,ee=(e.HTMLElement||e.Element||e.Node).prototype,te=!X.call(ee,V),ne=te?function(e){return 1===e.nodeType}:function(e){return X.call(ee,e)},re=te&&[],ie=ee.cloneNode,oe=ee.setAttribute,ae=ee.removeAttribute,le=t.createElement,ue=J&&{attributes:!0,characterData:!0,attributeOldValue:!0},ce=J||function(e){de=!1,V.removeEventListener(M,ce)},se=e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.msRequestAnimationFrame||function(e){setTimeout(e,10)},pe=!1,de=!0,fe=!0,he=!0;Z||G?(T=function(e,t){X.call(t,e)||f(e,t)},E=f):(T=function(e,t){e[O]||(e[O]=n(!0),f(e,t))},E=T),te?(de=!1,function(){var e=Y(ee,"addEventListener"),t=e.value,n=function(e){var t=new CustomEvent(M,{bubbles:!0});t.attrName=e,t.prevValue=this.getAttribute(e),t.newValue=null,t[C]=t.attrChange=2,ae.call(this,e),this.dispatchEvent(t)},r=function(e,t){var n=this.hasAttribute(e),r=n&&this.getAttribute(e),i=new CustomEvent(M,{bubbles:!0});oe.call(this,e,t),i.attrName=e,i.prevValue=n?r:null,i.newValue=t,n?i[S]=i.attrChange=1:i[k]=i.attrChange=0,this.dispatchEvent(i)},i=function(e){var t,n=e.currentTarget,r=n[O],i=e.propertyName;r.hasOwnProperty(i)&&(r=r[i],t=new CustomEvent(M,{bubbles:!0}),t.attrName=r.name,t.prevValue=r.value||null,t.newValue=r.value=n[i]||null,null==t.prevValue?t[k]=t.attrChange=0:t[S]=t.attrChange=1,n.dispatchEvent(t))};e.value=function(e,o,a){e===M&&this.attributeChangedCallback&&this.setAttribute!==r&&(this[O]={className:{name:"class",value:this.className}},this.setAttribute=r,this.removeAttribute=n,t.call(this,"propertychange",i)),t.call(this,e,o,a)},Q(ee,"addEventListener",e)}()):J||(V.addEventListener(M,ce),V.setAttribute(O,1),V.removeAttribute(O),de&&(m=function(e){var t,n,r,i=this;if(i===e.target){t=i[O],i[O]=n=w(i);for(r in n){if(!(r in t))return b(0,i,r,t[r],n[r],k);if(n[r]!==t[r])return b(1,i,r,t[r],n[r],S)}for(r in t)if(!(r in n))return b(2,i,r,t[r],n[r],C)}},b=function(e,t,n,r,i,o){var a={attrChange:e,currentTarget:t,attrName:n,prevValue:r,newValue:i};a[o]=e,c(a)},w=function(e){for(var t,n,r={},i=e.attributes,o=0,a=i.length;o<a;o++)t=i[o],"setAttribute"!==(n=t.name)&&(r[n]=t.value);return r})),t[r]=function(e,n){if(r=e.toUpperCase(),pe||(pe=!0,J?(y=function(e,t){function n(e,t){for(var n=0,r=e.length;n<r;t(e[n++]));}return new J(function(r){for(var i,o,a=0,l=r.length;a<l;a++)i=r[a],"childList"===i.type?(n(i.addedNodes,e),n(i.removedNodes,t)):(o=i.target,he&&o.attributeChangedCallback&&"style"!==i.attributeName&&o.attributeChangedCallback(i.attributeName,i.oldValue,o.getAttribute(i.attributeName)))})}(a(A),a(x)),y.observe(t,{childList:!0,subtree:!0})):(v=[],se(function e(){for(;v.length;)v.shift().call(null,v.shift());se(e)}),t.addEventListener("DOMNodeInserted",s(A)),t.addEventListener("DOMNodeRemoved",s(x))),t.addEventListener(N,p),t.addEventListener("readystatechange",p),t.createElement=function(e,n){var r=le.apply(t,arguments),i=""+e,o=q.call(R,(n?D:P)+(n||i).toUpperCase()),a=-1<o;return n&&(r.setAttribute("is",n=n.toLowerCase()),a&&(a=u(i.toUpperCase(),n))),he=!t.createElement.innerHTMLHelper,a&&E(r,_[o]),r},ee.cloneNode=function(e){var t=ie.call(this,!!e),n=l(t);return-1<n&&E(t,_[n]),e&&o(t.querySelectorAll(j)),t}),-2<q.call(R,D+r)+q.call(R,P+r))throw new Error("A "+e+" type is already registered");if(!F.test(r)||-1<q.call(z,r))throw new Error("The type "+e+" is invalid");var r,c=function(){return f?t.createElement(h,r):t.createElement(h)},d=n||U,f=H.call(d,L),h=f?n[L].toUpperCase():r,g=R.push((f?D:P)+r)-1;return j=j.concat(j.length?",":"",f?h+'[is="'+e.toLowerCase()+'"]':h),c.prototype=_[g]=H.call(d,"prototype")?d.prototype:K(ee),i(t.querySelectorAll(j),A),c}}}(window,document,Object,"registerElement"),function(e,t,n){"use strict";function r(){return e.performance!==n&&e.performance.now!==n?e.performance.now():Date.now()}function i(e){return.5*(1-Math.cos(Math.PI*e))}function o(e){if("object"!=typeof e||e.behavior===n||"auto"===e.behavior||"instant"===e.behavior)return!0;if("smooth"===e.behavior)return!1;throw new TypeError(e.behavior+" is not a valid value for enumeration ScrollBehavior")}function a(e,t,n){e.scrollTop=n,e.scrollLeft=t}function l(t,o){function a(){var f,h,g,v=r(),m=(v-d)/s;return m=m>1?1:m,f=i(m),h=l+(t-l)*f,g=u+(o-u)*f,p(h,g),h===t&&g===o?(l=u=d=null,e.cancelAnimationFrame(c),n):(c=e.requestAnimationFrame(a),n)}var l=e.scrollX||e.pageXOffset,u=e.scrollY||e.pageYOffset,d=r();c&&e.cancelAnimationFrame(c),c=e.requestAnimationFrame(a)}function u(o,u){function p(){var t,l,u,m=r(),b=(m-v)/s;return b=b>1?1:b,t=i(b),l=d+(h-d)*t,u=f+(g-f)*t,a(o,l,u),l===h&&u===g?(d=f=v=null,e.cancelAnimationFrame(c),n):(c=e.requestAnimationFrame(p),n)}if(o===t.documentElement||o===t.body)return l(u.left,u.top),n;var d=o.scrollLeft,f=o.scrollTop,h=u.left,g=u.top,v=r();c&&e.cancelAnimationFrame(c),c=e.requestAnimationFrame(p)}if(!("scrollBehavior"in t.documentElement.style)){var c,s=768,p=e.scrollTo,d=e.scrollBy,f=e.Element.prototype.scrollIntoView;e.scroll=e.scrollTo=function(){return o(arguments[0])?p.call(e,arguments[0].left||arguments[0],arguments[0].top||arguments[1]):l.call(e,~~arguments[0].left,~~arguments[0].top)},e.scrollBy=function(){if(o(arguments[0]))return d.call(e,arguments[0].left||arguments[0],arguments[0].top||arguments[1]);var t=e.scrollX||e.pageXOffset,n=e.scrollY||e.pageYOffset;return l(~~arguments[0].left+t,~~arguments[0].top+n)},Element.prototype.scrollIntoView=function(){var n,r,i,a;return o(arguments[0])?f.call(this,arguments[0]||!0):(a=e.getComputedStyle(t.body,null),r=parseInt(a.getPropertyValue("padding-left"),10),i=parseInt(a.getPropertyValue("padding-top"),10),n={top:this.offsetTop-2*i,left:this.offsetLeft-2*r},u(t.body,n))}}}(window,document); })(this);
// WCF.js
-(function (window, undefined) { "use strict";function wcfEval(expression){return eval(expression)}!function(){var e=jQuery.fn.data;jQuery.fn.data=function(t,i){var n=[].slice.call(arguments);if(t)switch(typeof t){case"object":for(var s in t)if(s.match(/ID$/)){var a=t[s];delete t[s],s=s.replace(/ID$/,"-id"),t[s]=a}n[0]=t;break;case"string":t.match(/ID$/)&&(n[0]=t.replace(/ID$/,"-id"))}var o=e.apply(this,n);if(void 0===t)for(var s in o)s.match(/Id$/)&&(o[s.replace(/Id$/,"ID")]=o[s],delete o[s]);return o},window.console||(window.console={});for(var t=["log","info","warn","exception","assert","dir","dirxml","trace","group","groupEnd","groupCollapsed","profile","profileEnd","count","clear","time","timeEnd","timeStamp","table","error"],i=0;i<t.length;i++)void 0===console[t[i]]&&(console[t[i]]=function(){});void 0===console.debug&&(console.debug=function(e){console.log(e)})}(),window.shuffle=function(e){for(var t,i,n=e.length;0!==n;)i=Math.floor(Math.random()*n),n-=1,t=e[n],e[n]=e[i],e[i]=t;return this},function(e){var t=navigator.userAgent.toLowerCase(),i=/(chrome)[ \/]([\w.]+)/.exec(t)||/(webkit)[ \/]([\w.]+)/.exec(t)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(t)||/(msie) ([\w.]+)/.exec(t)||t.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(t)||[],n={browser:i[1]||"",version:i[2]||"0"},s={};n.browser&&(s[n.browser]=!0,s.version=n.version),s.chrome?s.webkit=!0:s.webkit&&(s.safari=!0),e.browser=e.browser||{},e.browser=$.extend(e.browser,s),e.browser.touch=!!("ontouchstart"in window)||!!("msMaxTouchPoints"in window.navigator)&&window.navigator.msMaxTouchPoints>0,e.browser.smartphone="bottom"==$("html").css("caption-side"),e.browser.mozilla&&t.match(/trident/)&&(e.browser.mozilla=!1,e.browser.msie=!0),e.browser.iOS=/\((ipad|iphone|ipod);/.test(t),e.browser.iOS&&$("html").addClass("iOS"),e.browser.android=-1!==t.indexOf("android"),e.browser.editor="redactor",e.browser.ckeditor=!1,e.browser.redactor=!0,e.browser.iOS&&(e.fn.focus=function(e,t){return arguments.length>0?this.on("focus",null,e,t):this.trigger("focus")})}(jQuery),null==window.WCF&&(window.WCF={}),$.extend(!0,{removeArrayValue:function(e,t){return $.grep(e,function(e,i){return t!==e})},wcfEscapeID:function(e){return e.replace(/(:|\.)/g,"\\$1")},wcfIsset:function(e){return!!$("#"+$.wcfEscapeID(e)).length},getLength:function(e){var t=0;for(var i in e)e.hasOwnProperty(i)&&t++;return t}}),$.fn.extend({getTagName:function(){return this.length?this.get(0).tagName.toLowerCase():""},getDimensions:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"inner":i={height:this.innerHeight(),width:this.innerWidth()};break;case"outer":i={height:this.outerHeight(),width:this.outerWidth()};break;default:i={height:this.height(),width:this.width()}}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},getOffsets:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"offset":i=this.offset();break;case"position":default:i=this.position()}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},makePositioned:function(e,t){"absolute"!=e&&"fixed"!=e&&(e="absolute");var i=this.getOffsets("position");return this.css({position:e,left:i.left,margin:0,top:i.top}),t&&this.remove().appentTo("body"),this},disable:function(){return this.attr("disabled","disabled")},enable:function(){return this.removeAttr("disabled")},wcfIdentify:function(){return window.bc_wcfDomUtil.identify(this[0])},getCaret:function(){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return-1}else if(!this.is("textarea"))return-1;var e=0,t=this.get(0);if(document.selection){this.focus();var i=document.selection.createRange();i.moveStart("character",-this.val().length),e=i.text.length}else(t.selectionStart||"0"==t.selectionStart)&&(e=parseInt(t.selectionStart));return e},setCaret:function(e){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return!1}else if(!this.is("textarea"))return!1;var t=this.get(0);if(this.focus(),document.selection){var i=document.selection.createRange();i.moveStart("character",e),i.moveEnd("character",0),i.select()}else(t.selectionStart||"0"==t.selectionStart)&&(t.selectionStart=e,t.selectionEnd=e);return!0},wcfDropIn:function(e,t,i){return e||(e="up"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfDropOut:function(e,t,i){return e||(e="down"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfBlindIn:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfBlindOut:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfHighlight:function(e,t){return this.effect("highlight",e,600,t)},wcfFadeIn:function(e,t){return t&&parseInt(t)||(t=200),this.show(WCF.getEffect(this,"fade"),{},t,e)},wcfFadeOut:function(e,t){return t&&parseInt(t)||(t=200),this.hide(WCF.getEffect(this,"fade"),{},t,e)},cssAsNumber:function(e){if(this.length){var t=this.css(e);if(void 0!==t)return parseInt(t.replace(/px$/,""))}return 0},perfectScrollbar:function(e){var t=require("perfect-scrollbar");return this.each(function(){if("object"==typeof e||void 0===e){var i=e;$(this).data("psID")||t.initialize(this,i)}else{var n=e;"update"===n?t.update(this):"destroy"===n&&t.destroy(this)}return jQuery(this)})}}),$.extend(WCF,{activeDialogs:0,_idCounter:0,getRandomID:function(){return window.bc_wcfDomUtil.getUniqueId()},inArray:function(e,t){return-1!=$.inArray(e,t)},getEffect:function(e,t){return e.is("tr")?"highlight":t},getInlineCSS:function(e){var t={},i=e.attr("style");if(!i)return{};i=i.split(";");for(var n=0,s=i.length;n<s;n++){var a=$.trim(i[n]);""!=a&&(a=a.split(":"),t[$.trim(a[0])]=$.trim(a[1]))}return t},revertInlineCSS:function(e,t,i){for(var n=0,s=i.length;n<s;n++){var a=i[n];t[a]?e.css(a,t[a]):e.css(a,"")}},getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)})},base64toBlob:function(e,t,i){t=t||"",i=i||512;for(var n=atob(e),s=[],a=0;a<n.length;a+=i){for(var o=n.slice(a,a+i),r=new Array(o.length),l=0;l<o.length;l++)r[l]=o.charCodeAt(l);var c=new Uint8Array(r);s.push(c)}return new Blob(s,{type:t})},convertLegacyURL:function(e){return e.replace(/^index\.php\/(.*?)\/\?/,function(e,t){for(var i=t.split(/([A-Z][a-z0-9]+)/),n="",s=0,a=i.length;s<a;s++){var o=i[s].trim();o.length&&(n.length&&(n+="-"),n+=o.toLowerCase())}return"index.php?"+n+"/&"})}}),WCF.Browser={_isChrome:null,isChrome:function(){return null===this._isChrome&&(this._isChrome=!1,/chrom(e|ium)/.test(navigator.userAgent.toLowerCase())&&(this._isChrome=!0)),this._isChrome}},WCF.Dropdown={init:function(e){window.bc_wcfSimpleDropdown.initAll()},initDropdown:function(e,t){window.bc_wcfSimpleDropdown.init(e[0],t)},removeDropdown:function(e){window.bc_wcfSimpleDropdown.destroy(e)},initDropdownFragment:function(e,t){window.bc_wcfSimpleDropdown.initFragment(e[0],t[0])},registerCallback:function(e,t){window.bc_wcfSimpleDropdown.registerCallback(e,t)},_toggle:function(e,t){window.bc_wcfSimpleDropdown._toggle(e,t)},toggleDropdown:function(e){window.bc_wcfSimpleDropdown._toggle(null,e)},getDropdown:function(e){var t=window.bc_wcfSimpleDropdown.getDropdown(e);return t?$(t):null},getDropdownMenu:function(e){var t=window.bc_wcfSimpleDropdown.getDropdownMenu(e);return t?$(t):null},setAlignmentByID:function(e){window.bc_wcfSimpleDropdown.setAlignmentById(e)},setAlignment:function(e,t){window.bc_wcfSimpleDropdown.setAlignment(e[0],t[0])},_closeAll:function(){window.bc_wcfSimpleDropdown.closeAll()},close:function(e){window.bc_wcfSimpleDropdown.close(e)},destroy:function(e){window.bc_wcfSimpleDropdown.destroy(e)}},WCF.Dropdown.Interactive={},WCF.Dropdown.Interactive.Handler={_dropdownContainer:null,_dropdownMenus:{},create:function(e,t,i){null===this._dropdownContainer&&(this._dropdownContainer=$('<div class="dropdownMenuContainer" />').appendTo(document.body),WCF.CloseOverlayHandler.addCallback("WCF.Dropdown.Interactive.Handler",$.proxy(this.closeAll,this)));var n=new WCF.Dropdown.Interactive.Instance(this._dropdownContainer,e,t,i);return this._dropdownMenus[t]=n,n},open:function(e){return!!this._dropdownMenus[e]&&(this._dropdownMenus[e].open(),!0)},close:function(e){return!!this._dropdownMenus[e]&&(this._dropdownMenus[e].close(),!0)},closeAll:function(){for(var e in this._dropdownMenus)this._dropdownMenus.hasOwnProperty(e)&&this._dropdownMenus[e].close()},getOpenDropdown:function(){for(var e in this._dropdownMenus)if(this._dropdownMenus.hasOwnProperty(e)&&this._dropdownMenus[e].isOpen())return this._dropdownMenus[e];return null},getDropdown:function(e){return this._dropdownMenus[e]}},WCF.Dropdown.Interactive.Instance=Class.extend({_container:null,_itemList:null,_linkList:null,_options:{},_pointer:null,_triggerElement:null,init:function(e,t,i,n){this._options=n||{},this._triggerElement=t;var s=null;if(!0===n.staticDropdown)this._container=this._triggerElement.find(".interactiveDropdownStatic:eq(0)").data("source",i).click(function(e){e.stopPropagation()});else{this._container=$('<div class="interactiveDropdown" data-source="'+i+'" />').click(function(e){e.stopPropagation()});var a=$('<div class="interactiveDropdownHeader" />').appendTo(this._container);$('<span class="interactiveDropdownTitle">'+n.title+"</span>").appendTo(a),this._linkList=$('<ul class="interactiveDropdownLinks inlineList"></ul>').appendTo(a),s=$('<div class="interactiveDropdownItemsContainer" />').appendTo(this._container),this._itemList=$('<ul class="interactiveDropdownItems" />').appendTo(s),$('<a href="'+n.showAllLink+'" class="interactiveDropdownShowAll">'+WCF.Language.get("wcf.user.panel.showAll")+"</a>").appendTo(this._container)}this._pointer=$('<span class="elementPointer"><span /></span>').appendTo(this._container),require(["Environment"],function(e){"desktop"===e.platform()&&null!==s&&s.perfectScrollbar({suppressScrollX:!0})}.bind(this)),this._container.appendTo(e)},getContainer:function(){return this._container},getItemList:function(){return this._itemList},getLinkList:function(){return this._linkList},open:function(){WCF.Dropdown._closeAll(),this._triggerElement.addClass("open"),this._container.addClass("open"),WCF.System.Event.fireEvent("com.woltlab.wcf.Search","close"),this.render()},close:function(){this._triggerElement.removeClass("open"),this._container.removeClass("open")},isOpen:function(){return this._triggerElement.hasClass("open")},toggle:function(){return this._container.hasClass("open")?(this.close(),!1):(WCF.Dropdown.Interactive.Handler.closeAll(),this.open(),!0)},resetItems:function(){this._itemList.empty(),this.close()},render:function(){require(["Ui/Alignment","Ui/Screen"],function(e,t){t.is("screen-lg")?e.set(this._container[0],this._triggerElement[0],{horizontal:"right",pointer:!0}):this._container.css({bottom:"",left:"",right:"",top:elById("pageHeaderPanel").clientHeight+"px"})}.bind(this))},rebuildScrollbar:function(){require(["Environment"],function(e){if("desktop"===e.platform()){var t=this._itemList.parent();t.perfectScrollbar("destroy"),t.perfectScrollbar({suppressScrollX:!0})}}.bind(this))}}),WCF.Clipboard={init:function(e,t,i,n){require(["EventHandler","WoltLabSuite/Core/Controller/Clipboard"],function(s,a){a.setup({hasMarkedItems:t>0,pageClassName:e,pageObjectId:n});for(var o in i)i.hasOwnProperty(o)&&function(e){s.add("com.woltlab.wcf.clipboard",e,function(t){null!==t.responseData&&i[e].hasOwnProperty(t.responseData.actionName)&&i[e][t.responseData.actionName].triggerEffect(t.responseData.objectIDs)})}(o)})},reload:function(){require(["WoltLabSuite/Core/Controller/Clipboard"],function(e){e.reload()})}},WCF.PeriodicalExecuter=Class.extend({_callback:null,_delay:0,_intervalID:null,_isExecuting:!1,init:function(e,t){if(!$.isFunction(e))return void console.debug("[WCF.PeriodicalExecuter] Given callback is invalid, aborting.");this._callback=e,this._interval=t,this.resume()},_execute:function(){if(!this._isExecuting)try{this._isExecuting=!0,this._callback(this),this._isExecuting=!1}catch(e){throw this._isExecuting=!1,e}},stop:function(){this._intervalID&&clearInterval(this._intervalID)},resume:function(){this.restart()},restart:function(){this._intervalID&&this.stop(),this._intervalID=setInterval($.proxy(this._execute,this),this._interval)},setInterval:function(e){this._interval=e,this.restart()}}),WCF.LoadingOverlayHandler={show:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.show()})},hide:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.hide()})},updateIcon:function(e,t){var i=void 0===t||t?"addClass":"removeClass";e.find(".icon")[i]("fa-spinner"),e.hasClass("icon")&&e[i]("fa-spinner")}},WCF.Action={},WCF.Action.Proxy=Class.extend({_ajaxRequest:null,init:function(e){this._ajaxRequest=null,e=$.extend(!0,{autoSend:!1,data:{},dataType:"json",after:null,init:null,jsonp:"callback",async:!0,failure:null,showLoadingOverlay:!0,success:null,suppressErrors:!1,type:"POST",url:"index.php?ajax-proxy/&t="+SECURITY_TOKEN,aborted:null,autoAbortPrevious:!1},e),"jsonp"===e.dataType?require(["AjaxJsonp"],function(t){t.send(e.url,e.success,e.failure,{parameterName:e.jsonp})}):require(["AjaxRequest"],function(t){this._ajaxRequest=new t({data:e.data,type:e.type,url:e.url,withCredentials:e.url==="index.php?ajax-proxy/&t="+SECURITY_TOKEN,responseType:"json"===e.dataType?"application/json":"",autoAbort:e.autoAbortPrevious,ignoreError:e.suppressErrors,silent:!e.showLoadingOverlay,failure:e.failure,finalize:e.after,success:e.success}),e.autoSend&&this._ajaxRequest.sendRequest()}.bind(this))},sendRequest:function(e){require(["AjaxRequest"],function(t){null!==this._ajaxRequest&&this._ajaxRequest.sendRequest(e)}.bind(this))},abortPrevious:function(){require(["AjaxRequest"],function(e){null!==this._ajaxRequest&&this._ajaxRequest.abortPrevious()}.bind(this))},setOption:function(e,t){require(["AjaxRequest"],function(i){null!==this._ajaxRequest&&this._ajaxRequest.setOption(e,t)}.bind(this))},showLoadingOverlayOnce:function(){},suppressErrors:function(){},_failure:function(e,t,i){},_success:function(e,t,i){},_after:function(){}}),WCF.Action.SimpleProxy=Class.extend({init:function(e,t){this.options=$.extend(!0,{action:"",className:"",elements:null,eventName:"click"},e),this.callbacks=$.extend(!0,{after:null,failure:null,init:null,success:null},t),this.options.elements&&(this.proxy=new WCF.Action.Proxy(this.callbacks),this.options.elements.each($.proxy(function(e,t){$(t).bind(this.options.eventName,$.proxy(this._handleEvent,this))},this)))},_handleEvent:function(e){this.proxy.setOption("data",{actionName:this.options.action,className:this.options.className,objectIDs:[$(e.target).data("objectID")]}),this.proxy.sendRequest()}}),WCF.Action.Delete=Class.extend({_buttonSelector:"",_callback:null,_className:"",_containerSelector:"",_containers:[],init:function(e,t,i){this._containerSelector=t,this._className=e,this._buttonSelector=i||".jsDeleteButton",this._callback=null,this.proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initElements(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Action.Delete"+this._className.hashCode(),$.proxy(this._initElements,this))},_initElements:function(){$(this._containerSelector).each(function(e,t){var i=$(t),n=i.wcfIdentify();if(!WCF.inArray(n,this._containers)){var s=i.find(this._buttonSelector);s.length&&(this._containers.push(n),s.click($.proxy(this._click,this)))}}.bind(this))},_click:function(e){var t=$(e.currentTarget);e.preventDefault(),t.data("confirmMessageHtml")||t.data("confirmMessage")?WCF.System.Confirmation.show(t.data("confirmMessageHtml")?t.data("confirmMessageHtml"):t.data("confirmMessage"),$.proxy(this._execute,this),{target:t},void 0,!!t.data("confirmMessageHtml")):(WCF.LoadingOverlayHandler.updateIcon(t),this._sendRequest(t))},_didTriggerEffect:function(e){},_execute:function(e,t){"cancel"!==e&&(WCF.LoadingOverlayHandler.updateIcon(t.target),this._sendRequest(t.target))},_sendRequest:function(e){this.proxy.setOption("data",{actionName:"delete",className:this._className,interfaceName:"wcf\\data\\IDeleteAction",objectIDs:[$(e).data("objectID")]}),this.proxy.sendRequest()},_success:function(e,t,i){this._callback&&this._callback(e.objectIDs),this.triggerEffect(e.objectIDs)},setCallback:function(e){if("function"!=typeof e)throw new TypeError("[WCF.Action.Delete] Expected a valid callback for '"+this._className+"'.");this._callback=e},triggerEffect:function(e){for(var t in this._containers){var i=$("#"+this._containers[t]),n=i.find(this._buttonSelector);if(WCF.inArray(n.data("objectID"),e)){var s=this;i.wcfBlindOut("up",function(){var e=$(this).remove();s._containers.splice(s._containers.indexOf(e.wcfIdentify()),1),s._didTriggerEffect(e),n.data("eventName")&&WCF.System.Event.fireEvent("com.woltlab.wcf.action.delete",n.data("eventName"),{button:n,container:e})})}}}}),WCF.Action.NestedDelete=WCF.Action.Delete.extend({triggerEffect:function(e){for(var t in this._containers){var i=$("#"+this._containers[t]);if(WCF.inArray(i.find(this._buttonSelector).data("objectID"),e))if(i.has("ol").has("li").length)i.is(":only-child")?i.parent().replaceWith(i.find("> ol")):i.replaceWith(i.find("> ol > li")),this._containers.splice(this._containers.indexOf(i.wcfIdentify()),1),this._didTriggerEffect(i);else{var n=this;i.wcfBlindOut("up",function(){$(this).remove(),n._containers.splice(n._containers.indexOf($(this).wcfIdentify()),1),n._didTriggerEffect($(this))})}}}}),WCF.Action.Toggle=Class.extend({_buttonSelector:".jsToggleButton",_className:"",_containerSelector:"",_containers:[],init:function(e,t,i){this._containerSelector=t,this._className=e,this._buttonSelector=i||".jsToggleButton",this._containers=[];var n={success:$.proxy(this._success,this)};this.proxy=new WCF.Action.Proxy(n),this._initElements(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Action.Toggle"+this._className.hashCode(),$.proxy(this._initElements,this))},_initElements:function(){$(this._containerSelector).each($.proxy(function(e,t){var i=$(t),n=i.wcfIdentify();WCF.inArray(n,this._containers)||(this._containers.push(n),i.find(this._buttonSelector).click($.proxy(this._click,this)))},this))},_click:function(e){var t=$(e.currentTarget);e.preventDefault(),t.data("confirmMessageHtml")||t.data("confirmMessage")?WCF.System.Confirmation.show(t.data("confirmMessageHtml")?t.data("confirmMessageHtml"):t.data("confirmMessage"),$.proxy(this._execute,this),{target:t},void 0,!!t.data("confirmMessageHtml")):(WCF.LoadingOverlayHandler.updateIcon(t),this._sendRequest(t))},_execute:function(e,t){"cancel"!==e&&(WCF.LoadingOverlayHandler.updateIcon(t.target),this._sendRequest(t.target))},_sendRequest:function(e){this.proxy.setOption("data",{actionName:"toggle",className:this._className,interfaceName:"wcf\\data\\IToggleAction",objectIDs:[$(e).data("objectID")]}),this.proxy.sendRequest()},_success:function(e,t,i){this.triggerEffect(e.objectIDs)},triggerEffect:function(e){for(var t in this._containers){var i=$("#"+this._containers[t]),n=i.find(this._buttonSelector);WCF.inArray(n.data("objectID"),e)&&(i.wcfHighlight(),this._toggleButton(i,n))}},_toggleButton:function(e,t){var i="";WCF.LoadingOverlayHandler.updateIcon(t,!1),t.hasClass("fa-square-o")?(t.removeClass("fa-square-o").addClass("fa-check-square-o"),i=t.data("disableTitle")?t.data("disableTitle"):WCF.Language.get("wcf.global.button.disable"),t.attr("title",i)):(t.removeClass("fa-check-square-o").addClass("fa-square-o"),i=t.data("enableTitle")?t.data("enableTitle"):WCF.Language.get("wcf.global.button.enable"),t.attr("title",i)),e.toggleClass("disabled")}}),WCF.Action.Scroll=Class.extend({_callback:null,_reference:null,_target:null,_threshold:0,init:function(e,t,i,n){return this._threshold=parseInt(e),0===this._threshold?void console.debug("[WCF.Action.Scroll] Given threshold is invalid, aborting."):($.isFunction(t)&&(this._callback=t),null===this._callback?void console.debug("[WCF.Action.Scroll] Given callback is invalid, aborting."):(this._reference=$(i||window),this._target=$(n||document),this.start(),void this._scroll()))},_scroll:function(){var e=this._target.height(),t=this._reference.scrollTop();e-(this._reference.height()+t)<this._threshold&&this._callback(this)},start:function(){this._reference.on("scroll",$.proxy(this._scroll,this))},stop:function(){this._reference.off("scroll")}}),WCF.Date={},WCF.Date.Picker={init:function(){}},WCF.Date.Util={gmdate:function(e){var t=e||new Date;return Math.round(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDay(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds())/1e3)},getTimezoneDate:function(e,t){var i=new Date(e),n=6e4*i.getTimezoneOffset();return new Date(e+n+t)}},WCF.Dictionary=Class.extend({_variables:{},init:function(){this._variables={}},add:function(e,t){this._variables[e]=t},addObject:function(e){for(var t in e)this.add(t,e[t])},addDictionary:function(e){e.each($.proxy(function(e){this.add(e.key,e.value)},this))},get:function(e){return this.isset(e)?this._variables[e]:null},isset:function(e){return this._variables.hasOwnProperty(e)},remove:function(e){delete this._variables[e]},each:function(e){if($.isFunction(e))for(var t in this._variables){var i=this._variables[t],n={key:t,value:i};e(n)}},count:function(){return $.getLength(this._variables)},isEmpty:function(){return!this.count()}}),null==window.WCF.Language&&(WCF.Language={add:function(e,t){require(["Language"],function(i){i.add(e,t)})},addObject:function(e){require(["Language"],function(t){t.addObject(e)})},get:function(e,t){throw new Error('Call to deprecated WCF.Language.get("'+e+'")')}}),WCF.Number={round:function(e,t){return t=Math.pow(10,t||0),Math.round(e*t)/t}},WCF.String={addThousandsSeparator:function(e){return String(e).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g,"$1"+WCF.Language.get("wcf.global.thousandsSeparator"))},escapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")},escapeRegExp:function(e){return String(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},formatNumeric:function(e,t){e=String(WCF.Number.round(e,t||2));var i=e.split(".");return e=this.addThousandsSeparator(i[0]),i.length>1&&(e+=WCF.Language.get("wcf.global.decimalPoint")+i[1]),e=e.replace("-","−")},lcfirst:function(e){return String(e).substring(0,1).toLowerCase()+e.substring(1)},ucfirst:function(e){return String(e).substring(0,1).toUpperCase()+e.substring(1)},unescapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">")}},WCF.TabMenu={init:function(){require(["WoltLabSuite/Core/Ui/TabMenu"],function(e){e.setup()})},reload:function(){this.init()}},WCF.Template=Class.extend({init:function(e){var t=new WCF.Dictionary,i=0;e=e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/g,"\\n"),e=e.replace(/\{literal\}(.*?)\{\/literal\}/g,$.proxy(function(e){var i="@@@@@@@@@@@"+Math.random()+"@@@@@@@@@@@";return t.add(i,e.replace(/\{\/?literal\}/g,"")),i},this)),e=e.replace(/\{\*.*?\*\}/g,"");var n=function(e){for(var t=e.split(""),i={},n=!0,s="",a="",o=!1,r=!1,l=!1,c=0,u=t.length;c<u;c++){var h=t[c];n&&"="!=h&&" "!=h?s+=h:n&&"="==h?(n=!1,r=!1,o=!1,l=!1):n||r||o||" "!=h?n||!r||l||"'"!=h?n||r||o||"'"!=h?n||!o||l||'"'!=h?n||r||o||'"'!=h?n||!o&&!r||l||"\\"!=h?n||(l=!1,a+=h):(l=!0,a+=h):(o=!0,a+=h):(o=!1,a+=h):(r=!0,a+=h):(r=!1,a+=h):(n=!0,i[s]=a,a=s="")}if(i[s]=a,o||r||l)throw new Error('Syntax error in parameterList: "'+e+'"');return i},s=function(e){return e.replace(/\\n/g,"\n").replace(/\\\\/g,"\\").replace(/\\'/g,"'")};e=e.replace(/\{(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.escapeHTML("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{#(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.formatNumeric("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{@(\$[^\}]+?)\}/g,function(e,t){return"' + "+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+" + '"}).replace(/\{lang\}(.+?)\{\/lang\}/g,function(e,t){return"' + WCF.Language.get('"+t+"', v) + '"}).replace(/\{include (.+?)\}/g,function(e,t){t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var i=n(t);if(void 0===i.file)throw new Error("Missing file attribute in include-tag");return i.file=i.file.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"' + "+i.file+".fetch(v) + '"}).replace(/\{if (.+?)\}/g,function(e,t){return"';\nif ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{else ?if (.+?)\}/g,function(e,t){return"';\n}\nelse if ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{implode (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in implode-tag");if(void 0===s.item)throw new Error("Missing item attribute in implode-tag");return void 0===s.glue&&(s.glue="', '"),s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\nvar $implode_"+i+" = false;\nfor ($implodeKey_"+i+" in "+s.from+") {\n\tv["+s.item+"] = "+s.from+"[$implodeKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $implodeKey_"+i+";\n":"")+"\tif ($implode_"+i+") $output += "+s.glue+";\n\t$implode_"+i+" = true;\n\t$output += '"}).replace(/\{foreach (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in foreach-tag");if(void 0===s.item)throw new Error("Missing item attribute in foreach-tag");return s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\n$foreach_"+i+" = false;\nfor ($foreachKey_"+i+" in "+s.from+") {\n\t$foreach_"+i+" = true;\n\tbreak;\n}\nif ($foreach_"+i+") {\n\tfor ($foreachKey_"+i+" in "+s.from+") {\n\t\tv["+s.item+"] = "+s.from+"[$foreachKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $foreachKey_"+i+";\n":"")+"\t\t$output += '"}).replace(/\{foreachelse\}/g,"';\n\t}\n}\nelse {\n\t{\n\t\t$output += '").replace(/\{\/foreach\}/g,"';\n\t}\n}\n$output += '").replace(/\{else\}/g,"';\n}\nelse {\n\t$output += '").replace(/\{\/(if|implode)\}/g,"';\n}\n$output += '");for(var a in WCF.Template.callbacks)e=WCF.Template.callbacks[a](e);e=e.replace("{ldelim}","{").replace("{rdelim}","}"),t.each(function(t){e=e.replace(t.key,t.value)}),e="$output += '"+e+"';";try{this.fetch=new Function("v","v = window.$.extend({}, v, { __wcf: window.WCF, __window: window }); var $output = ''; "+e+" return $output;")}catch(t){throw console.debug("var $output = ''; "+e+" return $output;"),t}},fetch:function(e){}}),WCF.Template.callbacks=[],WCF.ToggleOptions=Class.extend({_element:null,_showItems:[],_hideItems:[],_callback:null,init:function(e,t,i,n){this._element=$("#"+e),this._showItems=t,this._hideItems=i,void 0!==n&&(this._callback=n),this._element.click($.proxy(this._toggle,this)),this._toggle()},_toggle:function(){if(this._element.prop("checked")){for(var e=0,t=this._showItems.length;e<t;e++){var i=this._showItems[e];$("#"+i).show()}for(var e=0,t=this._hideItems.length;e<t;e++){var i=this._hideItems[e];$("#"+i).hide()}null!==this._callback&&this._callback()}}}),WCF.Collapsible={},WCF.Collapsible.Simple={init:function(){$(".jsCollapsible").each($.proxy(function(e,t){this._initButton(t)},this))},_initButton:function(e){var t=$(e);t.data("isOpen")||$("#"+t.data("collapsibleContainer")).hide(),t.click($.proxy(this._toggle,this))},_toggle:function(e){var t=$(e.currentTarget),i=t.data("isOpen"),n=$("#"+$.wcfEscapeID(t.data("collapsibleContainer")));return i?(n.stop().wcfBlindOut("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!1):(n.stop().wcfBlindIn("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!0),t.data("isOpen",i),e.stopPropagation(),!1},_toggleImage:function(e){var t=e.find("span.icon");e.data("isOpen")?t.removeClass("fa-chevron-right").addClass("fa-chevron-down"):t.removeClass("fa-chevron-down").addClass("fa-chevron-right")}},WCF.Collapsible.Remote=Class.extend({_className:"",_containers:{},_containerData:{},_proxy:null,init:function(e){this._className=e,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._init(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Collapsible.Remote",$.proxy(this._init,this))},_init:function(e){this._getContainers().each($.proxy(function(e,t){var i=$(t),n=i.wcfIdentify();void 0===this._containers[n]&&(this._containers[n]=i,this._initContainer(n))},this))},_initContainer:function(e){var t=this._getTarget(e),i=this._getButtonContainer(e),n=this._createButton(e,i);this._containerData[e]={button:n,buttonContainer:i,isOpen:this._containers[e].data("isOpen"),target:t},this._containers[e].data("isOpen")||$("#"+e).addClass("jsCollapsed")},_getContainers:function(){},_getTarget:function(e){},_getButtonContainer:function(e){},_createButton:function(e,t){var i=elBySel(".jsStaticCollapsibleButton",t[0]);return null!==i&&i.parentNode===t[0]?(i.classList.remove("jsStaticCollapsibleButton"),i=$(i)):i=$('<span class="collapsibleButton jsTooltip pointer icon icon16 fa-chevron-down" title="'+WCF.Language.get("wcf.global.button.collapsible")+'">').prependTo(t),i.data("containerID",e).click($.proxy(this._toggleContainer,this)),i},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",a=n?"close":"open";this._proxy.setOption("data",{actionName:"loadContainer",className:this._className,interfaceName:"wcf\\data\\ILoadableContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:a},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),$("#"+i).toggleClass("jsCollapsed")},_exchangeIcon:function(e,t){t=t||"spinner",e.removeClass("fa-chevron-down fa-chevron-right fa-spinner").addClass("fa-"+t)},_getObjectID:function(e){return $("#"+e).data("objectID")},_getAdditionalParameters:function(e){return{}},_updateContent:function(e,t,i){this._containerData[e].target.html(t)},_success:function(e,t,i){if(e.returnValues.containerID){var n=e.returnValues.containerID;if(this._containers[n]){this._containerData[n].isOpen=!!e.returnValues.isOpen;var s=e.returnValues.isOpen?"open":"close";this._updateContent(n,$.trim(e.returnValues.content),s)}}}}),WCF.Collapsible.SimpleRemote=WCF.Collapsible.Remote.extend({init:function(e){this._super(e),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1})},_initContainer:function(e){this._super(e),this._containerData[e].isOpen||(this._containerData[e].target.hide(),this._exchangeIcon(this._containerData[e].button,"chevron-right"))},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",a=n?"close":"open";this._proxy.setOption("data",{actionName:"toggleContainer",className:this._className,interfaceName:"wcf\\data\\IToggleContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:a},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),this._exchangeIcon(this._containerData[i].button,"open"===a?"chevron-down":"chevron-right"),"open"===a?this._containerData[i].target.show():this._containerData[i].target.hide(),$("#"+i).toggleClass("jsCollapsed"),this._containerData[i].isOpen="open"===a}}),WCF.User={userID:0,username:"",init:function(e,t){this.userID=e,this.username=t}},WCF.Effect={},WCF.Effect.Scroll=Class.extend({scrollTo:function(e,t,i){if(!e.length)return!0;var n=e.getOffsets("offset").top,s=$(document).height(),a=$(window).height();return n>s-a&&(n=s-a)<0&&(n=0),!0===i?$("html,body").scrollTop(n):$("html,body").animate({scrollTop:n},400,function(e,t,i,n,s){return-n*((t=t/s-1)*t*t*t-1)+i}),!1}}),WCF.CloseOverlayHandler={addCallback:function(e,t){
-require(["Ui/CloseOverlay"],function(i){i.add(e,t)})},removeCallback:function(e){require(["Ui/CloseOverlay"],function(t){t.remove(e)})},forceExecution:function(){require(["Ui/CloseOverlay"],function(e){e.execute()})}},WCF.DOMNodeInsertedHandler={addCallback:function(e,t){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.add("__legacy__",t)})},_executeCallbacks:function(){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.trigger()})},execute:function(){this._executeCallbacks()}},WCF.DOMNodeRemovedHandler={_callbacks:new WCF.Dictionary,_isExecuting:!1,_isListening:!1,addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.DOMNodeRemovedHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){if(window.MutationObserver){new MutationObserver(function(e){var t=!1;e.forEach(function(e){e.removedNodes.length&&(t=!0)}.bind(this)),t&&this._executeCallbacks({})}.bind(this)).observe(document.body,{childList:!0,subtree:!0})}else $(document).bind("DOMNodeRemoved",$.proxy(this._executeCallbacks,this));this._isListening=!0}},_executeCallbacks:function(e){this._isExecuting||(this._isExecuting=!0,this._callbacks.each(function(t){t.value(e)}),this._isExecuting=!1)}},WCF.Option={},WCF.Option.Handler=Class.extend({init:function(){this._initOptions(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Option.Handler",$.proxy(this._initOptions,this))},_initOptions:function(){$(".jsEnablesOptions").each($.proxy(this._initOption,this))},_initOption:function(e,t){this._change(t),$(t).change($.proxy(this._handleChange,this))},_handleChange:function(e){this._change($(e.target))},_change:function(option){option=$(option);var disableOptions=eval(option.data("disableOptions")),enableOptions=eval(option.data("enableOptions"));switch(option.getTagName()){case"input":switch(option.attr("type")){case"checkbox":this._execute(option.prop("checked"),disableOptions,enableOptions);break;case"radio":if(option.prop("checked")){var isActive=!0;option.data("isBoolean")&&1!=option.val()&&(isActive=!1),this._execute(isActive,disableOptions,enableOptions)}}break;case"select":var $value=option.val(),relevantDisableOptions=[],relevantEnableOptions=[];if(disableOptions.length>0)for(var $index in disableOptions){var $item=disableOptions[$index];$item.value==$value?relevantDisableOptions.push($item.option):relevantEnableOptions.push($item.option)}if(enableOptions.length>0)for(var $index in enableOptions){var $item=enableOptions[$index];$item.value==$value?relevantEnableOptions.push($item.option):relevantDisableOptions.push($item.option)}this._execute(!0,relevantDisableOptions,relevantEnableOptions)}},_execute:function(e,t,i){if(t.length>0)for(var n=0,s=t.length;n<s;n++){var a=t[n];if($.wcfIsset(a))this._enableOption(a,!e);else{var o=$("."+a+"Input");o.length&&this._enableOptions(o.children("dd").find("input, select, textarea"),!e)}}if(i.length>0)for(var n=0,s=i.length;n<s;n++){var a=i[n];if($.wcfIsset(a))this._enableOption(a,e);else{var o=$("."+a+"Input");o.length&&this._enableOptions(o.children("dd").find("input, select, textarea"),e)}}},_enableOption:function(e,t){this._enableOptionElement($("#"+$.wcfEscapeID(e)),t)},_enableOptionElement:function(e,t){e=$(e);var i=e.getTagName();if("select"==i||"input"==i&&("checkbox"==e.attr("type")||"file"==e.attr("type")||"radio"==e.attr("type"))){if("input"===i&&"radio"===e[0].type?e[0].checked||(t?e.enable():e.disable()):t?e.enable():e.disable(),e.parents(".optionTypeBoolean:eq(0)")){var n=e.wcfIdentify().replace(/\./g,"\\."),s=$("#"+n+"_no");t?s.enable():s.disable();var a=$("#"+n+"_never");a.length&&(t?a.enable():a.disable())}}else t?e.removeAttr("readonly"):e.attr("readonly",!0);t?e.closest("dl").removeClass("disabled"):e.closest("dl").addClass("disabled")},_enableOptions:function(e,t){for(var i=0,n=e.length;i<n;i++)this._enableOptionElement(e[i],t)}}),WCF.PageVisibilityHandler={_callbacks:new WCF.Dictionary,_isListening:!1,_hiddenFieldName:"",addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.PageVisibilityHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){var e=null;void 0!==document.hidden?(this._hiddenFieldName="hidden",e="visibilitychange"):void 0!==document.mozHidden?(this._hiddenFieldName="mozHidden",e="mozvisibilitychange"):void 0!==document.msHidden?(this._hiddenFieldName="msHidden",e="msvisibilitychange"):void 0!==document.webkitHidden&&(this._hiddenFieldName="webkitHidden",e="webkitvisibilitychange"),null===e?console.debug("[WCF.PageVisibilityHandler] This browser does not support the page visibility API."):$(document).on(e,$.proxy(this._executeCallbacks,this)),this._isListening=!0}},_executeCallbacks:function(e){if(!this._isExecuting){this._isExecuting=!0;var t=document[this._hiddenFieldName];this._callbacks.each(function(e){e.value(t)}),this._isExecuting=!1}}},WCF.Table={},WCF.Table.EmptyTableHandler=Class.extend({_options:{},_rowClassName:"",init:function(e,t,i){this._rowClassName=t,this._tableContainer=e,this._options=$.extend(!0,{emptyMessage:null,messageType:"info",refreshPage:!1,updatePageNumber:!1,isTable:0!==this._tableContainer.find("table").length},i||{}),WCF.DOMNodeRemovedHandler.addCallback("WCF.Table.EmptyTableHandler."+t,$.proxy(this._remove,this))},_getRowCount:function(){return this._tableContainer.find((this._options.isTable?"table tr.":".tabularList .")+this._rowClassName).length},_handleEmptyTable:function(){if(this._options.emptyMessage)this._tableContainer.replaceWith($("<p />").addClass(this._options.messageType).text(this._options.emptyMessage));else if(this._options.refreshPage)if(this._options.updatePageNumber){var e=window.location.href.match(/(\?|&)pageNo=(\d+)/g);if(e){var t=e[e.length-1].match(/\d+/g);this._options.updatePageNumber>0?t++:t--,window.location=window.location.href.replace(e[e.length-1],e[e.length-1][0]+"pageNo="+t)}}else window.location.reload();else this._tableContainer.remove()},_remove:function(e){if($.getLength(e)){var t=$(e.target);if(t.hasClass(this._rowClassName))if(this._options.isTable){var i=t.parents("tbody:eq(0)");1==i.children("tr").length&&this._handleEmptyTable()}else 1===this._getRowCount()&&this._handleEmptyTable()}else this._getRowCount()||this._handleEmptyTable()}}),WCF.Search={},WCF.Search.Base=Class.extend({_callback:null,_caretAt:-1,_className:"",_commaSeperated:!1,_delay:0,_excludedSearchValues:[],_itemCount:0,_itemIndex:-1,_list:null,_oldSearchString:[],_proxy:null,_searchInput:null,_triggerLength:3,_timer:null,init:function(e,t,i,n,s){return null===t||void 0===t||$.isFunction(t)?(this._callback=t||null,this._caretAt=-1,this._delay=0,this._excludedSearchValues=[],i&&(this._excludedSearchValues=i),this._searchInput=$(e),this._searchInput.length?(this._searchInput.keydown($.proxy(this._keyDown,this)).keyup($.proxy(this._keyUp,this)).wrap('<span class="dropdown" />'),$.browser.mozilla&&$.browser.touch&&this._searchInput.on("input",$.proxy(this._keyUp,this)),this._list=$('<ul class="dropdownMenu" />').insertAfter(this._searchInput),this._commaSeperated=!!n,this._oldSearchString=[],this._itemCount=0,this._itemIndex=-1,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!0===s,success:$.proxy(this._success,this),autoAbortPrevious:!0}),this._searchInput.is("input")&&this._searchInput.attr("autocomplete","off"),this._searchInput.blur($.proxy(this._blur,this)),void WCF.Dropdown.initDropdownFragment(this._searchInput.parent(),this._list)):void console.debug("[WCF.Search.Base] Selector '"+e+"' for search input is invalid, aborting.")):void console.debug("[WCF.Search.Base] The given callback is invalid, aborting.")},_blur:function(){var e=this;new WCF.PeriodicalExecuter(function(t){e._list.is(":visible")&&e._clearList(!1),t.stop()},250)},_keyDown:function(e){if(e.which===$.ui.keyCode.ENTER){var t=this._searchInput.parents(".dropdown");t.data("disableAutoFocus")?-1!==this._itemIndex&&e.preventDefault():(t.data("preventSubmit")||-1!==this._itemIndex)&&e.preventDefault()}},_keyUp:function(e){switch(e.which){case 37:case 39:return;case 38:return void this._selectPreviousItem();case 40:return void this._selectNextItem();case 13:return this._selectElement(e)}var t=this._getSearchString(e);if(""===t)this._clearList(!1);else if(t.length>=this._triggerLength){var i={data:{excludedSearchValues:this._excludedSearchValues,searchString:t}};if(this._delay){null!==this._timer&&this._timer.stop();var n=this;this._timer=new WCF.PeriodicalExecuter(function(){n._queryServer(i),n._timer.stop(),n._timer=null},this._delay)}else this._queryServer(i)}else this._clearList(!1)},_queryServer:function(e){this._searchInput.parents(".searchBar").addClass("loading"),this._proxy.setOption("data",{actionName:"getSearchResultList",className:this._className,interfaceName:"wcf\\data\\ISearchAction",parameters:this._getParameters(e)}),this._proxy.sendRequest()},setDelay:function(e){this._delay=e},_selectNextItem:function(){0!==this._itemCount&&(this._itemIndex++,this._itemIndex===this._itemCount&&(this._itemIndex=0),this._highlightSelectedElement())},_selectPreviousItem:function(){0!==this._itemCount&&(this._itemIndex--,-1===this._itemIndex&&(this._itemIndex=this._itemCount-1),this._highlightSelectedElement())},_highlightSelectedElement:function(){this._list.find("li").removeClass("dropdownNavigationItem"),this._list.find("li:eq("+this._itemIndex+")").addClass("dropdownNavigationItem")},_selectElement:function(e){return 0===this._itemCount||(this._list.find("li.dropdownNavigationItem").trigger("click"),!1)},_getSearchString:function(e){var t=$.trim(this._searchInput.val());if(this._commaSeperated){if((e.keyCode||e.which)==$.ui.keyCode.COMMA)return"";for(var i=t.split(","),n=i.length,s=0;s<n;s++)i[s]=$.trim(i[s]);for(var s=0;s<n;s++){var a=i[s];if(!this._oldSearchString[s]){t=a;break}if(a!=this._oldSearchString[s]){t=a,this._caretAt=s;break}}this._oldSearchString=i}return t},_getParameters:function(e){return e},_success:function(e,t,i){if(this._clearList(!1),this._searchInput.parents(".searchBar").removeClass("loading"),$.getLength(e.returnValues))for(var n in e.returnValues){var s=e.returnValues[n];this._createListItem(s)}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var a=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(a).hasClass("dropdownOpen")||(WCF.Dropdown.toggleDropdown(a),this._openDropdown()),this._itemIndex=-1,WCF.Dropdown.getDropdown(a).data("disableAutoFocus")||this._selectNextItem()},_openDropdown:function(){},_handleEmptyResult:function(){return!1},_createListItem:function(e){var t=$("<li><span>"+WCF.String.escapeHTML(e.label)+"</span></li>").appendTo(this._list);return t.data("objectID",e.objectID).data("label",e.label).click($.proxy(this._executeCallback,this)),this._itemCount++,t},_executeCallback:function(e){var t=!1,i=$(e.currentTarget);if(this._commaSeperated){var n=i.data("label");this._oldSearchString[this._caretAt]=n,this._searchInput.val(this._oldSearchString.join(", ")),$.browser.webkit&&this._searchInput.css({display:"block"});var s=this._searchInput.val().toLowerCase().indexOf(n.toLowerCase())+n.length;this._searchInput.focus().setCaret(s)}else null===this._callback?this._searchInput.val(i.data("label")):t=!0===this._callback(i.data());this._clearList(t)},_clearList:function(e){e&&!this._commaSeperated&&this._searchInput.val(""),WCF.Dropdown.getDropdown(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),WCF.Dropdown.getDropdownMenu(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),this._list.end().empty(),WCF.CloseOverlayHandler.removeCallback("WCF.Search.Base"),this._itemCount=0,this._itemIndex=-1},addExcludedSearchValue:function(e){WCF.inArray(e,this._excludedSearchValues)||this._excludedSearchValues.push(e)},removeExcludedSearchValue:function(e){var t=$.inArray(e,this._excludedSearchValues);-1!=t&&this._excludedSearchValues.splice(t,1)}}),WCF.Search.User=WCF.Search.Base.extend({_className:"wcf\\data\\user\\UserAction",_includeUserGroups:!1,init:function(e,t,i,n,s){this._includeUserGroups=i,this._super(e,t,n,s)},_getParameters:function(e){return e.data.includeUserGroups=this._includeUserGroups?1:0,e},_createListItem:function(e){var t=this._super(e),i=null;if(e.icon?i=$(e.icon):this._includeUserGroups&&"group"===e.type&&(i=$('<span class="icon icon16 fa-users" />')),i){var n=t.find("span").detach(),s=$("<div />").addClass("box16").appendTo(t);s.append(i),s.append($("<div />").append(n))}return t.data("type",e.type),t}}),WCF.System={},WCF.System.Dependency={},WCF.System.Dependency.Manager={_callbacks:{},_loaded:[],_setupCallbacks:{},register:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Callback for identifier '"+e+"' is invalid, aborting.");WCF.inArray(e,this._loaded)?setTimeout(function(){t()},1):(this._callbacks[e]||(this._callbacks[e]=[]),this._callbacks[e].push(t))},setup:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Setup callback for identifier '"+e+"' is invalid, aborting.");this._setupCallbacks[e]||(this._setupCallbacks[e]=[]),this._setupCallbacks[e].push(t)},invoke:function(e){if(this._setupCallbacks[e]){for(var t=0,i=this._setupCallbacks[e].length;t<i;t++)this._setupCallbacks[e][t]();delete this._setupCallbacks[e]}if(this._loaded.push(e),this._callbacks[e]){for(var t=0,i=this._callbacks[e].length;t<i;t++)this._callbacks[e][t]();delete this._callbacks[e]}},reset:function(e){var t=this._loaded.indexOf(e);-1!==t&&this._loaded.splice(t,1)}},WCF.System.FlexibleMenu={init:function(){},registerMenu:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.register(e)})},rebuild:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.rebuild(e)})}},WCF.System.Mobile={},WCF.System.ObjectStore={_objects:{},add:function(e,t){void 0===this._objects[e]&&(this._objects[e]=[]),this._objects[e].push(t)},invoke:function(e,t){if(this._objects[e])for(var i=0;i<this._objects[e].length;i++)t(this._objects[e][i])}},WCF.System.Captcha={_registeredCaptchas:[],addCallback:function(e,t){require(["WoltLabSuite/Core/Controller/Captcha"],function(i){try{i.add(e,t),this._registeredCaptchas.push(e)}catch(e){if(e instanceof TypeError)return void console.debug("[WCF.System.Captcha] Given callback is no function")}}.bind(this))},getData:function(e){var t;if(-1===this._registeredCaptchas.indexOf(e))return t;var i=require("WoltLabSuite/Core/Controller/Captcha");try{t=i.getData(e)}catch(t){console.debug('[WCF.System.Captcha] Unknow captcha id "'+e+'"')}return t},removeCallback:function(e){require(["WoltLabSuite/Core/Controller/Captcha"],function(t){try{t.delete(e),this._registeredCaptchas.splice(this._registeredCaptchas.indexOf(item),1)}catch(e){}}.bind(this))}},WCF.System.Page={},WCF.System.Notification=Class.extend({_cssClassNames:"",_message:"",init:function(e,t){this._cssClassNames=t||"",this._message=e||""},show:function(e,t,i,n){require(["Ui/Notification"],function(t){t.show(i||this._message,e,n||this._cssClassNames)}.bind(this))}}),WCF.System.Confirmation={show:function(e,t,i,n,s){if("object"==typeof n){var a=$("<div />");a.append(n),n=a.html()}require(["Ui/Confirmation"],function(a){a.show({legacyCallback:t,message:e,parameters:i,template:n||"",messageIsHtml:!0===s})})}},WCF.System.DisableScrolling={_depth:0,_oldOverflow:null,disable:function(){$.browser.touch||(0===this._depth&&(this._oldOverflow=$(document.body).css("overflow"),$(document.body).css("overflow","hidden")),this._depth++)},enable:function(){0!==this._depth&&0===--this._depth&&$(document.body).css("overflow",this._oldOverflow)}},WCF.System.DisableZoom={_depth:0,_oldViewportSettings:null,disable:function(){if(0===this._depth){var e=$("meta[name=viewport]");this._oldViewportSettings=e.attr("content"),e.attr("content",this._oldViewportSettings+",maximum-scale=1")}this._depth++},enable:function(){0!==this._depth&&0===--this._depth&&$("meta[name=viewport]").attr("content",this._oldViewportSettings)}},WCF.System.Fullscreen={enterFullscreen:function(e){e.requestFullscreen?e.requestFullscreen():e.msRequestFullscreen?e.msRequestFullscreen():e.mozRequestFullScreen?e.mozRequestFullScreen():e.webkitRequestFullscreen&&e.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)},toggleFullscreen:function(e){null===this.getFullscreenElement()?this.enterFullscreen(e):this.exitFullscreen()},getFullscreenElement:function(){return document.fullscreenElement?document.fullscreenElement:document.mozFullScreenElement?document.mozFullScreenElement:document.webkitFullscreenElement?document.webkitFullscreenElement:document.msFullscreenElement?document.msFullscreenElement:null},exitFullscreen:function(){document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen()},isSupported:function(){return!!(document.documentElement.requestFullscreen||document.documentElement.msRequestFullscreen||document.documentElement.mozRequestFullScreen||document.documentElement.webkitRequestFullscreen)}},WCF.System.PageNavigation={init:function(e,t){require(["WoltLabSuite/Core/Ui/Page/JumpTo"],function(i){for(var n=elBySelAll(e),s=0,a=n.length;s<a;s++)i.init(n[s],t)})}},WCF.System.KeepAlive=Class.extend({init:function(e){new WCF.PeriodicalExecuter(function(e){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"keepAlive",className:"wcf\\data\\session\\SessionAction"},failure:function(){e.stop()},showLoadingOverlay:!1,success:function(e){WCF.System.PushNotification.executeCallbacks(e)},suppressErrors:!0})},1e3*e)}}),WCF.System.PushNotification={_callbacks:{},addCallback:function(e,t){void 0===this._callbacks[e]&&(this._callbacks[e]=[]),this._callbacks[e].push(t)},executeCallbacks:function(e){for(var t in e.returnValues)if(void 0!==this._callbacks[t])for(var i=0;i<this._callbacks[t].length;i++)this._callbacks[t][i](e.returnValues[t])}},WCF.System.Event={addListener:function(e,t,i){return window.__wcf_bc_eventHandler.add(e,t,i)},removeListener:function(e,t,i){return window.__wcf_bc_eventHandler.remove(e,t,i)},removeAllListeners:function(e,t){return window.__wcf_bc_eventHandler.removeAll(e,t)},fireEvent:function(e,t,i){window.__wcf_bc_eventHandler.fire(e,t,i)}},WCF.System.Worker=Class.extend({_aborted:!1,_actionName:"",_callback:null,_className:"",_dialog:null,_proxy:null,_title:"",init:function(e,t,i,n,s){this._aborted=!1,this._actionName=e,this._callback=s||null,this._className=t,this._dialog=null,this._proxy=new WCF.Action.Proxy({autoSend:!0,data:{actionName:this._actionName,className:this._className,parameters:n||{}},showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._title=i},_success:function(e){if(null===this._dialog&&(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.wcfDialog({closeConfirmMessage:WCF.Language.get("wcf.worker.abort.confirmMessage"),closeViaModal:!1,onClose:$.proxy(function(){this._aborted=!0,this._proxy.abortPrevious(),window.location.reload()},this),title:this._title})),!this._aborted)if(e.returnValues.template&&this._dialog.html(e.returnValues.template),this._dialog.find("progress").attr("value",e.returnValues.progress).text(e.returnValues.progress+"%").next("span").text(e.returnValues.progress+"%"),e.returnValues.progress<100){var t=e.returnValues.parameters||{};t.loopCount=e.returnValues.loopCount,this._proxy.setOption("data",{actionName:this._actionName,className:this._className,parameters:t}),this._proxy.sendRequest()}else if(null!==this._callback)this._callback(this,e);else{this._dialog.find(".fa-spinner").removeClass("fa-spinner").addClass("fa-check green"),this._dialog.find(".contentHeader h1").text(WCF.Language.get("wcf.global.worker.completed"));var i=$('<div class="formSubmit" />').appendTo(this._dialog);$('<button class="buttonPrimary">'+WCF.Language.get("wcf.global.button.next")+"</button>").appendTo(i).focus().click(function(){e.returnValues.redirectURL?window.location=e.returnValues.redirectURL:window.location.reload()}),this._dialog.wcfDialog("render")}}}),WCF.InlineEditor=Class.extend({_callbacks:[],_dropdowns:{},_elements:{},_notification:null,_options:[],_proxy:null,_triggerElements:{},_updateData:[],init:function(e){var t=$(e);if(t.length){this._setOptions();for(var i="",n=0,s=this._options.length;n<s;n++)if(this._options[n].isQuickOption){i=this._options[n].optionName;break}var a=this;t.each(function(e,t){var n=$(t),s=n.wcfIdentify(),o=a._getTriggerElement(n);null!==o&&1===o.length&&(o.on(WCF_CLICK_EVENT,$.proxy(a._show,a)).data("elementID",s),i&&o.disableSelection().data("optionName",i).dblclick($.proxy(a._click,a)),a._elements[s]=n)}),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),WCF.CloseOverlayHandler.addCallback("WCF.InlineEditor",$.proxy(this._closeAll,this)),this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success"),"success")}},_closeAll:function(){for(var e in this._elements)this._hide(e)},_setOptions:function(){this._options=[]},registerCallback:function(e){$.isFunction(e)&&this._callbacks.push(e)},_getTriggerElement:function(e){return null},_show:function(e){e.preventDefault();var t=$(e.currentTarget).data("elementID"),i=null;if(!this._dropdowns[t]){this._triggerElements[t]=i=this._getTriggerElement(this._elements[t]).addClass("dropdownToggle");var n=i[0].parentNode;n&&"LI"===n.nodeName&&1===n.childElementCount?n.classList.add("dropdown"):i.wrap('<span class="dropdown" />'),this._dropdowns[t]=$('<ul class="dropdownMenu" />').insertAfter(i)}this._dropdowns[t].empty();for(var s=!1,a="",o=0,r=this._options.length;o<r;o++){var l=this._options[o];if("divider"===l.optionName)""!==a&&"divider"!==a&&($('<li class="dropdownDivider" />').appendTo(this._dropdowns[t]),a=l.optionName);else if(this._validate(t,l.optionName)||this._validateCallbacks(t,l.optionName)){var c=$("<li><span>"+l.label+"</span></li>").appendTo(this._dropdowns[t]);c.data("elementID",t).data("optionName",l.optionName).data("isQuickOption",!!l.isQuickOption).click($.proxy(this._click,this)),s=!0,a=l.optionName}}if(s){var u=this._dropdowns[t].children().last();u.hasClass("dropdownDivider")&&u.remove();var h=null,d=0;if(this._dropdowns[t].children().each(function(e,t){var i=$(t);i.hasClass("dropdownDivider")||(i.data("isQuickOption")?h=i:d++)}),!d)return h.trigger("click"),this._triggerElements[t]&&WCF.Dropdown.close(this._triggerElements[t].parents(".dropdown").wcfIdentify()),!1}return null!==i&&WCF.Dropdown.initDropdown(i,!0),!1},_validate:function(e,t){return!1},_validateCallbacks:function(e,t){var i=this._callbacks.length;if(i)for(var n=0;n<i;n++)if(this._callbacks[n].validate(this._elements[e],t))return!0;return!1},_success:function(e,t,i){this._updateData.length&&(this._updateState(e),this._updateData=[])},_updateState:function(e){},_click:function(e){var t=$(e.currentTarget),i=t.data("elementID"),n=t.data("optionName");this._execute(i,n)||this._executeCallback(i,n),this._hide(i)},_execute:function(e,t){return!1},_executeCallback:function(e,t){var i=this._callbacks.length;if(i)for(var n=0;n<i;n++)if(this._callbacks[n].execute(this._elements[e],t))return!0;return!1},_hide:function(e){this._dropdowns[e]&&this._dropdowns[e].empty().removeClass("dropdownOpen")}}),WCF.Upload=Class.extend({_name:"__files[]",_buttonSelector:null,_fileListSelector:null,_fileUpload:null,_className:"",_iframe:null,_internalFileID:0,_options:{},_uploadMatrix:[],_supportsAJAXUpload:!0,_overlay:null,init:function(e,t,i,n){this._buttonSelector=e,this._fileListSelector=t,this._className=i,this._internalFileID=0,this._options=$.extend(!0,{action:"upload",multiple:!1,url:"index.php?ajax-upload/&t="+SECURITY_TOKEN},n||{}),this._options.url=WCF.convertLegacyURL(this._options.url),0===this._options.url.indexOf("index.php")&&(this._options.url=WSC_API_URL+this._options.url);var s=new XMLHttpRequest;this._supportsAJAXUpload=s&&"upload"in s&&"onprogress"in s.upload,this._createButton()},_createButton:function(){if(this._supportsAJAXUpload){this._fileUpload=$('<input type="file" name="'+this._name+'" '+(this._options.multiple?'multiple="true" ':"")+"/>"),this._fileUpload.change($.proxy(this._upload,this));var e=$('<p class="button uploadButton"><span>'+WCF.Language.get("wcf.global.button.upload")+"</span></p>");e.prepend(this._fileUpload)}else{var e=$('<p class="button uploadFallbackButton"><span>'+WCF.Language.get("wcf.global.button.upload")+"</span></p>");e.click($.proxy(this._showOverlay,this))}this._insertButton(e)},_insertButton:function(e){this._buttonSelector.prepend(e)},_removeButton:function(){var e=".uploadButton";this._supportsAJAXUpload||(e=".uploadFallbackButton"),this._buttonSelector.find(e).remove()},_upload:function(e,t,i){var n=null,s=[];if(t)s.push(t);else if(i){var a="";switch(i.type){case"image/png":a=".png";break;case"image/jpeg":a=".jpg";break;case"image/gif":a=".gif"}s.push({name:"pasted-from-clipboard"+a})}else s=this._fileUpload.prop("files");if(s.length){var o=new FormData;if(n=this._createUploadMatrix(s),!this._uploadMatrix[n].length)return null;for(var r=0,l=s.length;r<l;r++)if(this._uploadMatrix[n][r]){var c=this._uploadMatrix[n][r].data("internalFileID");i?o.append("__files["+c+"]",i,s[r].name):o.append("__files["+c+"]",s[r])}o.append("actionName",this._options.action),o.append("className",this._className);var u=this._getParameters();for(var h in u)o.append("parameters["+h+"]",u[h]);var d=this;$.ajax({type:"POST",url:this._options.url,enctype:"multipart/form-data",data:o,contentType:!1,processData:!1,success:function(e,t,i){d._success(n,e)},error:$.proxy(this._error,this),xhr:function(){var e=$.ajaxSettings.xhr();return e&&e.upload.addEventListener("progress",function(e){d._progress(n,e)},!1),e},xhrFields:{withCredentials:!0}})}return n},_createUploadMatrix:function(e){if(e.length){var t=this._uploadMatrix.length;this._uploadMatrix[t]=[];for(var i=0,n=e.length;i<n;i++){var s=e[i],a=this._initFile(s);a.hasClass("uploadFailed")||(a.data("filename",s.name).data("internalFileID",this._internalFileID++),this._uploadMatrix[t][i]=a)}return t}return null},_success:function(e,t){},_error:function(e,t,i){},_progress:function(e,t){var i=Math.round(100*t.loaded/t.total);for(var n in this._uploadMatrix[e])this._uploadMatrix[e][n].find("progress").attr("value",i)},_getParameters:function(){return{}},_initFile:function(e){return $("<li>"+e.name+" ("+e.size+')<progress max="100" /></li>').appendTo(this._fileListSelector)},_showOverlay:function(){if(null===this._iframe&&(this._iframe=$('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body)),!this._overlay){this._overlay=$('<div><form enctype="multipart/form-data" method="post" action="'+this._options.url+'" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);var e=this._overlay.find("form");$('<dl class="wide"><dd><input type="file" id="__fileUpload" name="'+this._name+'" '+(this._options.multiple?'multiple="true" ':"")+"/></dd></dl>").appendTo(e),$('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo(e),$('<input type="hidden" name="isFallback" value="1" />').appendTo(e),$('<input type="hidden" name="actionName" value="'+this._options.action+'" />').appendTo(e),$('<input type="hidden" name="className" value="'+this._className+'" />').appendTo(e);var t=this._getParameters();for(var i in t)$('<input type="hidden" name="'+i+'" value="'+t[i]+'" />').appendTo(e);e.submit($.proxy(function(){var e={name:this._getFilename(),size:""},t=this._createUploadMatrix([e]),i=this;this._iframe.data("loading",!0).off("load").load(function(){i._evaluateResponse(t)}),this._overlay.wcfDialog("close")},this))}this._overlay.wcfDialog({title:WCF.Language.get("wcf.global.button.upload")})},_evaluateResponse:function(e){var t=$.parseJSON(this._iframe.contents().find("pre").html());this._success(e,t)},_getFilename:function(){return $("#__fileUpload").val().split("\\").pop()}}),WCF.Upload.Parallel=WCF.Upload.extend({init:function(e,t,i,n){n=$.extend(!0,n||{},{multiple:!0}),this._super(e,t,i,n)},_upload:function(){for(var e=this._fileUpload.prop("files"),t=0,i=e.length;t<i;t++){var n=e[t],s=new FormData,a=this._createUploadMatrix(n);if(this._uploadMatrix[a].length){s.append("__files["+a+"]",n),s.append("actionName",this._options.action),s.append("className",this._className);var o=this._getParameters();for(var r in o)s.append("parameters["+r+"]",o[r]);this._sendRequest(a,s)}}},_sendRequest:function(e,t){var i=this;return $.ajax({type:"POST",url:this._options.url,enctype:"multipart/form-data",data:t,contentType:!1,processData:!1,success:function(t,n,s){i._success(e,t)},error:$.proxy(this._error,this),xhr:function(){var t=$.ajaxSettings.xhr();return t&&t.upload.addEventListener("progress",function(t){i._progress(e,t)},!1),t}})},_createUploadMatrix:function(e){var t=this._initFile(e);return t.hasClass("uploadFailed")?null:(t.data("filename",e.name).data("internalFileID",this._internalFileID),this._uploadMatrix[this._internalFileID++]=t,this._internalFileID-1)},_success:function(e,t){},_progress:function(e,t){var i=Math.round(100*t.loaded/t.total);this._uploadMatrix[e].find("progress").attr("value",i)},_showOverlay:function(){if(null===this._iframe&&(this._iframe=$('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body)),!this._overlay){this._overlay=$('<div><form enctype="multipart/form-data" method="post" action="'+this._options.url+'" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);var e=this._overlay.find("form");$('<dl class="wide"><dd><input type="file" id="__fileUpload" name="'+this._name+'" '+(this._options.multiple?'multiple="true" ':"")+"/></dd></dl>").appendTo(e),$('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo(e),$('<input type="hidden" name="isFallback" value="1" />').appendTo(e),$('<input type="hidden" name="actionName" value="'+this._options.action+'" />').appendTo(e),$('<input type="hidden" name="className" value="'+this._className+'" />').appendTo(e);var t=this._getParameters();for(var i in t)$('<input type="hidden" name="'+i+'" value="'+t[i]+'" />').appendTo(e);e.submit($.proxy(function(){var e={name:this._getFilename(),size:""},t=this._createUploadMatrix(e),i=this;this._iframe.data("loading",!0).off("load").load(function(){i._evaluateResponse(t)}),this._overlay.wcfDialog("close")},this))}this._overlay.wcfDialog({title:WCF.Language.get("wcf.global.button.upload")})},_evaluateResponse:function(e){var t=$.parseJSON(this._iframe.contents().find("pre").html());this._success(e,t)}}),WCF.Sortable={},WCF.Sortable.List=Class.extend({_additionalParameters:{},_className:"",_containerID:"",_container:null,_notification:null,_offset:0,_options:{},_proxy:null,_structure:{},init:function(e,t,i,n,s,a){this._additionalParameters=a||{},this._containerID=$.wcfEscapeID(e),this._container=$("#"+this._containerID),this._className=t,this._offset=i||0,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._structure={},this._options=$.extend(!0,{axis:"y",connectWith:"#"+this._containerID+" .sortableList",disableNesting:"sortableNoNesting",doNotClear:!0,errorClass:"sortableInvalidTarget",forcePlaceholderSize:!0,handle:"",helper:"clone",items:"li:not(.sortableNoSorting)",opacity:.6,placeholder:"sortablePlaceholder",tolerance:"pointer",toleranceElement:"> span"},n||{});var o=$("#"+this._containerID+" .sortableList")
-;if(o.is("tbody")&&"clone"===this._options.helper){this._options.helper=this._tableRowHelper.bind(this);var r=o.prev("thead");r&&r.find("th").each(function(e,t){t=$(t),t.width(t.width())})}if(s?o.sortable(this._options):o.nestedSortable(this._options),this._className){var l=this._container.find(".formSubmit");if(!l.length&&(l=this._container.next(".formSubmit"),!l.length))return void console.debug("[WCF.Sortable.Simple] Unable to find form submit for saving, aborting.");l.children('button[data-type="submit"]').click($.proxy(this._submit,this))}},_tableRowHelper:function(e,t){return t.children("td").each(function(e,t){t=$(t),t.width(t.width())}),t},_submit:function(){this._structure={},this._container.find(".sortableList").each($.proxy(function(e,t){var i=$(t),n=i.data("objectID");void 0!==n&&i.children(this._options.items).each($.proxy(function(e,t){var i=$(t).data("objectID");this._structure[n]||(this._structure[n]=[]),this._structure[n].push(i)},this))},this));var e=$.extend(!0,{data:{offset:this._offset,structure:this._structure}},this._additionalParameters);this._proxy.setOption("data",{actionName:"updatePosition",className:this._className,interfaceName:"wcf\\data\\ISortableAction",parameters:e}),this._proxy.sendRequest()},_success:function(e,t,i){null===this._notification&&(this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success.edit"))),this._notification.show()}}),WCF.Popover=Class.extend({_activeElementID:"",_identifier:"",_popoverObj:null,init:function(e){var t=!1;require(["Environment"],function(e){"desktop"!==e.platform()&&(t=!0)}.bind(this)),t||(this._activeElementID="",this._identifier=e,require(["WoltLabSuite/Core/Controller/Popover"],function(t){t.init({attributeName:"legacy",className:e,identifier:this._identifier,legacy:!0,loadCallback:this._legacyLoad.bind(this)})}.bind(this)))},_initContainers:function(){},_legacyLoad:function(e,t){this._activeElementID=e,this._popoverObj=t,this._loadContent()},_insertContent:function(e,t){this._popoverObj.setContent(this._identifier,e,t)}}),WCF.EditableItemList=Class.extend({_allowCustomInput:!1,_className:"",_data:{},_form:null,_itemList:null,_objectID:0,_objectTypeID:0,_search:null,_searchInput:null,init:function(e,t){if(this._itemList=$(e),this._searchInput=$(t),this._data={},!this._itemList.length||!this._searchInput.length)return void console.debug("[WCF.EditableItemList] Item list and/or search input do not exist, aborting.");if(this._objectID=this._getObjectID(),this._objectTypeID=this._getObjectTypeID(),this._itemList.find(".jsEditableItem").click($.proxy(this._click,this)),this._itemList.children("ul").length||$("<ul />").appendTo(this._itemList),this._itemList=this._itemList.children("ul"),this._form=this._itemList.parents("form").submit($.proxy(this._submit,this)),this._allowCustomInput){var i=this;this._searchInput.keydown($.proxy(this._keyDown,this)).keypress($.proxy(this._keyPress,this)).on("paste",function(){setTimeout(function(){i._onPaste()},100)})}this._searchInput.parents(".dropdown").data("preventSubmit",!0)},_keyDown:function(e){return null!==e||this._keyPress(null)},_keyPress:function(e){if(null===e||44===e.charCode||e.charCode===$.ui.keyCode.ENTER||$.browser.mozilla&&e.keyCode===$.ui.keyCode.ENTER){if(null!==e&&e.charCode===$.ui.keyCode.ENTER&&this._search&&-1!==this._search._itemIndex)return!1;var t=$.trim(this._searchInput.val());return e&&44===e.charCode&&(t=t.substring(0,this._searchInput.getCaret())),""===t?!0:(this.addItem({objectID:0,label:t}),e&&44===e.charCode?this._searchInput.val($.trim(this._searchInput.val().substr(this._searchInput.getCaret()))):this._searchInput.val(""),null!==e&&e.stopPropagation(),!1)}return!0},_onPaste:function(){var e=$.trim(this._searchInput.val());e=e.split(",");for(var t=0,i=e.length;t<i;t++){var n=$.trim(e[t]);""!==n&&this.addItem({objectID:0,label:n})}this._searchInput.val("")},load:function(e){},_click:function(e){var t=$(e.currentTarget),i=t.data("objectID"),n=t.data("label");return this._search&&this._search.removeExcludedSearchValue(n),this._removeItem(i,n),t.remove(),e.stopPropagation(),!1},_getObjectID:function(){return 0},_getObjectTypeID:function(){return 0},addItem:function(e){return!(!this._data[e.objectID]||0===e.objectID&&this._allowCustomInput)||($('<li class="badge">'+WCF.String.escapeHTML(e.label)+"</li>").data("objectID",e.objectID).data("label",e.label).appendTo(this._itemList).click($.proxy(this._click,this)),this._search&&this._search.addExcludedSearchValue(e.label),this._addItem(e.objectID,e.label),!0)},clearList:function(){this._itemList.children("li").each($.proxy(function(e,t){var i=$(t);this._search&&this._search.removeExcludedSearchValue(i.data("label")),i.remove(),this._removeItem(i.data("objectID"),i.data("label"))},this))},_submit:function(){this._keyDown(null)},_addItem:function(e,t){this._data[e]=t},_removeItem:function(e,t){delete this._data[e]},getSearchInput:function(){return this._searchInput}}),WCF.Language.Chooser=Class.extend({init:function(e,t,i,n,s,a){require(["WoltLabSuite/Core/Language/Chooser"],function(o){o.init(e,t,i,n,s,a)})}}),WCF.Style={},WCF.UserPanel=Class.extend({_container:null,_didLoad:!1,_link:null,_noItems:"",_revertOnEmpty:!0,init:function(e){if(this._container=$("#"+e),this._didLoad=!1,this._revertOnEmpty=!0,1!=this._container.length)return void console.debug("[WCF.UserPanel] Unable to find container identified by '"+e+"', aborting.");this._convert()},_convert:function(){this._container.addClass("dropdown"),this._link=this._container.children("a").remove();var e=$('<a href="'+this._link.attr("href")+'" class="dropdownToggle">'+this._link.html()+"</a>").appendTo(this._container).click($.proxy(this._click,this)),t=$('<ul class="dropdownMenu" />').appendTo(this._container);$('<li class="jsDropdownPlaceholder"><span>'+WCF.Language.get("wcf.global.loading")+"</span></li>").appendTo(t),this._addDefaultItems(t),this._container.dblclick($.proxy(function(){return window.location=this._link.attr("href"),!1},this)),WCF.Dropdown.initDropdown(e,!1)},_addDefaultItems:function(e){},_addDivider:function(e){$('<li class="dropdownDivider" />').appendTo(e)},_click:function(e){e.preventDefault(),this._didLoad||(new WCF.Action.Proxy({autoSend:!0,data:this._getParameters(),success:$.proxy(this._success,this)}),this._didLoad=!0)},_getParameters:function(){return{}},_success:function(e,t,i){var n=WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify());n.children(".jsDropdownPlaceholder").remove(),e.returnValues&&e.returnValues.template?($(""+e.returnValues.template).prependTo(n),this._updateBadge(e.returnValues.totalCount),this._after(n)):($("<li><span>"+WCF.Language.get(this._noItems)+"</span></li>").prependTo(n),this._updateBadge(0))},_updateBadge:function(e){if(e=parseInt(e)||0){var t=this._container.find(".badge");t.length||(t=$('<span class="badge badgeUpdate" />').appendTo(this._container.children(".dropdownToggle")),t.before(" ")),t.html(e)}else this._container.find(".badge").remove()},_after:function(e){}}),jQuery.fn.extend({wcfDialog:function(e){var t=arguments;return require(["Dom/Util","Ui/Dialog"],function(i,n){var s=i.identify(this[0]);if("close"===e)n.close(s);else if("render"===e)n.rebuild(s);else if("option"===e)3===t.length&&("title"===t[1]&&"string"==typeof t[2]?n.setTitle(s,t[2]):0===t[1].indexOf("on")?n.setCallback(s,t[1],t[2]):"closeConfirmMessage"===t[1]&&null===t[2]&&n.setCallback(s,"onBeforeClose",null));else{this[0].parentNode.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&document.body.appendChild(this[0]);var a=1===t.length&&"object"==typeof t[0]?t[0]:{};n.openStatic(s,null,a),a.hasOwnProperty("title")&&n.setTitle(s,a.title)}}.bind(this)),this}}),$.widget("ui.wcfSlideshow",{_buttonList:null,_count:0,_index:0,_itemList:null,_items:null,_timer:null,_width:0,options:{cycle:!0,cycleInterval:5,itemGap:50},_create:function(){this._itemList=this.element.children("ul"),this._items=this._itemList.children("li"),this._count=this._items.length,this._index=0,this._count>1&&this._initSlideshow()},_initSlideshow:function(){var e=$(this._items.get(0)).outerHeight();this._items.addClass("slideshowItem"),this._width=this.element.css("height",e).innerWidth(),this._itemList.addClass("slideshowItemList").css("left",0),this._items.each($.proxy(function(t,i){$(i).show().css({height:e,left:(this._width+this.options.itemGap)*t,width:this._width})},this)),this.element.css({height:e,width:this._width}).hover($.proxy(this._hoverIn,this),$.proxy(this._hoverOut,this)),this._buttonList=$('<ul class="slideshowButtonList" />').appendTo(this.element);for(var t=0;t<this._count;t++){var i=$('<li><a><span class="icon icon16 fa-circle" /></a></li>').data("index",t).click($.proxy(this._click,this)).appendTo(this._buttonList);0==t&&i.find(".icon").addClass("active")}this._resetTimer(),$(window).resize($.proxy(this._resize,this))},rebuildHeight:function(){var e=$(this._items.get(0)).css("height","auto"),t=e.outerHeight();this._items.css("height",t+"px"),this.element.css("height",t+"px")},_resize:function(){this._width=this.element.css("width","auto").innerWidth(),this._items.each($.proxy(function(e,t){$(t).css({left:(this._width+this.options.itemGap)*e,width:this._width})},this)),this._index--,this.moveTo(null)},_hoverIn:function(){null!==this._timer&&this._timer.stop()},_hoverOut:function(){this._resetTimer()},_resetTimer:function(){if(this.options.cycle){null!==this._timer&&this._timer.stop();var e=this;this._timer=new WCF.PeriodicalExecuter(function(){e.moveTo(null)},1e3*this.options.cycleInterval)}},_click:function(e){this.moveTo($(e.currentTarget).data("index")),this._resetTimer()},moveTo:function(e){this._index=null===e?this._index+1:e,this._index==this._count&&(this._index=0),$(this._buttonList.find(".icon").removeClass("active").get(this._index)).addClass("active"),this._itemList.css("left",this._index*(this._width+this.options.itemGap)*-1),this._trigger("moveTo",null,{index:this._index})},getItem:function(e){return this._items[e]?this._items[e]:null}}),jQuery.fn.extend({datepicker:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);switch(e){case"destroy":window.__wcf_bc_datePicker.destroy(t);break;case"getDate":return window.__wcf_bc_datePicker.getDate(t);case"option":if("onClose"===i[0])return i.length>1?this.datepicker("setOption","onClose",i[1]):function(){};console.warn("datepicker('option') supports only 'onClose'.");break;case"setDate":window.__wcf_bc_datePicker.setDate(t,i[0]);break;case"setOption":"onClose"===i[0]?window.__wcf_bc_datePicker.setCloseCallback(t,i[1]):console.warn("datepicker('setOption') supports only 'onClose'.");break;default:console.debug("Unsupported method '"+e+"' for datepicker()")}return this}}),jQuery.fn.extend({wcfTabs:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);require(["Dom/Util","WoltLabSuite/Core/Ui/TabMenu"],function(n,s){var a=s.getTabMenu(n.identify(t));null!==a&&a[e].apply(a,i)})}}),$.widget("ui.wcfPages",{_api:null,SHOW_LINKS:11,SHOW_SUB_LINKS:20,options:{activePage:1,maxPage:1},_create:function(){require(["WoltLabSuite/Core/Ui/Pagination"],function(e){this._api=new e(this.element[0],{activePage:this.options.activePage,maxPage:this.options.maxPage,callbackShouldSwitch:function(e){return!1!==this._trigger("shouldSwitch",void 0,{nextPage:e})}.bind(this),callbackSwitch:function(e){this._trigger("switched",void 0,{activePage:e})}.bind(this)})}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this._api=null,this.element[0].innerHTML=""},_setOption:function(e,t){if("activePage"==e&&t!=this.options[e]&&t>0&&t<=this.options.maxPage){var i=this._trigger("shouldSwitch",void 0,{nextPage:t});i||void 0!==i?this._api.switchPage(t):this._trigger("notSwitched",void 0,{activePage:t})}return this}}),WCF.Category={},WCF.Category.NestedList=Class.extend({_categories:{},init:function(){var e=this;$(".jsCategory").each(function(t,i){var n=$(i).data("parentCategoryID",null).change($.proxy(e._updateSelection,e));e._categories[n.val()]=n;var s=[];n.parents("li").find(".jsChildCategory").each(function(t,i){var a=$(i).data("parentCategoryID",n.val()).change($.proxy(e._updateSelection,e));e._categories[a.val()]=a,s.push(a.val()),a.is(":checked")&&n.prop("checked","checked")}),n.data("childCategoryIDs",s)})},_updateSelection:function(e){var t=$(e.currentTarget),i=t.data("parentCategoryID");if(t.is(":checked"))null!==i&&this._categories[i].prop("checked","checked");else if(null===i)for(var n=t.data("childCategoryIDs"),s=0,a=n.length;s<a;s++)this._categories[n[s]].prop("checked",!1)}}),WCF.Category.FlexibleCategoryList=Class.extend({_list:null,_categories:{},init:function(e){if(this._list=$("#"+e),this._buildStructure(),this._list.find("input:checked").each(function(){$(this).trigger("change")}),this._list.children("li").length<2)return void this._list.addClass("flexibleCategoryListDisabled")},_buildStructure:function(){var e=this;this._list.find(".jsCategory").each(function(t,i){var n=$(i).change(e._updateSelection.bind(e)),s=parseInt(n.val()),a=[];n.parents("li:eq(0)").find(".jsChildCategory").each(function(t,i){var s=$(i);s.data("parentCategory",n).change(e._updateSelection.bind(e));var o=parseInt(s.val());a.push(s);var r=[];s.parents("li:eq(0)").find(".jsSubChildCategory").each(function(t,i){var n=$(i);n.data("parentCategory",s).change(e._updateSelection.bind(e)),r.push(n)}),e._categories[o]=r}),e._categories[s]=a})},_updateSelection:function(e){var t=$(e.currentTarget),i=parseInt(t.val()),n=t.data("parentCategory");if(t.is(":checked"))n&&(n.prop("checked","checked"),(n=n.data("parentCategory"))&&n.prop("checked","checked"));else{if(this._categories[i])for(var s=0,a=this._categories[i].length;s<a;s++){var o=this._categories[i][s];o.prop("checked",!1);var r=parseInt(o.val());if(this._categories[r])for(var l=0,c=this._categories[r].length;l<c;l++)this._categories[r][l].prop("checked",!1)}if(n){for(var u=parseInt(n.val()),s=0,a=this._categories[u].length;s<a;s++)if(this._categories[u][s].prop("checked"))return;if(n=n.data("parentCategory")){u=parseInt(n.val());for(var s=0,a=this._categories[u].length;s<a;s++)if(this._categories[u][s].prop("checked"))return}}}}}),WCF.Condition={},WCF.Notice={}; })(this);
+(function (window, undefined) { "use strict";function wcfEval(expression){return eval(expression)}!function(){var e=jQuery.fn.data;jQuery.fn.data=function(t,i){var n=[].slice.call(arguments);if(t)switch(typeof t){case"object":for(var s in t)if(s.match(/ID$/)){var a=t[s];delete t[s],s=s.replace(/ID$/,"-id"),t[s]=a}n[0]=t;break;case"string":t.match(/ID$/)&&(n[0]=t.replace(/ID$/,"-id"))}var o=e.apply(this,n);if(void 0===t)for(var s in o)s.match(/Id$/)&&(o[s.replace(/Id$/,"ID")]=o[s],delete o[s]);return o},window.console||(window.console={});for(var t=["log","info","warn","exception","assert","dir","dirxml","trace","group","groupEnd","groupCollapsed","profile","profileEnd","count","clear","time","timeEnd","timeStamp","table","error"],i=0;i<t.length;i++)void 0===console[t[i]]&&(console[t[i]]=function(){});void 0===console.debug&&(console.debug=function(e){console.log(e)})}(),window.shuffle=function(e){for(var t,i,n=e.length;0!==n;)i=Math.floor(Math.random()*n),n-=1,t=e[n],e[n]=e[i],e[i]=t;return this},function(e){var t=navigator.userAgent.toLowerCase(),i=/(chrome)[ \/]([\w.]+)/.exec(t)||/(webkit)[ \/]([\w.]+)/.exec(t)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(t)||/(msie) ([\w.]+)/.exec(t)||t.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(t)||[],n={browser:i[1]||"",version:i[2]||"0"},s={};n.browser&&(s[n.browser]=!0,s.version=n.version),s.chrome?s.webkit=!0:s.webkit&&(s.safari=!0),e.browser=e.browser||{},e.browser=$.extend(e.browser,s),e.browser.touch=!!("ontouchstart"in window)||!!("msMaxTouchPoints"in window.navigator)&&window.navigator.msMaxTouchPoints>0,e.browser.smartphone="bottom"==$("html").css("caption-side"),e.browser.mozilla&&t.match(/trident/)&&(e.browser.mozilla=!1,e.browser.msie=!0),e.browser.iOS=/\((ipad|iphone|ipod);/.test(t),e.browser.iOS&&$("html").addClass("iOS"),e.browser.android=-1!==t.indexOf("android"),e.browser.editor="redactor",e.browser.ckeditor=!1,e.browser.redactor=!0,e.browser.iOS&&(e.fn.focus=function(e,t){return arguments.length>0?this.on("focus",null,e,t):this.trigger("focus")})}(jQuery),null==window.WCF&&(window.WCF={}),$.extend(!0,{removeArrayValue:function(e,t){return $.grep(e,function(e,i){return t!==e})},wcfEscapeID:function(e){return e.replace(/(:|\.)/g,"\\$1")},wcfIsset:function(e){return!!$("#"+$.wcfEscapeID(e)).length},getLength:function(e){var t=0;for(var i in e)e.hasOwnProperty(i)&&t++;return t}}),$.fn.extend({getTagName:function(){return this.length?this.get(0).tagName.toLowerCase():""},getDimensions:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"inner":i={height:this.innerHeight(),width:this.innerWidth()};break;case"outer":i={height:this.outerHeight(),width:this.outerWidth()};break;default:i={height:this.height(),width:this.width()}}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},getOffsets:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"offset":i=this.offset();break;case"position":default:i=this.position()}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},makePositioned:function(e,t){"absolute"!=e&&"fixed"!=e&&(e="absolute");var i=this.getOffsets("position");return this.css({position:e,left:i.left,margin:0,top:i.top}),t&&this.remove().appentTo("body"),this},disable:function(){return this.attr("disabled","disabled")},enable:function(){return this.removeAttr("disabled")},wcfIdentify:function(){return window.bc_wcfDomUtil.identify(this[0])},getCaret:function(){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return-1}else if(!this.is("textarea"))return-1;var e=0,t=this.get(0);if(document.selection){this.focus();var i=document.selection.createRange();i.moveStart("character",-this.val().length),e=i.text.length}else(t.selectionStart||"0"==t.selectionStart)&&(e=parseInt(t.selectionStart));return e},setCaret:function(e){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return!1}else if(!this.is("textarea"))return!1;var t=this.get(0);if(this.focus(),document.selection){var i=document.selection.createRange();i.moveStart("character",e),i.moveEnd("character",0),i.select()}else(t.selectionStart||"0"==t.selectionStart)&&(t.selectionStart=e,t.selectionEnd=e);return!0},wcfDropIn:function(e,t,i){return e||(e="up"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfDropOut:function(e,t,i){return e||(e="down"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfBlindIn:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfBlindOut:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfHighlight:function(e,t){return this.effect("highlight",e,600,t)},wcfFadeIn:function(e,t){return t&&parseInt(t)||(t=200),this.show(WCF.getEffect(this,"fade"),{},t,e)},wcfFadeOut:function(e,t){return t&&parseInt(t)||(t=200),this.hide(WCF.getEffect(this,"fade"),{},t,e)},cssAsNumber:function(e){if(this.length){var t=this.css(e);if(void 0!==t)return parseInt(t.replace(/px$/,""))}return 0},perfectScrollbar:function(e){var t=require("perfect-scrollbar");return this.each(function(){if("object"==typeof e||void 0===e){var i=e;$(this).data("psID")||t.initialize(this,i)}else{var n=e;"update"===n?t.update(this):"destroy"===n&&t.destroy(this)}return jQuery(this)})}}),$.extend(WCF,{activeDialogs:0,_idCounter:0,getRandomID:function(){return window.bc_wcfDomUtil.getUniqueId()},inArray:function(e,t){return-1!=$.inArray(e,t)},getEffect:function(e,t){return e.is("tr")?"highlight":t},getInlineCSS:function(e){var t={},i=e.attr("style");if(!i)return{};i=i.split(";");for(var n=0,s=i.length;n<s;n++){var a=$.trim(i[n]);""!=a&&(a=a.split(":"),t[$.trim(a[0])]=$.trim(a[1]))}return t},revertInlineCSS:function(e,t,i){for(var n=0,s=i.length;n<s;n++){var a=i[n];t[a]?e.css(a,t[a]):e.css(a,"")}},getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)})},base64toBlob:function(e,t,i){t=t||"",i=i||512;for(var n=atob(e),s=[],a=0;a<n.length;a+=i){for(var o=n.slice(a,a+i),r=new Array(o.length),l=0;l<o.length;l++)r[l]=o.charCodeAt(l);var c=new Uint8Array(r);s.push(c)}return new Blob(s,{type:t})},convertLegacyURL:function(e){return e.replace(/^index\.php\/(.*?)\/\?/,function(e,t){for(var i=t.split(/([A-Z][a-z0-9]+)/),n="",s=0,a=i.length;s<a;s++){var o=i[s].trim();o.length&&(n.length&&(n+="-"),n+=o.toLowerCase())}return"index.php?"+n+"/&"})}}),WCF.Browser={_isChrome:null,isChrome:function(){return null===this._isChrome&&(this._isChrome=!1,/chrom(e|ium)/.test(navigator.userAgent.toLowerCase())&&(this._isChrome=!0)),this._isChrome}},WCF.Dropdown={init:function(e){window.bc_wcfSimpleDropdown.initAll()},initDropdown:function(e,t){window.bc_wcfSimpleDropdown.init(e[0],t)},removeDropdown:function(e){window.bc_wcfSimpleDropdown.destroy(e)},initDropdownFragment:function(e,t){window.bc_wcfSimpleDropdown.initFragment(e[0],t[0])},registerCallback:function(e,t){window.bc_wcfSimpleDropdown.registerCallback(e,t)},_toggle:function(e,t){window.bc_wcfSimpleDropdown._toggle(e,t)},toggleDropdown:function(e,t){window.bc_wcfSimpleDropdown._toggle(null,e,null,t)},getDropdown:function(e){var t=window.bc_wcfSimpleDropdown.getDropdown(e);return t?$(t):null},getDropdownMenu:function(e){var t=window.bc_wcfSimpleDropdown.getDropdownMenu(e);return t?$(t):null},setAlignmentByID:function(e){window.bc_wcfSimpleDropdown.setAlignmentById(e)},setAlignment:function(e,t){window.bc_wcfSimpleDropdown.setAlignment(e[0],t[0])},_closeAll:function(){window.bc_wcfSimpleDropdown.closeAll()},close:function(e){window.bc_wcfSimpleDropdown.close(e)},destroy:function(e){window.bc_wcfSimpleDropdown.destroy(e)}},WCF.Dropdown.Interactive={},WCF.Dropdown.Interactive.Handler={_dropdownContainer:null,_dropdownMenus:{},create:function(e,t,i){null===this._dropdownContainer&&(this._dropdownContainer=$('<div class="dropdownMenuContainer" />').appendTo(document.body),WCF.CloseOverlayHandler.addCallback("WCF.Dropdown.Interactive.Handler",$.proxy(this.closeAll,this)));var n=new WCF.Dropdown.Interactive.Instance(this._dropdownContainer,e,t,i);return this._dropdownMenus[t]=n,n},open:function(e){return!!this._dropdownMenus[e]&&(this._dropdownMenus[e].open(),!0)},close:function(e){return!!this._dropdownMenus[e]&&(this._dropdownMenus[e].close(),!0)},closeAll:function(){for(var e in this._dropdownMenus)this._dropdownMenus.hasOwnProperty(e)&&this._dropdownMenus[e].close()},getOpenDropdown:function(){for(var e in this._dropdownMenus)if(this._dropdownMenus.hasOwnProperty(e)&&this._dropdownMenus[e].isOpen())return this._dropdownMenus[e];return null},getDropdown:function(e){return this._dropdownMenus[e]}},WCF.Dropdown.Interactive.Instance=Class.extend({_container:null,_itemList:null,_linkList:null,_options:{},_pointer:null,_triggerElement:null,init:function(e,t,i,n){this._options=n||{},this._triggerElement=t;var s=null;if(!0===n.staticDropdown)this._container=this._triggerElement.find(".interactiveDropdownStatic:eq(0)").data("source",i).click(function(e){e.stopPropagation()});else{this._container=$('<div class="interactiveDropdown" data-source="'+i+'" />').click(function(e){e.stopPropagation()});var a=$('<div class="interactiveDropdownHeader" />').appendTo(this._container);$('<span class="interactiveDropdownTitle">'+n.title+"</span>").appendTo(a),this._linkList=$('<ul class="interactiveDropdownLinks inlineList"></ul>').appendTo(a),s=$('<div class="interactiveDropdownItemsContainer" />').appendTo(this._container),this._itemList=$('<ul class="interactiveDropdownItems" />').appendTo(s),$('<a href="'+n.showAllLink+'" class="interactiveDropdownShowAll">'+WCF.Language.get("wcf.user.panel.showAll")+"</a>").appendTo(this._container)}this._pointer=$('<span class="elementPointer"><span /></span>').appendTo(this._container),require(["Environment"],function(e){"desktop"===e.platform()&&null!==s&&s.perfectScrollbar({suppressScrollX:!0})}.bind(this)),this._container.appendTo(e)},getContainer:function(){return this._container},getItemList:function(){return this._itemList},getLinkList:function(){return this._linkList},open:function(){WCF.Dropdown._closeAll(),this._triggerElement.addClass("open"),this._container.addClass("open"),WCF.System.Event.fireEvent("com.woltlab.wcf.Search","close"),this.render()},close:function(){this._triggerElement.removeClass("open"),this._container.removeClass("open")},isOpen:function(){return this._triggerElement.hasClass("open")},toggle:function(){return this._container.hasClass("open")?(this.close(),!1):(WCF.Dropdown.Interactive.Handler.closeAll(),this.open(),!0)},resetItems:function(){this._itemList.empty(),this.close()},render:function(){require(["Ui/Alignment","Ui/Screen"],function(e,t){t.is("screen-lg")?e.set(this._container[0],this._triggerElement[0],{horizontal:"right",pointer:!0}):this._container.css({bottom:"",left:"",right:"",top:elById("pageHeaderPanel").clientHeight+"px"})}.bind(this))},rebuildScrollbar:function(){require(["Environment"],function(e){if("desktop"===e.platform()){var t=this._itemList.parent();t.perfectScrollbar("destroy"),t.perfectScrollbar({suppressScrollX:!0})}}.bind(this))}}),WCF.Clipboard={init:function(e,t,i,n){require(["EventHandler","WoltLabSuite/Core/Controller/Clipboard"],function(s,a){a.setup({hasMarkedItems:t>0,pageClassName:e,pageObjectId:n});for(var o in i)i.hasOwnProperty(o)&&function(e){s.add("com.woltlab.wcf.clipboard",e,function(t){null!==t.responseData&&i[e].hasOwnProperty(t.responseData.actionName)&&i[e][t.responseData.actionName].triggerEffect(t.responseData.objectIDs)})}(o)})},reload:function(){require(["WoltLabSuite/Core/Controller/Clipboard"],function(e){e.reload()})}},WCF.PeriodicalExecuter=Class.extend({_callback:null,_delay:0,_intervalID:null,_isExecuting:!1,init:function(e,t){if(!$.isFunction(e))return void console.debug("[WCF.PeriodicalExecuter] Given callback is invalid, aborting.");this._callback=e,this._interval=t,this.resume()},_execute:function(){if(!this._isExecuting)try{this._isExecuting=!0,this._callback(this),this._isExecuting=!1}catch(e){throw this._isExecuting=!1,e}},stop:function(){this._intervalID&&clearInterval(this._intervalID)},resume:function(){this.restart()},restart:function(){this._intervalID&&this.stop(),this._intervalID=setInterval($.proxy(this._execute,this),this._interval)},setInterval:function(e){this._interval=e,this.restart()}}),WCF.LoadingOverlayHandler={show:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.show()})},hide:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.hide()})},updateIcon:function(e,t){var i=void 0===t||t?"addClass":"removeClass";e.find(".icon")[i]("fa-spinner"),e.hasClass("icon")&&e[i]("fa-spinner")}},WCF.Action={},WCF.Action.Proxy=Class.extend({_ajaxRequest:null,init:function(e){this._ajaxRequest=null,e=$.extend(!0,{autoSend:!1,data:{},dataType:"json",after:null,init:null,jsonp:"callback",async:!0,failure:null,showLoadingOverlay:!0,success:null,suppressErrors:!1,type:"POST",url:"index.php?ajax-proxy/&t="+SECURITY_TOKEN,aborted:null,autoAbortPrevious:!1},e),"jsonp"===e.dataType?require(["AjaxJsonp"],function(t){t.send(e.url,e.success,e.failure,{parameterName:e.jsonp})}):require(["AjaxRequest"],function(t){this._ajaxRequest=new t({data:e.data,type:e.type,url:e.url,withCredentials:e.url==="index.php?ajax-proxy/&t="+SECURITY_TOKEN,responseType:"json"===e.dataType?"application/json":"",autoAbort:e.autoAbortPrevious,ignoreError:e.suppressErrors,silent:!e.showLoadingOverlay,failure:e.failure,finalize:e.after,success:e.success}),e.autoSend&&this._ajaxRequest.sendRequest()}.bind(this))},sendRequest:function(e){require(["AjaxRequest"],function(t){null!==this._ajaxRequest&&this._ajaxRequest.sendRequest(e)}.bind(this))},abortPrevious:function(){require(["AjaxRequest"],function(e){null!==this._ajaxRequest&&this._ajaxRequest.abortPrevious()}.bind(this))},setOption:function(e,t){require(["AjaxRequest"],function(i){null!==this._ajaxRequest&&this._ajaxRequest.setOption(e,t)}.bind(this))},showLoadingOverlayOnce:function(){},suppressErrors:function(){},_failure:function(e,t,i){},_success:function(e,t,i){},_after:function(){}}),WCF.Action.SimpleProxy=Class.extend({init:function(e,t){this.options=$.extend(!0,{action:"",className:"",elements:null,eventName:"click"},e),this.callbacks=$.extend(!0,{after:null,failure:null,init:null,success:null},t),this.options.elements&&(this.proxy=new WCF.Action.Proxy(this.callbacks),this.options.elements.each($.proxy(function(e,t){$(t).bind(this.options.eventName,$.proxy(this._handleEvent,this))},this)))},_handleEvent:function(e){this.proxy.setOption("data",{actionName:this.options.action,className:this.options.className,objectIDs:[$(e.target).data("objectID")]}),this.proxy.sendRequest()}}),WCF.Action.Delete=Class.extend({_buttonSelector:"",_callback:null,_className:"",_containerSelector:"",_containers:[],init:function(e,t,i){this._containerSelector=t,this._className=e,this._buttonSelector=i||".jsDeleteButton",this._callback=null,this.proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initElements(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Action.Delete"+this._className.hashCode(),$.proxy(this._initElements,this))},_initElements:function(){$(this._containerSelector).each(function(e,t){var i=$(t),n=i.wcfIdentify();if(!WCF.inArray(n,this._containers)){var s=i.find(this._buttonSelector);s.length&&(this._containers.push(n),s.click($.proxy(this._click,this)))}}.bind(this))},_click:function(e){var t=$(e.currentTarget);e.preventDefault(),t.data("confirmMessageHtml")||t.data("confirmMessage")?WCF.System.Confirmation.show(t.data("confirmMessageHtml")?t.data("confirmMessageHtml"):t.data("confirmMessage"),$.proxy(this._execute,this),{target:t},void 0,!!t.data("confirmMessageHtml")):(WCF.LoadingOverlayHandler.updateIcon(t),this._sendRequest(t))},_didTriggerEffect:function(e){},_execute:function(e,t){"cancel"!==e&&(WCF.LoadingOverlayHandler.updateIcon(t.target),this._sendRequest(t.target))},_sendRequest:function(e){this.proxy.setOption("data",{actionName:"delete",className:this._className,interfaceName:"wcf\\data\\IDeleteAction",objectIDs:[$(e).data("objectID")]}),this.proxy.sendRequest()},_success:function(e,t,i){this._callback&&this._callback(e.objectIDs),this.triggerEffect(e.objectIDs)},setCallback:function(e){if("function"!=typeof e)throw new TypeError("[WCF.Action.Delete] Expected a valid callback for '"+this._className+"'.");this._callback=e},triggerEffect:function(e){for(var t in this._containers){var i=$("#"+this._containers[t]),n=i.find(this._buttonSelector);if(WCF.inArray(n.data("objectID"),e)){var s=this;i.wcfBlindOut("up",function(){var e=$(this).remove();s._containers.splice(s._containers.indexOf(e.wcfIdentify()),1),s._didTriggerEffect(e),n.data("eventName")&&WCF.System.Event.fireEvent("com.woltlab.wcf.action.delete",n.data("eventName"),{button:n,container:e})})}}}}),WCF.Action.NestedDelete=WCF.Action.Delete.extend({triggerEffect:function(e){for(var t in this._containers){var i=$("#"+this._containers[t]);if(WCF.inArray(i.find(this._buttonSelector).data("objectID"),e))if(i.has("ol").has("li").length)i.is(":only-child")?i.parent().replaceWith(i.find("> ol")):i.replaceWith(i.find("> ol > li")),this._containers.splice(this._containers.indexOf(i.wcfIdentify()),1),this._didTriggerEffect(i);else{var n=this;i.wcfBlindOut("up",function(){$(this).remove(),n._containers.splice(n._containers.indexOf($(this).wcfIdentify()),1),n._didTriggerEffect($(this))})}}}}),WCF.Action.Toggle=Class.extend({_buttonSelector:".jsToggleButton",_className:"",_containerSelector:"",_containers:[],init:function(e,t,i){this._containerSelector=t,this._className=e,this._buttonSelector=i||".jsToggleButton",this._containers=[];var n={success:$.proxy(this._success,this)};this.proxy=new WCF.Action.Proxy(n),this._initElements(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Action.Toggle"+this._className.hashCode(),$.proxy(this._initElements,this))},_initElements:function(){$(this._containerSelector).each($.proxy(function(e,t){var i=$(t),n=i.wcfIdentify();WCF.inArray(n,this._containers)||(this._containers.push(n),i.find(this._buttonSelector).click($.proxy(this._click,this)))},this))},_click:function(e){var t=$(e.currentTarget);e.preventDefault(),t.data("confirmMessageHtml")||t.data("confirmMessage")?WCF.System.Confirmation.show(t.data("confirmMessageHtml")?t.data("confirmMessageHtml"):t.data("confirmMessage"),$.proxy(this._execute,this),{target:t},void 0,!!t.data("confirmMessageHtml")):(WCF.LoadingOverlayHandler.updateIcon(t),this._sendRequest(t))},_execute:function(e,t){"cancel"!==e&&(WCF.LoadingOverlayHandler.updateIcon(t.target),this._sendRequest(t.target))},_sendRequest:function(e){this.proxy.setOption("data",{actionName:"toggle",className:this._className,interfaceName:"wcf\\data\\IToggleAction",objectIDs:[$(e).data("objectID")]}),this.proxy.sendRequest()},_success:function(e,t,i){this.triggerEffect(e.objectIDs)},triggerEffect:function(e){for(var t in this._containers){var i=$("#"+this._containers[t]),n=i.find(this._buttonSelector);WCF.inArray(n.data("objectID"),e)&&(i.wcfHighlight(),this._toggleButton(i,n))}},_toggleButton:function(e,t){var i="";WCF.LoadingOverlayHandler.updateIcon(t,!1),t.hasClass("fa-square-o")?(t.removeClass("fa-square-o").addClass("fa-check-square-o"),i=t.data("disableTitle")?t.data("disableTitle"):WCF.Language.get("wcf.global.button.disable"),t.attr("title",i)):(t.removeClass("fa-check-square-o").addClass("fa-square-o"),i=t.data("enableTitle")?t.data("enableTitle"):WCF.Language.get("wcf.global.button.enable"),t.attr("title",i)),e.toggleClass("disabled")}}),WCF.Action.Scroll=Class.extend({_callback:null,_reference:null,_target:null,_threshold:0,init:function(e,t,i,n){return this._threshold=parseInt(e),0===this._threshold?void console.debug("[WCF.Action.Scroll] Given threshold is invalid, aborting."):($.isFunction(t)&&(this._callback=t),null===this._callback?void console.debug("[WCF.Action.Scroll] Given callback is invalid, aborting."):(this._reference=$(i||window),this._target=$(n||document),this.start(),void this._scroll()))},_scroll:function(){var e=this._target.height(),t=this._reference.scrollTop();e-(this._reference.height()+t)<this._threshold&&this._callback(this)},start:function(){this._reference.on("scroll",$.proxy(this._scroll,this))},stop:function(){this._reference.off("scroll")}}),WCF.Date={},WCF.Date.Picker={init:function(){}},WCF.Date.Util={gmdate:function(e){var t=e||new Date;return Math.round(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDay(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds())/1e3)},getTimezoneDate:function(e,t){var i=new Date(e),n=6e4*i.getTimezoneOffset();return new Date(e+n+t)}},WCF.Dictionary=Class.extend({_variables:{},init:function(){this._variables={}},add:function(e,t){this._variables[e]=t},addObject:function(e){for(var t in e)this.add(t,e[t])},addDictionary:function(e){e.each($.proxy(function(e){this.add(e.key,e.value)},this))},get:function(e){return this.isset(e)?this._variables[e]:null},isset:function(e){return this._variables.hasOwnProperty(e)},remove:function(e){delete this._variables[e]},each:function(e){if($.isFunction(e))for(var t in this._variables){var i=this._variables[t],n={key:t,value:i};e(n)}},count:function(){return $.getLength(this._variables)},isEmpty:function(){return!this.count()}}),null==window.WCF.Language&&(WCF.Language={add:function(e,t){require(["Language"],function(i){i.add(e,t)})},addObject:function(e){require(["Language"],function(t){t.addObject(e)})},get:function(e,t){throw new Error('Call to deprecated WCF.Language.get("'+e+'")')}}),WCF.Number={round:function(e,t){return t=Math.pow(10,t||0),Math.round(e*t)/t}},WCF.String={addThousandsSeparator:function(e){return String(e).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g,"$1"+WCF.Language.get("wcf.global.thousandsSeparator"))},escapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")},escapeRegExp:function(e){return String(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},formatNumeric:function(e,t){e=String(WCF.Number.round(e,t||2));var i=e.split(".");return e=this.addThousandsSeparator(i[0]),i.length>1&&(e+=WCF.Language.get("wcf.global.decimalPoint")+i[1]),e=e.replace("-","−")},lcfirst:function(e){return String(e).substring(0,1).toLowerCase()+e.substring(1)},ucfirst:function(e){return String(e).substring(0,1).toUpperCase()+e.substring(1)},unescapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">")}},WCF.TabMenu={init:function(){require(["WoltLabSuite/Core/Ui/TabMenu"],function(e){e.setup()})},reload:function(){this.init()}},WCF.Template=Class.extend({init:function(e){var t=new WCF.Dictionary,i=0;e=e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/g,"\\n"),e=e.replace(/\{literal\}(.*?)\{\/literal\}/g,$.proxy(function(e){var i="@@@@@@@@@@@"+Math.random()+"@@@@@@@@@@@";return t.add(i,e.replace(/\{\/?literal\}/g,"")),i},this)),e=e.replace(/\{\*.*?\*\}/g,"");var n=function(e){for(var t=e.split(""),i={},n=!0,s="",a="",o=!1,r=!1,l=!1,c=0,u=t.length;c<u;c++){var h=t[c];n&&"="!=h&&" "!=h?s+=h:n&&"="==h?(n=!1,r=!1,o=!1,l=!1):n||r||o||" "!=h?n||!r||l||"'"!=h?n||r||o||"'"!=h?n||!o||l||'"'!=h?n||r||o||'"'!=h?n||!o&&!r||l||"\\"!=h?n||(l=!1,a+=h):(l=!0,a+=h):(o=!0,a+=h):(o=!1,a+=h):(r=!0,a+=h):(r=!1,a+=h):(n=!0,i[s]=a,a=s="")}if(i[s]=a,o||r||l)throw new Error('Syntax error in parameterList: "'+e+'"');return i},s=function(e){return e.replace(/\\n/g,"\n").replace(/\\\\/g,"\\").replace(/\\'/g,"'")};e=e.replace(/\{(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.escapeHTML("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{#(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.formatNumeric("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{@(\$[^\}]+?)\}/g,function(e,t){return"' + "+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+" + '"}).replace(/\{lang\}(.+?)\{\/lang\}/g,function(e,t){return"' + WCF.Language.get('"+t+"', v) + '"}).replace(/\{include (.+?)\}/g,function(e,t){t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var i=n(t);if(void 0===i.file)throw new Error("Missing file attribute in include-tag");return i.file=i.file.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"' + "+i.file+".fetch(v) + '"}).replace(/\{if (.+?)\}/g,function(e,t){return"';\nif ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{else ?if (.+?)\}/g,function(e,t){return"';\n}\nelse if ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{implode (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in implode-tag");if(void 0===s.item)throw new Error("Missing item attribute in implode-tag");return void 0===s.glue&&(s.glue="', '"),s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\nvar $implode_"+i+" = false;\nfor ($implodeKey_"+i+" in "+s.from+") {\n\tv["+s.item+"] = "+s.from+"[$implodeKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $implodeKey_"+i+";\n":"")+"\tif ($implode_"+i+") $output += "+s.glue+";\n\t$implode_"+i+" = true;\n\t$output += '"}).replace(/\{foreach (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in foreach-tag");if(void 0===s.item)throw new Error("Missing item attribute in foreach-tag");return s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\n$foreach_"+i+" = false;\nfor ($foreachKey_"+i+" in "+s.from+") {\n\t$foreach_"+i+" = true;\n\tbreak;\n}\nif ($foreach_"+i+") {\n\tfor ($foreachKey_"+i+" in "+s.from+") {\n\t\tv["+s.item+"] = "+s.from+"[$foreachKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $foreachKey_"+i+";\n":"")+"\t\t$output += '"}).replace(/\{foreachelse\}/g,"';\n\t}\n}\nelse {\n\t{\n\t\t$output += '").replace(/\{\/foreach\}/g,"';\n\t}\n}\n$output += '").replace(/\{else\}/g,"';\n}\nelse {\n\t$output += '").replace(/\{\/(if|implode)\}/g,"';\n}\n$output += '");for(var a in WCF.Template.callbacks)e=WCF.Template.callbacks[a](e);e=e.replace("{ldelim}","{").replace("{rdelim}","}"),t.each(function(t){e=e.replace(t.key,t.value)}),e="$output += '"+e+"';";try{this.fetch=new Function("v","v = window.$.extend({}, v, { __wcf: window.WCF, __window: window }); var $output = ''; "+e+" return $output;")}catch(t){throw console.debug("var $output = ''; "+e+" return $output;"),t}},fetch:function(e){}}),WCF.Template.callbacks=[],WCF.ToggleOptions=Class.extend({_element:null,_showItems:[],_hideItems:[],_callback:null,init:function(e,t,i,n){this._element=$("#"+e),this._showItems=t,this._hideItems=i,void 0!==n&&(this._callback=n),this._element.click($.proxy(this._toggle,this)),this._toggle()},_toggle:function(){if(this._element.prop("checked")){for(var e=0,t=this._showItems.length;e<t;e++){var i=this._showItems[e];$("#"+i).show()}for(var e=0,t=this._hideItems.length;e<t;e++){var i=this._hideItems[e];$("#"+i).hide()}null!==this._callback&&this._callback()}}}),WCF.Collapsible={},WCF.Collapsible.Simple={init:function(){$(".jsCollapsible").each($.proxy(function(e,t){this._initButton(t)},this))},_initButton:function(e){var t=$(e);t.data("isOpen")||$("#"+t.data("collapsibleContainer")).hide(),t.click($.proxy(this._toggle,this))},_toggle:function(e){var t=$(e.currentTarget),i=t.data("isOpen"),n=$("#"+$.wcfEscapeID(t.data("collapsibleContainer")));return i?(n.stop().wcfBlindOut("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!1):(n.stop().wcfBlindIn("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!0),t.data("isOpen",i),e.stopPropagation(),!1},_toggleImage:function(e){var t=e.find("span.icon");e.data("isOpen")?t.removeClass("fa-chevron-right").addClass("fa-chevron-down"):t.removeClass("fa-chevron-down").addClass("fa-chevron-right")}},WCF.Collapsible.Remote=Class.extend({_className:"",_containers:{},_containerData:{},_proxy:null,init:function(e){this._className=e,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._init(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Collapsible.Remote",$.proxy(this._init,this))},_init:function(e){this._getContainers().each($.proxy(function(e,t){var i=$(t),n=i.wcfIdentify();void 0===this._containers[n]&&(this._containers[n]=i,this._initContainer(n))},this))},_initContainer:function(e){var t=this._getTarget(e),i=this._getButtonContainer(e),n=this._createButton(e,i);this._containerData[e]={button:n,buttonContainer:i,isOpen:this._containers[e].data("isOpen"),target:t},this._containers[e].data("isOpen")||$("#"+e).addClass("jsCollapsed")},_getContainers:function(){},_getTarget:function(e){},_getButtonContainer:function(e){},_createButton:function(e,t){var i=elBySel(".jsStaticCollapsibleButton",t[0]);return null!==i&&i.parentNode===t[0]?(i.classList.remove("jsStaticCollapsibleButton"),i=$(i)):i=$('<span class="collapsibleButton jsTooltip pointer icon icon16 fa-chevron-down" title="'+WCF.Language.get("wcf.global.button.collapsible")+'">').prependTo(t),i.data("containerID",e).click($.proxy(this._toggleContainer,this)),i},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",a=n?"close":"open";this._proxy.setOption("data",{actionName:"loadContainer",className:this._className,interfaceName:"wcf\\data\\ILoadableContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:a},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),$("#"+i).toggleClass("jsCollapsed")},_exchangeIcon:function(e,t){t=t||"spinner",e.removeClass("fa-chevron-down fa-chevron-right fa-spinner").addClass("fa-"+t)},_getObjectID:function(e){return $("#"+e).data("objectID")},_getAdditionalParameters:function(e){return{}},_updateContent:function(e,t,i){this._containerData[e].target.html(t)},_success:function(e,t,i){if(e.returnValues.containerID){var n=e.returnValues.containerID;if(this._containers[n]){this._containerData[n].isOpen=!!e.returnValues.isOpen;var s=e.returnValues.isOpen?"open":"close";this._updateContent(n,$.trim(e.returnValues.content),s)}}}}),WCF.Collapsible.SimpleRemote=WCF.Collapsible.Remote.extend({init:function(e){this._super(e),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1})},_initContainer:function(e){this._super(e),this._containerData[e].isOpen||(this._containerData[e].target.hide(),this._exchangeIcon(this._containerData[e].button,"chevron-right"))},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",a=n?"close":"open";this._proxy.setOption("data",{actionName:"toggleContainer",className:this._className,interfaceName:"wcf\\data\\IToggleContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:a},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),this._exchangeIcon(this._containerData[i].button,"open"===a?"chevron-down":"chevron-right"),"open"===a?this._containerData[i].target.show():this._containerData[i].target.hide(),$("#"+i).toggleClass("jsCollapsed"),this._containerData[i].isOpen="open"===a}}),WCF.User={userID:0,username:"",init:function(e,t){this.userID=e,this.username=t}},WCF.Effect={},WCF.Effect.Scroll=Class.extend({scrollTo:function(e,t,i){if(!e.length)return!0;var n=e.getOffsets("offset").top,s=$(document).height(),a=$(window).height();return n>s-a&&(n=s-a)<0&&(n=0),!0===i?$("html,body").scrollTop(n):$("html,body").animate({scrollTop:n},400,function(e,t,i,n,s){return-n*((t=t/s-1)*t*t*t-1)+i}),!1}}),WCF.CloseOverlayHandler={addCallback:function(e,t){
+require(["Ui/CloseOverlay"],function(i){i.add(e,t)})},removeCallback:function(e){require(["Ui/CloseOverlay"],function(t){t.remove(e)})},forceExecution:function(){require(["Ui/CloseOverlay"],function(e){e.execute()})}},WCF.DOMNodeInsertedHandler={addCallback:function(e,t){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.add("__legacy__",t)})},_executeCallbacks:function(){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.trigger()})},execute:function(){this._executeCallbacks()}},WCF.DOMNodeRemovedHandler={_callbacks:new WCF.Dictionary,_isExecuting:!1,_isListening:!1,addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.DOMNodeRemovedHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){if(window.MutationObserver){new MutationObserver(function(e){var t=!1;e.forEach(function(e){e.removedNodes.length&&(t=!0)}.bind(this)),t&&this._executeCallbacks({})}.bind(this)).observe(document.body,{childList:!0,subtree:!0})}else $(document).bind("DOMNodeRemoved",$.proxy(this._executeCallbacks,this));this._isListening=!0}},_executeCallbacks:function(e){this._isExecuting||(this._isExecuting=!0,this._callbacks.each(function(t){t.value(e)}),this._isExecuting=!1)}},WCF.Option={},WCF.Option.Handler=Class.extend({init:function(){this._initOptions(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Option.Handler",$.proxy(this._initOptions,this))},_initOptions:function(){$(".jsEnablesOptions").each($.proxy(this._initOption,this))},_initOption:function(e,t){this._change(t),$(t).change($.proxy(this._handleChange,this))},_handleChange:function(e){this._change($(e.target))},_change:function(option){option=$(option);var disableOptions=eval(option.data("disableOptions")),enableOptions=eval(option.data("enableOptions"));switch(option.getTagName()){case"input":switch(option.attr("type")){case"checkbox":this._execute(option.prop("checked"),disableOptions,enableOptions);break;case"radio":if(option.prop("checked")){var isActive=!0;option.data("isBoolean")&&1!=option.val()&&(isActive=!1),this._execute(isActive,disableOptions,enableOptions)}}break;case"select":var $value=option.val(),relevantDisableOptions=[],relevantEnableOptions=[];if(disableOptions.length>0)for(var $index in disableOptions){var $item=disableOptions[$index];$item.value==$value?relevantDisableOptions.push($item.option):relevantEnableOptions.push($item.option)}if(enableOptions.length>0)for(var $index in enableOptions){var $item=enableOptions[$index];$item.value==$value?relevantEnableOptions.push($item.option):relevantDisableOptions.push($item.option)}this._execute(!0,relevantDisableOptions,relevantEnableOptions)}},_execute:function(e,t,i){if(t.length>0)for(var n=0,s=t.length;n<s;n++){var a=t[n];if($.wcfIsset(a))this._enableOption(a,!e);else{var o=$("."+a+"Input");o.length&&this._enableOptions(o.children("dd").find("input, select, textarea"),!e)}}if(i.length>0)for(var n=0,s=i.length;n<s;n++){var a=i[n];if($.wcfIsset(a))this._enableOption(a,e);else{var o=$("."+a+"Input");o.length&&this._enableOptions(o.children("dd").find("input, select, textarea"),e)}}},_enableOption:function(e,t){this._enableOptionElement($("#"+$.wcfEscapeID(e)),t)},_enableOptionElement:function(e,t){e=$(e);var i=e.getTagName();if("select"==i||"input"==i&&("checkbox"==e.attr("type")||"file"==e.attr("type")||"radio"==e.attr("type"))){if("input"===i&&"radio"===e[0].type?e[0].checked||(t?e.enable():e.disable()):t?e.enable():e.disable(),e.parents(".optionTypeBoolean:eq(0)")){var n=e.wcfIdentify().replace(/\./g,"\\."),s=$("#"+n+"_no");t?s.enable():s.disable();var a=$("#"+n+"_never");a.length&&(t?a.enable():a.disable())}}else t?e.removeAttr("readonly"):e.attr("readonly",!0);t?e.closest("dl").removeClass("disabled"):e.closest("dl").addClass("disabled")},_enableOptions:function(e,t){for(var i=0,n=e.length;i<n;i++)this._enableOptionElement(e[i],t)}}),WCF.PageVisibilityHandler={_callbacks:new WCF.Dictionary,_isListening:!1,_hiddenFieldName:"",addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.PageVisibilityHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){var e=null;void 0!==document.hidden?(this._hiddenFieldName="hidden",e="visibilitychange"):void 0!==document.mozHidden?(this._hiddenFieldName="mozHidden",e="mozvisibilitychange"):void 0!==document.msHidden?(this._hiddenFieldName="msHidden",e="msvisibilitychange"):void 0!==document.webkitHidden&&(this._hiddenFieldName="webkitHidden",e="webkitvisibilitychange"),null===e?console.debug("[WCF.PageVisibilityHandler] This browser does not support the page visibility API."):$(document).on(e,$.proxy(this._executeCallbacks,this)),this._isListening=!0}},_executeCallbacks:function(e){if(!this._isExecuting){this._isExecuting=!0;var t=document[this._hiddenFieldName];this._callbacks.each(function(e){e.value(t)}),this._isExecuting=!1}}},WCF.Table={},WCF.Table.EmptyTableHandler=Class.extend({_options:{},_rowClassName:"",init:function(e,t,i){this._rowClassName=t,this._tableContainer=e,this._options=$.extend(!0,{emptyMessage:null,emptyMessageHtml:null,messageType:"info",refreshPage:!1,updatePageNumber:!1,isTable:0!==this._tableContainer.find("table").length},i||{}),WCF.DOMNodeRemovedHandler.addCallback("WCF.Table.EmptyTableHandler."+t,$.proxy(this._remove,this))},_getRowCount:function(){return this._tableContainer.find((this._options.isTable?"table tr.":".tabularList .")+this._rowClassName).length},_handleEmptyTable:function(){if(this._options.emptyMessage)this._tableContainer.replaceWith($("<p />").addClass(this._options.messageType).text(this._options.emptyMessage));else if(this._options.emptyMessageHtml)this._tableContainer.replaceWith($("<p />").addClass(this._options.messageType).html(this._options.emptyMessageHtml));else if(this._options.refreshPage)if(this._options.updatePageNumber){var e=window.location.href.match(/(\?|&)pageNo=(\d+)/g);if(e){var t=e[e.length-1].match(/\d+/g);this._options.updatePageNumber>0?t++:t--,window.location=window.location.href.replace(e[e.length-1],e[e.length-1][0]+"pageNo="+t)}}else window.location.reload();else this._tableContainer.remove()},_remove:function(e){if($.getLength(e)){var t=$(e.target);if(t.hasClass(this._rowClassName))if(this._options.isTable){var i=t.parents("tbody:eq(0)");1==i.children("tr").length&&this._handleEmptyTable()}else 1===this._getRowCount()&&this._handleEmptyTable()}else this._getRowCount()||this._handleEmptyTable()}}),WCF.Search={},WCF.Search.Base=Class.extend({_callback:null,_caretAt:-1,_className:"",_commaSeperated:!1,_delay:0,_excludedSearchValues:[],_itemCount:0,_itemIndex:-1,_lastValue:"",_list:null,_oldSearchString:[],_proxy:null,_searchInput:null,_triggerLength:3,_timer:null,init:function(e,t,i,n,s){return null===t||void 0===t||$.isFunction(t)?(this._callback=t||null,this._caretAt=-1,this._delay=0,this._excludedSearchValues=[],this._lastValue="",i&&(this._excludedSearchValues=i),this._searchInput=$(e),this._searchInput.length?(this._searchInput.keydown($.proxy(this._keyDown,this)).keyup($.proxy(this._keyUp,this)).wrap('<span class="dropdown" />'),$.browser.mozilla&&$.browser.touch&&this._searchInput.on("input",$.proxy(this._keyUp,this)),this._list=$('<ul class="dropdownMenu" />').insertAfter(this._searchInput),this._commaSeperated=!!n,this._oldSearchString=[],this._itemCount=0,this._itemIndex=-1,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!0===s,success:$.proxy(this._success,this),autoAbortPrevious:!0}),this._searchInput.is("input")&&this._searchInput.attr("autocomplete","off"),this._searchInput.blur($.proxy(this._blur,this)),void WCF.Dropdown.initDropdownFragment(this._searchInput.parent(),this._list)):void console.debug("[WCF.Search.Base] Selector '"+e+"' for search input is invalid, aborting.")):void console.debug("[WCF.Search.Base] The given callback is invalid, aborting.")},_blur:function(){var e=this;new WCF.PeriodicalExecuter(function(t){e._list.is(":visible")&&e._clearList(!1),t.stop()},250)},_keyDown:function(e){if(e.which===$.ui.keyCode.ENTER){var t=this._searchInput.parents(".dropdown");t.data("disableAutoFocus")?-1!==this._itemIndex&&e.preventDefault():(t.data("preventSubmit")||-1!==this._itemIndex)&&e.preventDefault()}},_keyUp:function(e){switch(e.which){case 37:case 39:return;case 38:return void this._selectPreviousItem();case 40:return void this._selectNextItem();case 13:return this._selectElement(e)}var t=this._getSearchString(e);if(""===t)this._clearList(!1);else if(t.length>=this._triggerLength){if(this._lastValue===t)return;this._lastValue=t;var i={data:{excludedSearchValues:this._excludedSearchValues,searchString:t}};if(this._delay){null!==this._timer&&this._timer.stop();var n=this;this._timer=new WCF.PeriodicalExecuter(function(){n._queryServer(i),n._timer.stop(),n._timer=null},this._delay)}else this._queryServer(i)}else this._clearList(!1)},_queryServer:function(e){this._searchInput.parents(".searchBar").addClass("loading"),this._proxy.setOption("data",{actionName:"getSearchResultList",className:this._className,interfaceName:"wcf\\data\\ISearchAction",parameters:this._getParameters(e)}),this._proxy.sendRequest()},setDelay:function(e){this._delay=e},_selectNextItem:function(){0!==this._itemCount&&(this._itemIndex++,this._itemIndex===this._itemCount&&(this._itemIndex=0),this._highlightSelectedElement())},_selectPreviousItem:function(){0!==this._itemCount&&(this._itemIndex--,-1===this._itemIndex&&(this._itemIndex=this._itemCount-1),this._highlightSelectedElement())},_highlightSelectedElement:function(){this._list.find("li").removeClass("dropdownNavigationItem"),this._list.find("li:eq("+this._itemIndex+")").addClass("dropdownNavigationItem")},_selectElement:function(e){return 0===this._itemCount||(this._list.find("li.dropdownNavigationItem").trigger("click"),!1)},_getSearchString:function(e){var t=$.trim(this._searchInput.val());if(this._commaSeperated){if((e.keyCode||e.which)==$.ui.keyCode.COMMA)return"";for(var i=t.split(","),n=i.length,s=0;s<n;s++)i[s]=$.trim(i[s]);for(var s=0;s<n;s++){var a=i[s];if(!this._oldSearchString[s]){t=a;break}if(a!=this._oldSearchString[s]){t=a,this._caretAt=s;break}}this._oldSearchString=i}return t},_getParameters:function(e){return e},_success:function(e,t,i){if(this._clearList(!1),this._searchInput.parents(".searchBar").removeClass("loading"),$.getLength(e.returnValues))for(var n in e.returnValues){var s=e.returnValues[n];this._createListItem(s)}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var a=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(a).hasClass("dropdownOpen")||(WCF.Dropdown.toggleDropdown(a,!0),this._openDropdown()),this._itemIndex=-1,WCF.Dropdown.getDropdown(a).data("disableAutoFocus")||this._selectNextItem()},_openDropdown:function(){},_handleEmptyResult:function(){return!1},_createListItem:function(e){var t=$("<li><span>"+WCF.String.escapeHTML(e.label)+"</span></li>").appendTo(this._list);return t.data("objectID",e.objectID).data("label",e.label).click($.proxy(this._executeCallback,this)),this._itemCount++,t},_executeCallback:function(e){var t=!1,i=$(e.currentTarget);if(this._commaSeperated){var n=i.data("label");this._oldSearchString[this._caretAt]=n,this._searchInput.val(this._oldSearchString.join(", ")),$.browser.webkit&&this._searchInput.css({display:"block"});var s=this._searchInput.val().toLowerCase().indexOf(n.toLowerCase())+n.length;this._searchInput.focus().setCaret(s)}else null===this._callback?this._searchInput.val(i.data("label")):t=!0===this._callback(i.data());this._clearList(t)},_clearList:function(e){e&&!this._commaSeperated&&this._searchInput.val(""),WCF.Dropdown.getDropdown(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),WCF.Dropdown.getDropdownMenu(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),this._list.end().empty(),WCF.CloseOverlayHandler.removeCallback("WCF.Search.Base"),this._itemCount=0,this._itemIndex=-1},addExcludedSearchValue:function(e){WCF.inArray(e,this._excludedSearchValues)||this._excludedSearchValues.push(e)},removeExcludedSearchValue:function(e){var t=$.inArray(e,this._excludedSearchValues);-1!=t&&this._excludedSearchValues.splice(t,1)}}),WCF.Search.User=WCF.Search.Base.extend({_className:"wcf\\data\\user\\UserAction",_includeUserGroups:!1,init:function(e,t,i,n,s){this._includeUserGroups=i,this._super(e,t,n,s)},_getParameters:function(e){return e.data.includeUserGroups=this._includeUserGroups?1:0,e},_createListItem:function(e){var t=this._super(e),i=null;if(e.icon?i=$(e.icon):this._includeUserGroups&&"group"===e.type&&(i=$('<span class="icon icon16 fa-users" />')),i){var n=t.find("span").detach(),s=$("<div />").addClass("box16").appendTo(t);s.append(i),s.append($("<div />").append(n))}return t.data("type",e.type),t}}),WCF.System={},WCF.System.Dependency={},WCF.System.Dependency.Manager={_callbacks:{},_loaded:[],_setupCallbacks:{},register:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Callback for identifier '"+e+"' is invalid, aborting.");WCF.inArray(e,this._loaded)?setTimeout(function(){t()},1):(this._callbacks[e]||(this._callbacks[e]=[]),this._callbacks[e].push(t))},setup:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Setup callback for identifier '"+e+"' is invalid, aborting.");this._setupCallbacks[e]||(this._setupCallbacks[e]=[]),this._setupCallbacks[e].push(t)},invoke:function(e){if(this._setupCallbacks[e]){for(var t=0,i=this._setupCallbacks[e].length;t<i;t++)this._setupCallbacks[e][t]();delete this._setupCallbacks[e]}if(this._loaded.push(e),this._callbacks[e]){for(var t=0,i=this._callbacks[e].length;t<i;t++)this._callbacks[e][t]();delete this._callbacks[e]}},reset:function(e){var t=this._loaded.indexOf(e);-1!==t&&this._loaded.splice(t,1)}},WCF.System.FlexibleMenu={init:function(){},registerMenu:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.register(e)})},rebuild:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.rebuild(e)})}},WCF.System.Mobile={},WCF.System.ObjectStore={_objects:{},add:function(e,t){void 0===this._objects[e]&&(this._objects[e]=[]),this._objects[e].push(t)},invoke:function(e,t){if(this._objects[e])for(var i=0;i<this._objects[e].length;i++)t(this._objects[e][i])}},WCF.System.Captcha={_registeredCaptchas:[],addCallback:function(e,t){require(["WoltLabSuite/Core/Controller/Captcha"],function(i){try{i.add(e,t),this._registeredCaptchas.push(e)}catch(e){if(e instanceof TypeError)return void console.debug("[WCF.System.Captcha] Given callback is no function")}}.bind(this))},getData:function(e){var t;if(-1===this._registeredCaptchas.indexOf(e))return t;var i=require("WoltLabSuite/Core/Controller/Captcha");try{t=i.getData(e)}catch(t){console.debug('[WCF.System.Captcha] Unknow captcha id "'+e+'"')}return t},removeCallback:function(e){require(["WoltLabSuite/Core/Controller/Captcha"],function(t){try{t.delete(e),this._registeredCaptchas.splice(this._registeredCaptchas.indexOf(item),1)}catch(e){}}.bind(this))}},WCF.System.Page={},WCF.System.Notification=Class.extend({_cssClassNames:"",_message:"",init:function(e,t){this._cssClassNames=t||"",this._message=e||""},show:function(e,t,i,n){require(["Ui/Notification"],function(t){t.show(i||this._message,e,n||this._cssClassNames)}.bind(this))}}),WCF.System.Confirmation={show:function(e,t,i,n,s){if("object"==typeof n){var a=$("<div />");a.append(n),n=a.html()}require(["Ui/Confirmation"],function(a){a.show({legacyCallback:t,message:e,parameters:i,template:n||"",messageIsHtml:!0===s})})}},WCF.System.DisableScrolling={_depth:0,_oldOverflow:null,disable:function(){$.browser.touch||(0===this._depth&&(this._oldOverflow=$(document.body).css("overflow"),$(document.body).css("overflow","hidden")),this._depth++)},enable:function(){0!==this._depth&&0===--this._depth&&$(document.body).css("overflow",this._oldOverflow)}},WCF.System.DisableZoom={_depth:0,_oldViewportSettings:null,disable:function(){if(0===this._depth){var e=$("meta[name=viewport]");this._oldViewportSettings=e.attr("content"),e.attr("content",this._oldViewportSettings+",maximum-scale=1")}this._depth++},enable:function(){0!==this._depth&&0===--this._depth&&$("meta[name=viewport]").attr("content",this._oldViewportSettings)}},WCF.System.Fullscreen={enterFullscreen:function(e){e.requestFullscreen?e.requestFullscreen():e.msRequestFullscreen?e.msRequestFullscreen():e.mozRequestFullScreen?e.mozRequestFullScreen():e.webkitRequestFullscreen&&e.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)},toggleFullscreen:function(e){null===this.getFullscreenElement()?this.enterFullscreen(e):this.exitFullscreen()},getFullscreenElement:function(){return document.fullscreenElement?document.fullscreenElement:document.mozFullScreenElement?document.mozFullScreenElement:document.webkitFullscreenElement?document.webkitFullscreenElement:document.msFullscreenElement?document.msFullscreenElement:null},exitFullscreen:function(){document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen()},isSupported:function(){return!!(document.documentElement.requestFullscreen||document.documentElement.msRequestFullscreen||document.documentElement.mozRequestFullScreen||document.documentElement.webkitRequestFullscreen)}},WCF.System.PageNavigation={init:function(e,t){require(["WoltLabSuite/Core/Ui/Page/JumpTo"],function(i){for(var n=elBySelAll(e),s=0,a=n.length;s<a;s++)i.init(n[s],t)})}},WCF.System.KeepAlive=Class.extend({init:function(e){new WCF.PeriodicalExecuter(function(e){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"keepAlive",className:"wcf\\data\\session\\SessionAction"},failure:function(){e.stop()},showLoadingOverlay:!1,success:function(e){WCF.System.PushNotification.executeCallbacks(e)},suppressErrors:!0})},1e3*e)}}),WCF.System.PushNotification={_callbacks:{},addCallback:function(e,t){void 0===this._callbacks[e]&&(this._callbacks[e]=[]),this._callbacks[e].push(t)},executeCallbacks:function(e){for(var t in e.returnValues)if(void 0!==this._callbacks[t])for(var i=0;i<this._callbacks[t].length;i++)this._callbacks[t][i](e.returnValues[t])}},WCF.System.Event={addListener:function(e,t,i){return window.__wcf_bc_eventHandler.add(e,t,i)},removeListener:function(e,t,i){return window.__wcf_bc_eventHandler.remove(e,t,i)},removeAllListeners:function(e,t){return window.__wcf_bc_eventHandler.removeAll(e,t)},fireEvent:function(e,t,i){window.__wcf_bc_eventHandler.fire(e,t,i)}},WCF.System.Worker=Class.extend({_aborted:!1,_actionName:"",_callback:null,_className:"",_dialog:null,_proxy:null,_title:"",init:function(e,t,i,n,s){this._aborted=!1,this._actionName=e,this._callback=s||null,this._className=t,this._dialog=null,this._proxy=new WCF.Action.Proxy({autoSend:!0,data:{actionName:this._actionName,className:this._className,parameters:n||{}},showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._title=i},_success:function(e){if(null===this._dialog&&(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.wcfDialog({closeConfirmMessage:WCF.Language.get("wcf.worker.abort.confirmMessage"),closeViaModal:!1,onClose:$.proxy(function(){this._aborted=!0,this._proxy.abortPrevious(),window.location.reload()},this),title:this._title})),!this._aborted)if(e.returnValues.template&&this._dialog.html(e.returnValues.template),this._dialog.find("progress").attr("value",e.returnValues.progress).text(e.returnValues.progress+"%").next("span").text(e.returnValues.progress+"%"),e.returnValues.progress<100){var t=e.returnValues.parameters||{};t.loopCount=e.returnValues.loopCount,this._proxy.setOption("data",{actionName:this._actionName,className:this._className,parameters:t}),this._proxy.sendRequest()}else if(null!==this._callback)this._callback(this,e);else{this._dialog.find(".fa-spinner").removeClass("fa-spinner").addClass("fa-check green"),this._dialog.find(".contentHeader h1").text(WCF.Language.get("wcf.global.worker.completed"));var i=$('<div class="formSubmit" />').appendTo(this._dialog);$('<button class="buttonPrimary">'+WCF.Language.get("wcf.global.button.next")+"</button>").appendTo(i).focus().click(function(){e.returnValues.redirectURL?window.location=e.returnValues.redirectURL:window.location.reload()}),this._dialog.wcfDialog("render")}}}),WCF.InlineEditor=Class.extend({_callbacks:[],_dropdowns:{},_elements:{},_notification:null,_options:[],_proxy:null,_triggerElements:{},_updateData:[],_elementSelector:null,_quickOption:null,init:function(e){if(this._elementSelector=e,$(e).length){this._setOptions();for(var t=0,i=this._options.length;t<i;t++)if(this._options[t].isQuickOption){this._quickOption=this._options[t].optionName;break}this.rebuild(),WCF.DOMNodeInsertedHandler.addCallback("WCF.InlineEditor"+this._elementSelector.hashCode(),$.proxy(this.rebuild,this)),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),WCF.CloseOverlayHandler.addCallback("WCF.InlineEditor",$.proxy(this._closeAll,this)),this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success"),"success")}},rebuild:function(){var e=$(this._elementSelector),t=this;e.each(function(e,i){var n=$(i),s=n.wcfIdentify();if(void 0===t._elements[s]){var a=t._getTriggerElement(n);if(null===a||1!==a.length)return;a.on(WCF_CLICK_EVENT,$.proxy(t._show,t)).data("elementID",s),this._quickOption&&a.disableSelection().data("optionName",$quickOption).dblclick($.proxy(t._click,t)),t._elements[s]=n}})},_closeAll:function(){for(var e in this._elements)this._hide(e)},_setOptions:function(){this._options=[]},registerCallback:function(e){$.isFunction(e)&&this._callbacks.push(e)},_getTriggerElement:function(e){return null},_show:function(e){e.preventDefault();var t=$(e.currentTarget).data("elementID"),i=null;if(!this._dropdowns[t]){this._triggerElements[t]=i=this._getTriggerElement(this._elements[t]).addClass("dropdownToggle");var n=i[0].parentNode;n&&"LI"===n.nodeName&&1===n.childElementCount?n.classList.add("dropdown"):i.wrap('<span class="dropdown" />'),this._dropdowns[t]=$('<ul class="dropdownMenu" />').insertAfter(i)}this._dropdowns[t].empty();for(var s=!1,a="",o=0,r=this._options.length;o<r;o++){var l=this._options[o];if("divider"===l.optionName)""!==a&&"divider"!==a&&($('<li class="dropdownDivider" />').appendTo(this._dropdowns[t]),a=l.optionName);else if(this._validate(t,l.optionName)||this._validateCallbacks(t,l.optionName)){var c=$("<li><span>"+l.label+"</span></li>").appendTo(this._dropdowns[t]);c.data("elementID",t).data("optionName",l.optionName).data("isQuickOption",!!l.isQuickOption).click($.proxy(this._click,this)),s=!0,a=l.optionName}}if(s){var u=this._dropdowns[t].children().last();u.hasClass("dropdownDivider")&&u.remove();var h=null,d=0;if(this._dropdowns[t].children().each(function(e,t){var i=$(t);i.hasClass("dropdownDivider")||(i.data("isQuickOption")?h=i:d++)}),!d)return h.trigger("click"),this._triggerElements[t]&&WCF.Dropdown.close(this._triggerElements[t].parents(".dropdown").wcfIdentify()),!1}return null!==i&&WCF.Dropdown.initDropdown(i,e.originalEvent||e),!1},_validate:function(e,t){return!1},_validateCallbacks:function(e,t){var i=this._callbacks.length;if(i)for(var n=0;n<i;n++)if(this._callbacks[n].validate(this._elements[e],t))return!0;return!1},_success:function(e,t,i){this._updateData.length&&(this._updateState(e),this._updateData=[])},_updateState:function(e){},_click:function(e){var t=$(e.currentTarget),i=t.data("elementID"),n=t.data("optionName");this._execute(i,n)||this._executeCallback(i,n),this._hide(i)},_execute:function(e,t){return!1},_executeCallback:function(e,t){var i=this._callbacks.length;if(i)for(var n=0;n<i;n++)if(this._callbacks[n].execute(this._elements[e],t))return!0;return!1},_hide:function(e){this._dropdowns[e]&&this._dropdowns[e].empty().removeClass("dropdownOpen")}}),WCF.Upload=Class.extend({_name:"__files[]",_buttonSelector:null,_fileListSelector:null,_fileUpload:null,_className:"",_iframe:null,_internalFileID:0,_options:{},_uploadMatrix:[],_supportsAJAXUpload:!0,_overlay:null,init:function(e,t,i,n){this._buttonSelector=e,this._fileListSelector=t,this._className=i,this._internalFileID=0,this._options=$.extend(!0,{action:"upload",multiple:!1,url:"index.php?ajax-upload/&t="+SECURITY_TOKEN},n||{}),this._options.url=WCF.convertLegacyURL(this._options.url),0===this._options.url.indexOf("index.php")&&(this._options.url=WSC_API_URL+this._options.url);var s=new XMLHttpRequest;this._supportsAJAXUpload=s&&"upload"in s&&"onprogress"in s.upload,this._createButton()},_createButton:function(){if(this._supportsAJAXUpload){this._fileUpload=$('<input type="file" name="'+this._name+'" '+(this._options.multiple?'multiple="true" ':"")+"/>"),this._fileUpload.change($.proxy(this._upload,this));var e=$('<p class="button uploadButton"><span>'+WCF.Language.get("wcf.global.button.upload")+"</span></p>");elAttr(e[0],"role","button"),elAttr(e[0],"tabindex","0"),e.prepend(this._fileUpload)}else{var e=$('<p class="button uploadFallbackButton"><span>'+WCF.Language.get("wcf.global.button.upload")+"</span></p>");elAttr(e[0],"role","button"),elAttr(e[0],"tabindex","0"),e.click($.proxy(this._showOverlay,this))}this._insertButton(e)},_insertButton:function(e){this._buttonSelector.prepend(e)},_removeButton:function(){var e=".uploadButton";this._supportsAJAXUpload||(e=".uploadFallbackButton"),this._buttonSelector.find(e).remove()},_upload:function(e,t,i,n){var s=null,a=[];if(void 0!==n)a=n;else if(t)a.push(t);else if(i){var o="";switch(i.type){case"image/png":o=".png";break;case"image/jpeg":o=".jpg";break;case"image/gif":o=".gif"}a.push({name:"pasted-from-clipboard"+o})}else a=this._fileUpload.prop("files");if(a.length){var r=new FormData;if(s=this._createUploadMatrix(a),!this._uploadMatrix[s].length)return null;for(var l=0,c=a.length;l<c;l++)if(this._uploadMatrix[s][l]){var u=this._uploadMatrix[s][l].data("internalFileID");i?r.append("__files["+u+"]",i,a[l].name):r.append("__files["+u+"]",a[l],a[l].name)}r.append("actionName",this._options.action),r.append("className",this._className);var h=this._getParameters();for(var d in h)r.append("parameters["+d+"]",h[d]);var p=this;$.ajax({type:"POST",url:this._options.url,enctype:"multipart/form-data",data:r,contentType:!1,processData:!1,success:function(e,t,i){p._success(s,e)},error:$.proxy(this._error,this),xhr:function(){var e=$.ajaxSettings.xhr();return e&&e.upload.addEventListener("progress",function(e){p._progress(s,e)},!1),e},xhrFields:{withCredentials:!0}})}return s},_createUploadMatrix:function(e){if(e.length){var t=this._uploadMatrix.length;this._uploadMatrix[t]=[];for(var i=0,n=e.length;i<n;i++){var s=e[i],a=this._initFile(s);a.hasClass("uploadFailed")||(a.data("filename",s.name).data("internalFileID",this._internalFileID++),this._uploadMatrix[t][i]=a)}return t}return null},_success:function(e,t){},_error:function(e,t,i){},_progress:function(e,t){var i=Math.round(100*t.loaded/t.total);for(var n in this._uploadMatrix[e])this._uploadMatrix[e][n].find("progress").attr("value",i)},_getParameters:function(){return{}},_initFile:function(e){return $("<li>"+e.name+" ("+e.size+')<progress max="100" /></li>').appendTo(this._fileListSelector)},_showOverlay:function(){if(null===this._iframe&&(this._iframe=$('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body)),!this._overlay){this._overlay=$('<div><form enctype="multipart/form-data" method="post" action="'+this._options.url+'" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);var e=this._overlay.find("form");$('<dl class="wide"><dd><input type="file" id="__fileUpload" name="'+this._name+'" '+(this._options.multiple?'multiple="true" ':"")+"/></dd></dl>").appendTo(e),$('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo(e),$('<input type="hidden" name="isFallback" value="1" />').appendTo(e),$('<input type="hidden" name="actionName" value="'+this._options.action+'" />').appendTo(e),$('<input type="hidden" name="className" value="'+this._className+'" />').appendTo(e);var t=this._getParameters();for(var i in t)$('<input type="hidden" name="'+i+'" value="'+t[i]+'" />').appendTo(e);e.submit($.proxy(function(){var e={name:this._getFilename(),size:""},t=this._createUploadMatrix([e]),i=this;this._iframe.data("loading",!0).off("load").load(function(){i._evaluateResponse(t)}),this._overlay.wcfDialog("close")},this))}this._overlay.wcfDialog({title:WCF.Language.get("wcf.global.button.upload")})},_evaluateResponse:function(e){var t=$.parseJSON(this._iframe.contents().find("pre").html());this._success(e,t)},_getFilename:function(){return $("#__fileUpload").val().split("\\").pop()}}),WCF.Upload.Parallel=WCF.Upload.extend({init:function(e,t,i,n){n=$.extend(!0,n||{},{multiple:!0}),this._super(e,t,i,n)},_upload:function(){for(var e=this._fileUpload.prop("files"),t=0,i=e.length;t<i;t++){var n=e[t],s=new FormData,a=this._createUploadMatrix(n);if(this._uploadMatrix[a].length){s.append("__files["+a+"]",n),s.append("actionName",this._options.action),s.append("className",this._className);var o=this._getParameters();for(var r in o)s.append("parameters["+r+"]",o[r]);this._sendRequest(a,s)}}},_sendRequest:function(e,t){var i=this;return $.ajax({type:"POST",url:this._options.url,enctype:"multipart/form-data",data:t,contentType:!1,processData:!1,success:function(t,n,s){i._success(e,t)},error:$.proxy(this._error,this),xhr:function(){var t=$.ajaxSettings.xhr();return t&&t.upload.addEventListener("progress",function(t){i._progress(e,t)},!1),t}})},_createUploadMatrix:function(e){var t=this._initFile(e);return t.hasClass("uploadFailed")?null:(t.data("filename",e.name).data("internalFileID",this._internalFileID),this._uploadMatrix[this._internalFileID++]=t,this._internalFileID-1)},_success:function(e,t){},_progress:function(e,t){var i=Math.round(100*t.loaded/t.total);this._uploadMatrix[e].find("progress").attr("value",i)},_showOverlay:function(){if(null===this._iframe&&(this._iframe=$('<iframe name="__fileUploadIFrame" />').hide().appendTo(document.body)),!this._overlay){this._overlay=$('<div><form enctype="multipart/form-data" method="post" action="'+this._options.url+'" target="__fileUploadIFrame" /></div>').hide().appendTo(document.body);var e=this._overlay.find("form");$('<dl class="wide"><dd><input type="file" id="__fileUpload" name="'+this._name+'" '+(this._options.multiple?'multiple="true" ':"")+"/></dd></dl>").appendTo(e),$('<div class="formSubmit"><input type="submit" value="Upload" accesskey="s" /></div></form>').appendTo(e),$('<input type="hidden" name="isFallback" value="1" />').appendTo(e),$('<input type="hidden" name="actionName" value="'+this._options.action+'" />').appendTo(e),$('<input type="hidden" name="className" value="'+this._className+'" />').appendTo(e);var t=this._getParameters();for(var i in t)$('<input type="hidden" name="'+i+'" value="'+t[i]+'" />').appendTo(e);e.submit($.proxy(function(){var e={name:this._getFilename(),size:""},t=this._createUploadMatrix(e),i=this;this._iframe.data("loading",!0).off("load").load(function(){i._evaluateResponse(t)}),this._overlay.wcfDialog("close")},this))}this._overlay.wcfDialog({title:WCF.Language.get("wcf.global.button.upload")})},_evaluateResponse:function(e){var t=$.parseJSON(this._iframe.contents().find("pre").html());this._success(e,t)}}),WCF.Sortable={},WCF.Sortable.List=Class.extend({_additionalParameters:{},_className:"",_containerID:"",_container:null,_notification:null,
+_offset:0,_options:{},_proxy:null,_structure:{},init:function(e,t,i,n,s,a){this._additionalParameters=a||{},this._containerID=$.wcfEscapeID(e),this._container=$("#"+this._containerID),this._className=t,this._offset=i||0,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._structure={},this._options=$.extend(!0,{axis:"y",connectWith:"#"+this._containerID+" .sortableList",disableNesting:"sortableNoNesting",doNotClear:!0,errorClass:"sortableInvalidTarget",forcePlaceholderSize:!0,handle:"",helper:"clone",items:"li:not(.sortableNoSorting)",opacity:.6,placeholder:"sortablePlaceholder",tolerance:"pointer",toleranceElement:"> span"},n||{});var o=$("#"+this._containerID+" .sortableList");if(o.is("tbody")&&("li:not(.sortableNoSorting)"===this._options.items&&(this._options.items="tr:not(.sortableNoSorting)",this._options.toleranceElement=""),"clone"===this._options.helper)){this._options.helper=this._tableRowHelper.bind(this);var r=o.prev("thead");r&&r.find("th").each(function(e,t){t=$(t),t.width(t.width())})}if(s?o.sortable(this._options):o.nestedSortable(this._options),this._className){var l=this._container.find(".formSubmit");if(!l.length&&(l=this._container.next(".formSubmit"),!l.length))return void console.debug("[WCF.Sortable.Simple] Unable to find form submit for saving, aborting.");l.children('button[data-type="submit"]').click($.proxy(this._submit,this))}},_tableRowHelper:function(e,t){return t.children("td").each(function(e,t){t=$(t),t.width(t.width())}),t},_submit:function(){this._structure={},this._container.find(".sortableList").each($.proxy(function(e,t){var i=$(t),n=i.data("objectID");void 0!==n&&i.children(this._options.items).each($.proxy(function(e,t){var i=$(t).data("objectID");this._structure[n]||(this._structure[n]=[]),this._structure[n].push(i)},this))},this));var e=$.extend(!0,{data:{offset:this._offset,structure:this._structure}},this._additionalParameters);this._proxy.setOption("data",{actionName:"updatePosition",className:this._className,interfaceName:"wcf\\data\\ISortableAction",parameters:e}),this._proxy.sendRequest()},_success:function(e,t,i){null===this._notification&&(this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success.edit"))),this._notification.show()}}),WCF.Popover=Class.extend({_activeElementID:"",_identifier:"",_popoverObj:null,init:function(e){var t=!1;require(["Environment"],function(e){"desktop"!==e.platform()&&(t=!0)}.bind(this)),t||(this._activeElementID="",this._identifier=e,require(["WoltLabSuite/Core/Controller/Popover"],function(t){t.init({attributeName:"legacy",className:e,identifier:this._identifier,legacy:!0,loadCallback:this._legacyLoad.bind(this)})}.bind(this)))},_initContainers:function(){},_legacyLoad:function(e,t){this._activeElementID=e,this._popoverObj=t,this._loadContent()},_insertContent:function(e,t){this._popoverObj.setContent(this._identifier,e,t)}}),WCF.EditableItemList=Class.extend({_allowCustomInput:!1,_className:"",_data:{},_form:null,_itemList:null,_objectID:0,_objectTypeID:0,_search:null,_searchInput:null,init:function(e,t){if(this._itemList=$(e),this._searchInput=$(t),this._data={},!this._itemList.length||!this._searchInput.length)return void console.debug("[WCF.EditableItemList] Item list and/or search input do not exist, aborting.");if(this._objectID=this._getObjectID(),this._objectTypeID=this._getObjectTypeID(),this._itemList.find(".jsEditableItem").click($.proxy(this._click,this)),this._itemList.children("ul").length||$("<ul />").appendTo(this._itemList),this._itemList=this._itemList.children("ul"),this._form=this._itemList.parents("form").submit($.proxy(this._submit,this)),this._allowCustomInput){var i=this;this._searchInput.keydown($.proxy(this._keyDown,this)).keypress($.proxy(this._keyPress,this)).on("paste",function(){setTimeout(function(){i._onPaste()},100)})}this._searchInput.parents(".dropdown").data("preventSubmit",!0)},_keyDown:function(e){return null!==e||this._keyPress(null)},_keyPress:function(e){if(null===e||44===e.charCode||e.charCode===$.ui.keyCode.ENTER||$.browser.mozilla&&e.keyCode===$.ui.keyCode.ENTER){if(null!==e&&e.charCode===$.ui.keyCode.ENTER&&this._search&&-1!==this._search._itemIndex)return!1;var t=$.trim(this._searchInput.val());return e&&44===e.charCode&&(t=t.substring(0,this._searchInput.getCaret())),""===t?!0:(this.addItem({objectID:0,label:t}),e&&44===e.charCode?this._searchInput.val($.trim(this._searchInput.val().substr(this._searchInput.getCaret()))):this._searchInput.val(""),null!==e&&e.stopPropagation(),!1)}return!0},_onPaste:function(){var e=$.trim(this._searchInput.val());e=e.split(",");for(var t=0,i=e.length;t<i;t++){var n=$.trim(e[t]);""!==n&&this.addItem({objectID:0,label:n})}this._searchInput.val("")},load:function(e){},_click:function(e){var t=$(e.currentTarget),i=t.data("objectID"),n=t.data("label");return this._search&&this._search.removeExcludedSearchValue(n),this._removeItem(i,n),t.remove(),e.stopPropagation(),!1},_getObjectID:function(){return 0},_getObjectTypeID:function(){return 0},addItem:function(e){return!(!this._data[e.objectID]||0===e.objectID&&this._allowCustomInput)||($('<li class="badge">'+WCF.String.escapeHTML(e.label)+"</li>").data("objectID",e.objectID).data("label",e.label).appendTo(this._itemList).click($.proxy(this._click,this)),this._search&&this._search.addExcludedSearchValue(e.label),this._addItem(e.objectID,e.label),!0)},clearList:function(){this._itemList.children("li").each($.proxy(function(e,t){var i=$(t);this._search&&this._search.removeExcludedSearchValue(i.data("label")),i.remove(),this._removeItem(i.data("objectID"),i.data("label"))},this))},_submit:function(){this._keyDown(null)},_addItem:function(e,t){this._data[e]=t},_removeItem:function(e,t){delete this._data[e]},getSearchInput:function(){return this._searchInput}}),WCF.Language.Chooser=Class.extend({init:function(e,t,i,n,s,a){require(["WoltLabSuite/Core/Language/Chooser"],function(o){o.init(e,t,i,n,s,a)})}}),WCF.Style={},WCF.UserPanel=Class.extend({_container:null,_didLoad:!1,_link:null,_noItems:"",_revertOnEmpty:!0,init:function(e){if(this._container=$("#"+e),this._didLoad=!1,this._revertOnEmpty=!0,1!=this._container.length)return void console.debug("[WCF.UserPanel] Unable to find container identified by '"+e+"', aborting.");this._convert()},_convert:function(){this._container.addClass("dropdown"),this._link=this._container.children("a").remove();var e=$('<a href="'+this._link.attr("href")+'" class="dropdownToggle">'+this._link.html()+"</a>").appendTo(this._container).click($.proxy(this._click,this)),t=$('<ul class="dropdownMenu" />').appendTo(this._container);$('<li class="jsDropdownPlaceholder"><span>'+WCF.Language.get("wcf.global.loading")+"</span></li>").appendTo(t),this._addDefaultItems(t),this._container.dblclick($.proxy(function(){return window.location=this._link.attr("href"),!1},this)),WCF.Dropdown.initDropdown(e,!1)},_addDefaultItems:function(e){},_addDivider:function(e){$('<li class="dropdownDivider" />').appendTo(e)},_click:function(e){e.preventDefault(),this._didLoad||(new WCF.Action.Proxy({autoSend:!0,data:this._getParameters(),success:$.proxy(this._success,this)}),this._didLoad=!0)},_getParameters:function(){return{}},_success:function(e,t,i){var n=WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify());n.children(".jsDropdownPlaceholder").remove(),e.returnValues&&e.returnValues.template?($(""+e.returnValues.template).prependTo(n),this._updateBadge(e.returnValues.totalCount),this._after(n)):($("<li><span>"+WCF.Language.get(this._noItems)+"</span></li>").prependTo(n),this._updateBadge(0))},_updateBadge:function(e){if(e=parseInt(e)||0){var t=this._container.find(".badge");t.length||(t=$('<span class="badge badgeUpdate" />').appendTo(this._container.children(".dropdownToggle")),t.before(" ")),t.html(e)}else this._container.find(".badge").remove()},_after:function(e){}}),jQuery.fn.extend({wcfDialog:function(e){var t=arguments;return require(["Dom/Util","Ui/Dialog"],function(i,n){var s=i.identify(this[0]);if("close"===e)n.close(s);else if("render"===e)n.rebuild(s);else if("option"===e)3===t.length&&("title"===t[1]&&"string"==typeof t[2]?n.setTitle(s,t[2]):0===t[1].indexOf("on")?n.setCallback(s,t[1],t[2]):"closeConfirmMessage"===t[1]&&null===t[2]&&n.setCallback(s,"onBeforeClose",null));else{this[0].parentNode.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&document.body.appendChild(this[0]);var a=1===t.length&&"object"==typeof t[0]?t[0]:{};n.openStatic(s,null,a),a.hasOwnProperty("title")&&n.setTitle(s,a.title)}}.bind(this)),this}}),$.widget("ui.wcfSlideshow",{_buttonList:null,_count:0,_index:0,_itemList:null,_items:null,_timer:null,_width:0,options:{cycle:!0,cycleInterval:5,itemGap:50},_create:function(){this._itemList=this.element.children("ul"),this._items=this._itemList.children("li"),this._count=this._items.length,this._index=0,this._count>1&&this._initSlideshow()},_initSlideshow:function(){var e=$(this._items.get(0)).outerHeight();this._items.addClass("slideshowItem"),this._width=this.element.css("height",e).innerWidth(),this._itemList.addClass("slideshowItemList").css("left",0),this._items.each($.proxy(function(t,i){$(i).show().css({height:e,left:(this._width+this.options.itemGap)*t,width:this._width})},this)),this.element.css({height:e,width:this._width}).hover($.proxy(this._hoverIn,this),$.proxy(this._hoverOut,this)),this._buttonList=$('<ul class="slideshowButtonList" />').appendTo(this.element);for(var t=0;t<this._count;t++){var i=$('<li><a><span class="icon icon16 fa-circle" /></a></li>').data("index",t).click($.proxy(this._click,this)).appendTo(this._buttonList);0==t&&i.find(".icon").addClass("active")}this._resetTimer(),$(window).resize($.proxy(this._resize,this))},rebuildHeight:function(){var e=$(this._items.get(0)).css("height","auto"),t=e.outerHeight();this._items.css("height",t+"px"),this.element.css("height",t+"px")},_resize:function(){this._width=this.element.css("width","auto").innerWidth(),this._items.each($.proxy(function(e,t){$(t).css({left:(this._width+this.options.itemGap)*e,width:this._width})},this)),this._index--,this.moveTo(null)},_hoverIn:function(){null!==this._timer&&this._timer.stop()},_hoverOut:function(){this._resetTimer()},_resetTimer:function(){if(this.options.cycle){null!==this._timer&&this._timer.stop();var e=this;this._timer=new WCF.PeriodicalExecuter(function(){e.moveTo(null)},1e3*this.options.cycleInterval)}},_click:function(e){this.moveTo($(e.currentTarget).data("index")),this._resetTimer()},moveTo:function(e){this._index=null===e?this._index+1:e,this._index==this._count&&(this._index=0),$(this._buttonList.find(".icon").removeClass("active").get(this._index)).addClass("active"),this._itemList.css("left",this._index*(this._width+this.options.itemGap)*-1),this._trigger("moveTo",null,{index:this._index})},getItem:function(e){return this._items[e]?this._items[e]:null}}),jQuery.fn.extend({datepicker:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);switch(e){case"destroy":window.__wcf_bc_datePicker.destroy(t);break;case"getDate":return window.__wcf_bc_datePicker.getDate(t);case"option":if("onClose"===i[0])return i.length>1?this.datepicker("setOption","onClose",i[1]):function(){};console.warn("datepicker('option') supports only 'onClose'.");break;case"setDate":window.__wcf_bc_datePicker.setDate(t,i[0]);break;case"setOption":"onClose"===i[0]?window.__wcf_bc_datePicker.setCloseCallback(t,i[1]):console.warn("datepicker('setOption') supports only 'onClose'.");break;default:console.debug("Unsupported method '"+e+"' for datepicker()")}return this}}),jQuery.fn.extend({wcfTabs:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);require(["Dom/Util","WoltLabSuite/Core/Ui/TabMenu"],function(n,s){var a=s.getTabMenu(n.identify(t));null!==a&&a[e].apply(a,i)})}}),$.widget("ui.wcfPages",{_api:null,SHOW_LINKS:11,SHOW_SUB_LINKS:20,options:{activePage:1,maxPage:1},_create:function(){require(["WoltLabSuite/Core/Ui/Pagination"],function(e){this._api=new e(this.element[0],{activePage:this.options.activePage,maxPage:this.options.maxPage,callbackShouldSwitch:function(e){return!1!==this._trigger("shouldSwitch",void 0,{nextPage:e})}.bind(this),callbackSwitch:function(e){this._trigger("switched",void 0,{activePage:e})}.bind(this)})}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this._api=null,this.element[0].innerHTML=""},_setOption:function(e,t){if("activePage"==e&&t!=this.options[e]&&t>0&&t<=this.options.maxPage){var i=this._trigger("shouldSwitch",void 0,{nextPage:t});i||void 0!==i?this._api.switchPage(t):this._trigger("notSwitched",void 0,{activePage:t})}return this}}),WCF.Category={},WCF.Category.NestedList=Class.extend({_categories:{},init:function(){var e=this;$(".jsCategory").each(function(t,i){var n=$(i).data("parentCategoryID",null).change($.proxy(e._updateSelection,e));e._categories[n.val()]=n;var s=[];n.parents("li").find(".jsChildCategory").each(function(t,i){var a=$(i).data("parentCategoryID",n.val()).change($.proxy(e._updateSelection,e));e._categories[a.val()]=a,s.push(a.val()),a.is(":checked")&&n.prop("checked","checked")}),n.data("childCategoryIDs",s)})},_updateSelection:function(e){var t=$(e.currentTarget),i=t.data("parentCategoryID");if(t.is(":checked"))null!==i&&this._categories[i].prop("checked","checked");else if(null===i)for(var n=t.data("childCategoryIDs"),s=0,a=n.length;s<a;s++)this._categories[n[s]].prop("checked",!1)}}),WCF.Category.FlexibleCategoryList=Class.extend({_list:null,_categories:{},init:function(e){if(this._list=$("#"+e),this._buildStructure(),this._list.find("input:checked").each(function(){$(this).trigger("change")}),this._list.children("li").length<2)return void this._list.addClass("flexibleCategoryListDisabled")},_buildStructure:function(){var e=this;this._list.find(".jsCategory").each(function(t,i){var n=$(i).change(e._updateSelection.bind(e)),s=parseInt(n.val()),a=[];n.parents("li:eq(0)").find(".jsChildCategory").each(function(t,i){var s=$(i);s.data("parentCategory",n).change(e._updateSelection.bind(e));var o=parseInt(s.val());a.push(s);var r=[];s.parents("li:eq(0)").find(".jsSubChildCategory").each(function(t,i){var n=$(i);n.data("parentCategory",s).change(e._updateSelection.bind(e)),r.push(n)}),e._categories[o]=r}),e._categories[s]=a})},_updateSelection:function(e){var t=$(e.currentTarget),i=parseInt(t.val()),n=t.data("parentCategory");if(t.is(":checked"))n&&(n.prop("checked","checked"),(n=n.data("parentCategory"))&&n.prop("checked","checked"));else{if(this._categories[i])for(var s=0,a=this._categories[i].length;s<a;s++){var o=this._categories[i][s];o.prop("checked",!1);var r=parseInt(o.val());if(this._categories[r])for(var l=0,c=this._categories[r].length;l<c;l++)this._categories[r][l].prop("checked",!1)}if(n){for(var u=parseInt(n.val()),s=0,a=this._categories[u].length;s<a;s++)if(this._categories[u][s].prop("checked"))return;if(n=n.data("parentCategory")){u=parseInt(n.val());for(var s=0,a=this._categories[u].length;s<a;s++)if(this._categories[u][s].prop("checked"))return}}}}}),WCF.Condition={},WCF.Notice={}; })(this);
// WCF.Like.js
-(function (window, undefined) { "use strict";WCF.Like=Class.extend({_allowForOwnContent:!1,_canLike:!1,_containers:{},_containerData:{},_enableDislikes:!0,_isBusy:!1,_likeDetails:{},_proxy:null,_showSummary:!0,init:function(t,e,i,a){this._canLike=t,this._enableDislikes=e,this._isBusy=!1,this._likeDetails={},this._showSummary=i,this._allowForOwnContent=a;var s=this._getContainers();this._initContainers(s),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)});var n=new Date,o=n.toString().hashCode+n.getUTCMilliseconds();WCF.DOMNodeInsertedHandler.addCallback("WCF.Like"+o,$.proxy(this._domNodeInserted,this))},_domNodeInserted:function(){var t=this._getContainers();this._initContainers(t)},_initContainers:function(containers){var $createdWidgets=!1;containers.each($.proxy(function(index,container){var $container=$(container),$containerID=$container.wcfIdentify();this._containers[$containerID]||(this._containers[$containerID]=$container,this._containerData[$containerID]={likeButton:null,badge:null,dislikeButton:null,likes:$container.data("like-likes"),dislikes:$container.data("like-dislikes"),objectType:$container.data("objectType"),objectID:this._getObjectID($containerID),users:eval($container.data("like-users")),liked:$container.data("like-liked")},this._createWidget($containerID),$createdWidgets=!0)},this)),$createdWidgets&&new WCF.PeriodicalExecuter(function(t){t.stop(),WCF.DOMNodeInsertedHandler.execute()},250)},_getContainers:function(){},_getWidgetContainer:function(t){},_getObjectID:function(t){},_addWidget:function(t,e){var i=this._getWidgetContainer(t);e.appendTo(i)},_buildWidget:function(t,e,i,a,s){var n=$('<aside class="likesWidget"><ul></ul></aside>');this._canLike&&(e.appendTo(n.find("ul")),i.appendTo(n.find("ul"))),a.appendTo(n),this._addWidget(t,n)},_createWidget:function(t){var e=$('<li class="wcfLikeButton"><a href="#" title="'+WCF.Language.get("wcf.like.button.like")+'" class="jsTooltip"><span class="icon icon16 fa-thumbs-o-up" /> <span class="invisible">'+WCF.Language.get("wcf.like.button.like")+"</span></a></li>"),i=$('<li class="wcfDislikeButton"><a href="#" title="'+WCF.Language.get("wcf.like.button.dislike")+'" class="jsTooltip"><span class="icon icon16 fa-thumbs-o-down" /> <span class="invisible">'+WCF.Language.get("wcf.like.button.dislike")+"</span></a></li>");this._enableDislikes||i.hide(),this._allowForOwnContent||WCF.User.userID!=this._containers[t].data("userID")||(e=$(""),i=$(""));var a=$('<a class="badge jsTooltip likesBadge" />').data("containerID",t).click($.proxy(this._showLikeDetails,this)),s=null;this._showSummary&&(s=$('<p class="likesSummary"><span class="pointer" /></p>'),s.children("span").data("containerID",t).click($.proxy(this._showLikeDetails,this))),this._buildWidget(t,e,i,a,s),this._containerData[t].likeButton=e,this._containerData[t].dislikeButton=i,this._containerData[t].badge=a,this._containerData[t].summary=s,e.data("containerID",t).data("type","like").click($.proxy(this._click,this)),i.data("containerID",t).data("type","dislike").click($.proxy(this._click,this)),this._setActiveState(e,i,this._containerData[t].liked),this._updateBadge(t),this._showSummary&&this._updateSummary(t)},_showLikeDetails:function(t,e){var i=null===t?e:$(t.currentTarget).data("containerID");void 0===this._likeDetails[i]&&(this._likeDetails[i]=new WCF.User.List("wcf\\data\\like\\LikeAction",WCF.Language.get("wcf.like.details"),{data:{containerID:i,objectID:this._containerData[i].objectID,objectType:this._containerData[i].objectType}})),this._likeDetails[i].open()},_click:function(t){t.preventDefault();var e=$(t.currentTarget);if(null===e)return void console.debug("[WCF.Like] Unable to find target button, aborting.");this._sendRequest(e.data("containerID"),e.data("type"))},_sendRequest:function(t,e){this._isBusy||(this._isBusy=!0,this._proxy.setOption("data",{actionName:e,className:"wcf\\data\\like\\LikeAction",parameters:{data:{containerID:t,objectID:this._containerData[t].objectID,objectType:this._containerData[t].objectType}}}),this._proxy.sendRequest())},_success:function(t,e,i){var a=t.returnValues.containerID;if(this._containers[a])switch(t.actionName){case"dislike":case"like":this._containerData[a].likes=parseInt(t.returnValues.likes),this._containerData[a].dislikes=parseInt(t.returnValues.dislikes),this._containerData[a].users=t.returnValues.users,$.each(this._containerData[a].users,function(t,e){e.username=WCF.String.escapeHTML(e.username)}),this._updateBadge(a),this._showSummary&&this._updateSummary(a);var s=this._containerData[a].likeButton,n=this._containerData[a].dislikeButton,o=0;t.returnValues.isLiked?o=1:t.returnValues.isDisliked&&(o=-1),this._setActiveState(s,n,o),void 0!==this._likeDetails[a]&&delete this._likeDetails[a],this._isBusy=!1}},_updateBadge:function(t){if(this._containerData[t].likes||this._containerData[t].dislikes){this._containerData[t].badge.show();var e=this._containerData[t].likes-this._containerData[t].dislikes,i=this._containerData[t].badge;i.removeClass("green red"),e>0?(i.text("+"+WCF.String.formatNumeric(e)),i.addClass("green")):e<0?(i.text(WCF.String.formatNumeric(e)),i.addClass("red")):i.text("±0");var a=this._containerData[t].likes,s=this._containerData[t].dislikes;i.attr("data-tooltip",WCF.Language.get("wcf.like.tooltip",{likes:a,dislikes:s}))}else this._containerData[t].badge.hide()},_updateSummary:function(t){if(this._containerData[t].likes){this._containerData[t].summary.show();var e=this._containerData[t].users,i=[];for(var a in e)i.push(e[a].username);var s=this._containerData[t].likes-i.length;this._containerData[t].summary.children("span").html(WCF.Language.get("wcf.like.summary",{users:i,others:s}))}else this._containerData[t].summary.hide()},_setActiveState:function(t,e,i){t.removeClass("active"),e.removeClass("active"),1==i?t.addClass("active"):-1==i&&e.addClass("active")}}); })(this);
+(function (window, undefined) { "use strict";WCF.Like=Class.extend({init:function(){throw new Error("The `WCF.Like` API is obsolete and therefore no longer supported. Please use the current API `WoltLabSuite/Core/Ui/Reaction/Handler` instead.")},_domNodeInserted:function(){},_initContainers:function(){},_getContainers:function(){},_getWidgetContainer:function(){},_getObjectID:function(){},_addWidget:function(){},_buildWidget:function(){},_createWidget:function(){},_showLikeDetails:function(){},_click:function(){},_sendRequest:function(){},_success:function(){},_updateBadge:function(){},_updateSummary:function(){},_setActiveState:function(){}}); })(this);
// WCF.ACL.js
(function (window, undefined) { "use strict";WCF.ACL={},WCF.ACL.List=Class.extend({_categoryName:"",_container:null,_containerElements:{},_objectID:0,_objectTypeID:null,_options:{},_proxy:null,_search:null,_values:{group:{},user:{}},init:function(e,t,n,i,a,s){this._objectID=i||0,this._objectTypeID=t,this._categoryName=n,void 0===a&&(a=!0),this._values={group:{},user:{}},this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._container=$(e).hide().addClass("aclContainer");var r=this._container.children("dd"),c=$('<ul class="aclList containerList" />').appendTo(r),l=$('<input type="text" class="long" placeholder="'+WCF.Language.get("wcf.acl.search."+(a?"":"user.")+"description")+'" />').appendTo(r),o=$('<ul class="aclPermissionList containerList" />').hide().appendTo(r);elData(o[0],"grant",WCF.Language.get("wcf.acl.option.grant")),elData(o[0],"deny",WCF.Language.get("wcf.acl.option.deny")),this._containerElements={aclList:c,denyAll:null,grantAll:null,permissionList:o,searchInput:l},this._search=new WCF.Search.User(l,$.proxy(this.addObject,this),a);var h=this._container.parents("form:eq(0)");h.submit($.proxy(this.submit,this));var p=h.find("input[type=reset]:eq(0)");p.length&&p.click($.proxy(this._reset,this)),s?this._success(s):this._loadACL()},_reset:function(){this._values={group:{},user:{}},this._containerElements.aclList.empty(),this._containerElements.searchInput.val(""),this._containerElements.permissionList.hide().find("input[type=checkbox]").prop("checked",!1)},_loadACL:function(){this._proxy.setOption("data",{actionName:"loadAll",className:"wcf\\data\\acl\\option\\ACLOptionAction",parameters:{categoryName:this._categoryName,objectID:this._objectID,objectTypeID:this._objectTypeID}}),this._proxy.sendRequest()},addObject:function(e){var t=this._createListItem(e.objectID,e.label,e.type);this._savePermissions(),this._containerElements.aclList.children("li").removeClass("active"),t.addClass("active"),this._search.addExcludedSearchValue(e.label),this._containerElements.permissionList.find("input[type=checkbox]").prop("checked",!1),this._containerElements.searchInput.val(""),this._containerElements.permissionList.show(),WCF.DOMNodeInsertedHandler.execute()},_createListItem:function(e,t,n){var i=$('<li><span class="icon icon16 fa-user'+("group"===n?"s":"")+'" /> <span class="aclLabel">'+t+"</span></li>").appendTo(this._containerElements.aclList);return i.data("objectID",e).data("type",n).data("label",t).click($.proxy(this._click,this)),$('<span class="icon icon16 fa-times jsTooltip pointer" title="'+WCF.Language.get("wcf.global.button.delete")+'" />').click($.proxy(this._removeItem,this)).appendTo(i),i},_removeItem:function(e){var t=$(e.currentTarget).parent(),n=t.data("type"),i=t.data("objectID");this._search.removeExcludedSearchValue(t.data("label")),t.remove(),this._values[n][i]&&delete this._values[n][i],this._selectFirstEntry()},_selectFirstEntry:function(){var e=this._containerElements.aclList.children("li:eq(0)");e.length?this._select(e,!1):this._reset()},_success:function(e,t,n){if($.getLength(e.returnValues.options)){var i=0,a={};for(var s in e.returnValues.options){var r=e.returnValues.options[s],c=$("<li><span>"+r.label+"</span></li>").data("optionID",s).data("optionName",r.optionName),l=$('<input type="checkbox" id="grant'+s+'" />').appendTo(c).wrap('<label for="grant'+s+'" class="jsTooltip" title="'+WCF.Language.get("wcf.acl.option.grant")+'" />'),o=$('<input type="checkbox" id="deny'+s+'" />').appendTo(c).wrap('<label for="deny'+s+'" class="jsTooltip" title="'+WCF.Language.get("wcf.acl.option.deny")+'" />');l.data("type","grant").data("optionID",s).change($.proxy(this._change,this)),o.data("type","deny").data("optionID",s).change($.proxy(this._change,this)),a[r.categoryName]||(a[r.categoryName]=[]),""===r.categoryName?c.appendTo(this._containerElements.permissionList):a[r.categoryName].push(c),i++}if(i>1){var c=$('<li class="aclFullAccess"><span>'+WCF.Language.get("wcf.acl.option.fullAccess")+"</span></li>").prependTo(this._containerElements.permissionList);this._containerElements.grantAll=$('<input type="checkbox" id="grantAll_'+this._container.attr("id")+'" />').appendTo(c).wrap('<label class="jsTooltip" title="'+WCF.Language.get("wcf.acl.option.grant")+'" />'),this._containerElements.denyAll=$('<input type="checkbox" id="denyAll_'+this._container.attr("id")+'" />').appendTo(c).wrap('<label class="jsTooltip" title="'+WCF.Language.get("wcf.acl.option.deny")+'" />'),this._containerElements.grantAll.data("type","grant").change($.proxy(this._changeAll,this)),this._containerElements.denyAll.data("type","deny").change($.proxy(this._changeAll,this))}if($.getLength(a))for(var h in a){var p=a[h];e.returnValues.categories[h]&&$('<li class="aclCategory">'+e.returnValues.categories[h]+"</li>").appendTo(this._containerElements.permissionList);for(var d=0,u=p.length;d<u;d++)p[d].appendTo(this._containerElements.permissionList)}this._parseData(e,"group"),this._parseData(e,"user"),this._container.show(),this._selectFirstEntry()}},_parseData:function(e,t){if($.getLength(e.returnValues[t].option)){for(var n in e.returnValues[t].label)this._createListItem(n,e.returnValues[t].label[n],t),this._search.addExcludedSearchValue(e.returnValues[t].label[n]);this._values[t]=e.returnValues[t].option,WCF.DOMNodeInsertedHandler.execute()}},_click:function(e){var t=$(e.currentTarget);t.hasClass("active")||this._select(t,!0)},_select:function(e,t){t&&this._savePermissions(),this._containerElements.aclList.children("li").removeClass("active"),e.addClass("active"),this._setupPermissions(e.data("type"),e.data("objectID"))},_change:function(e){var t=$(e.currentTarget),n=t.data("optionID"),i=t.data("type");t.is(":checked")?"deny"===i?($("#grant"+n).prop("checked",!1),null!==this._containerElements.grantAll&&this._containerElements.grantAll.prop("checked",!1)):($("#deny"+n).prop("checked",!1),null!==this._containerElements.denyAll&&this._containerElements.denyAll.prop("checked",!1)):"deny"===i&&null!==this._containerElements.denyAll?this._containerElements.denyAll.prop("checked",!1):"grant"===i&&null!==this._containerElements.grantAll&&this._containerElements.grantAll.prop("checked",!1);var a=!0;this._containerElements.permissionList.find("input[type=checkbox]").each($.proxy(function(e,t){var n=$(t);if(n.data("type")===i&&n.attr("id")!==i+"All_"+this._container.attr("id")&&!n.is(":checked"))return a=!1,!1},this)),"deny"==i?null!==this._containerElements.denyAll&&(a?this._containerElements.denyAll.prop("checked",!0):this._containerElements.denyAll.prop("checked",!1)):null!==this._containerElements.grantAll&&(a?this._containerElements.grantAll.prop("checked",!0):this._containerElements.grantAll.prop("checked",!1))},_changeAll:function(e){var t=$(e.currentTarget),n=t.data("type");t.is(":checked")?"deny"===n?(this._containerElements.grantAll.prop("checked",!1),this._containerElements.permissionList.find("input[type=checkbox]").each($.proxy(function(e,t){var n=$(t);"deny"===n.data("type")&&n.attr("id")!=="denyAll_"+this._container.attr("id")&&n.prop("checked",!0).trigger("change")},this))):(this._containerElements.denyAll.prop("checked",!1),this._containerElements.permissionList.find("input[type=checkbox]").each($.proxy(function(e,t){var n=$(t);"grant"===n.data("type")&&n.attr("id")!=="grantAll_"+this._container.attr("id")&&n.prop("checked",!0).trigger("change")},this))):"deny"===n?(this._containerElements.grantAll.prop("checked",!1),this._containerElements.permissionList.find("input[type=checkbox]").each($.proxy(function(e,t){var n=$(t);"deny"===n.data("type")&&n.attr("id")!=="denyAll_"+this._container.attr("id")&&n.prop("checked",!1).trigger("change")},this))):(this._containerElements.denyAll.prop("checked",!1),this._containerElements.permissionList.find("input[type=checkbox]").each($.proxy(function(e,t){var n=$(t);"grant"===n.data("type")&&n.attr("id")!=="grantAll_"+this._container.attr("id")&&n.prop("checked",!1).trigger("change")},this)))},_setupPermissions:function(e,t){if(this._containerElements.permissionList.find("input[type='checkbox']").prop("checked",!1),this._values[e]&&this._values[e][t])for(var n in this._values[e][t])1==this._values[e][t][n]?$("#grant"+n).prop("checked",!0).trigger("change"):$("#deny"+n).prop("checked",!0).trigger("change");this._containerElements.permissionList.show()},_savePermissions:function(){var e=this._containerElements.aclList.find("li.active");if(e.length){var t=e.data("objectID"),n=e.data("type");this._values[n][t]={},this._containerElements.permissionList.find("input[type='checkbox']").each(function(e,i){var a=$(i);if(a.attr("id")!="grantAll_"+this._container.attr("id")&&a.attr("id")!="denyAll_"+this._container.attr("id")){var s="deny"===a.data("type")?0:1,r=a.data("optionID");a.is(":checked")?(this._values[n][t][r]=s,a.prop("checked",!1)):this._values[n]&&this._values[n][t]&&this._values[n][t][r]&&this._values[n][t][r]==s&&delete this._values[n][t][r]}}.bind(this))}},submit:function(e){this._savePermissions(),this._save("group"),this._save("user")},_save:function(e){if($.getLength(this._values[e])){var t=this._container.parents("form:eq(0)");for(var n in this._values[e]){var i=this._values[e][n];for(var a in i)$('<input type="hidden" name="aclValues['+e+"]["+n+"]["+a+']" value="'+i[a]+'" />').appendTo(t)}}}}); })(this);
// WCF.Attachment.js
-(function (window, undefined) { "use strict";WCF.Attachment={},WCF.Attachment.Upload=WCF.Upload.extend({_autoInsert:[],_insertAllButton:null,_objectType:"",_objectID:0,_tmpHash:"",_parentObjectID:0,_editorId:"",_replaceOnLoad:{},init:function(t,e,a,i,s,n,r,l){if(this._super(t,e,"wcf\\data\\attachment\\AttachmentAction",{multiple:!0,maxUploads:r}),this._autoInsert=[],this._objectType=a,this._objectID=parseInt(i),this._tmpHash=s,this._parentObjectID=parseInt(n),this._editorId=l,this._buttonSelector.children("p.button").click($.proxy(this._validateLimit,this)),this._fileListSelector.find(".jsButtonInsertAttachment").click($.proxy(this._insert,this)),this._fileListSelector.find(".jsButtonAttachmentInsertThumbnail").click($.proxy(this._insert,this)),this._fileListSelector.find(".jsButtonAttachmentInsertFull").click($.proxy(this._insert,this)),WCF.DOMNodeRemovedHandler.addCallback("WCF.Attachment.Upload",$.proxy(this._removeLimitError,this)),WCF.System.Event.addListener("com.woltlab.wcf.action.delete","attachment_"+this._editorId,$.proxy(this._removeLimitError,this)),this._makeSortable(),this._insertAllButton=$('<p class="button jsButtonAttachmentInsertAll">'+WCF.Language.get("wcf.attachment.insertAll")+"</p>").hide().appendTo(this._buttonSelector),this._insertAllButton.click($.proxy(this._insertAll,this)),this._fileListSelector.children("li:not(.uploadFailed)").length&&this._insertAllButton.show(),this._editorId){WCF.System.Event.addListener("com.woltlab.wcf.redactor2","submit_"+this._editorId,this._submitInline.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+this._editorId,this._reset.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","dragAndDrop_"+this._editorId,this._editorUpload.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this._editorId,this._editorUpload.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","autosaveMetaData_"+this._editorId,function(t){t.tmpHashes&&Array.isArray(t.tmpHashes)||(t.tmpHashes=[]);var e=t.tmpHashes.indexOf(s);this._fileListSelector.children("li:not(.uploadFailed)").length>0?-1===e&&t.tmpHashes.push(s):-1!==e&&t.tmpHashes.splice(e)}.bind(this));var o=WCF.System.Event.addListener("com.woltlab.wcf.redactor2","metacode_attach_"+this._editorId,function(t){var e=this._getImageAttachments(),a=t.attributes[0]||0;if(e.hasOwnProperty(a)){var i=t.attributes[2];i=!0===i||"true"===i||~~i>0;var s=elCreate("img");s.className="woltlabAttachment",s.src=e[a][i?"thumbnailUrl":"url"],elData(s,"attachment-id",a);var n=t.attributes[1]||"none";"left"===n?s.classList.add("messageFloatObjectLeft"):"right"===n&&s.classList.add("messageFloatObjectRight");var r=t.metacode;r.parentNode.insertBefore(s,r),elRemove(r),t.cancel=!0}}.bind(this));WCF.System.Event.addListener("com.woltlab.wcf.redactor2","destroy_"+this._editorId,function(){WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","submit_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","reset_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","insertAttachment_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","dragAndDrop_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","autosaveMetaData_"+this._editorId),WCF.System.Event.removeListener("com.woltlab.wcf.redactor2","metacode_attach_"+this._editorId,o)}.bind(this))}},_editorUpload:function(t){var e,a=null;this._fileListSelector.closest(".messageTabMenu").messageTabMenu("showTab","attachments",!0),t.file?e=this._upload(void 0,t.file):(e=this._upload(void 0,void 0,t.blob),a=t.replace||null),null===a?this._autoInsert.push(e):this._replaceOnLoad[e]=a,t.uploadID=e},_getImageAttachments:function(){var t={};return this._fileListSelector.children("li").each(function(e,a){var i=$(a);i.data("isImage")&&(t[~~i.data("objectID")]={thumbnailUrl:i.find(".jsButtonAttachmentInsertThumbnail").data("url"),url:i.find(".jsButtonAttachmentInsertFull").data("url")})}),t},_submitInline:function(t){if(this._tmpHash){t.tmpHash=this._tmpHash;var e={};WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","getMetaData_"+this._editorId,e),e.tmpHashes&&Array.isArray(e.tmpHashes)&&e.tmpHashes.length>0&&(t.tmpHash+=","+e.tmpHashes.join(","))}},_reset:function(){this._fileListSelector.hide().empty(),this._insertAllButton.hide(),this._validateLimit()},_validateLimit:function(){var t=this._buttonSelector.next("small.innerError"),e=this._options.maxUploads-this._fileListSelector.children("li:not(.uploadFailed)").length,a=this._fileUpload?this._fileUpload.prop("files").length:0;if(e<=0||e<a){var i=e<=0?WCF.Language.get("wcf.attachment.upload.error.reachedLimit"):WCF.Language.get("wcf.attachment.upload.error.reachedRemainingLimit").replace(/#remaining#/,e);return t.length||(t=$('<small class="innerError" />').insertAfter(this._buttonSelector)),t.html(i),!1}return t.remove(),!0},_removeLimitError:function(t){var e=this._fileListSelector.children("li");e.filter(":not(.uploadFailed)").length||this._insertAllButton.hide(),e.length||this._fileListSelector.hide(),this._editorId&&t.button&&WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","deleteAttachment_"+this._editorId,{attachmentId:t.button.data("objectID")})},_upload:function(t,e,a){var i=void 0;return this._validateLimit()&&(i=this._super(t,e,a)),this._fileUpload&&(this._removeButton(),this._createButton()),i},_createUploadMatrix:function(t){return this._fileListSelector.children("li.uploadFailed").remove(),this._super(t)},_getParameters:function(){return{objectType:this._objectType,objectID:this._objectID,tmpHash:this._tmpHash,parentObjectID:this._parentObjectID}},_initFile:function(t){var e=$('<li class="box64"><span class="icon icon64 fa-spinner" /><div><div><p>'+t.name+'</p><small><progress max="100"></progress></small></div><ul></ul></div></li>').data("filename",t.name);return this._fileListSelector.append(e),this._fileListSelector.show(),this._buttonSelector.data("maxSize")<t.size&&(e.find("progress").remove(),e.children(".fa-spinner").removeClass("fa-spinner").addClass("fa-ban"),e.find("div > div").append($('<small class="innerError">'+WCF.Language.get("wcf.attachment.upload.error.tooLarge")+"</small>")),e.addClass("uploadFailed")),e},_useThumbnail:function(){return elDataBool(this._fileListSelector[0],"enable-thumbnails")},_success:function(t,e){var a;for(var i in this._uploadMatrix[t])if(this._uploadMatrix[t].hasOwnProperty(i)){var s=this._uploadMatrix[t][i];s.find("progress").remove();var n=s.data("filename"),r=s.data("internalFileID");if(e.returnValues&&e.returnValues.attachments[r]){a=e.returnValues.attachments[r],a.tinyURL?(s.children(".fa-spinner").replaceWith($('<img src="'+a.tinyURL+'" alt="" class="attachmentTinyThumbnail" />')),s.data("height",a.height),s.data("width",a.width),elData(s[0],"is-image",a.isImage)):s.children(".fa-spinner").removeClass("fa-spinner").addClass("fa-"+a.iconName);var l=$('<a href=""></a>');l.text(n).attr("href",a.url),l[0].target="_blank",0!=a.isImage&&l.addClass("jsImageViewer").attr("title",n),s.find("p").empty().append(l),s.find("small").append(a.formattedFilesize);var o=s.find("ul").addClass("buttonGroup"),d=$('<li><span class="button small jsDeleteButton" data-object-id="'+a.attachmentID+'" data-confirm-message="'+WCF.Language.get("wcf.attachment.delete.sure")+'" data-event-name="attachment_'+this._editorId+'">'+WCF.Language.get("wcf.global.button.delete")+"</span></li>");if(o.append(d),s.data("objectID",a.attachmentID),this._editorId)if(a.tinyURL||!this._useThumbnail()&&a.isImage){if(a.thumbnailURL){var c=$('<li><span class="button small jsButtonAttachmentInsertThumbnail" data-object-id="'+a.attachmentID+'" data-url="'+WCF.String.escapeHTML(a.thumbnailURL)+'">'+WCF.Language.get("wcf.attachment.insertThumbnail")+"</span></li>").appendTo(o);c.children("span.button").click($.proxy(this._insert,this))}var h=$('<li><span class="button small jsButtonAttachmentInsertFull" data-object-id="'+a.attachmentID+'" data-url="'+WCF.String.escapeHTML(a.url)+'">'+WCF.Language.get("wcf.attachment.insertFull")+"</span></li>").appendTo(o);h.children("span.button").click($.proxy(this._insert,this))}else{var m=$('<li><span class="button small jsButtonAttachmentInsertPlain" data-object-id="'+a.attachmentID+'">'+WCF.Language.get("wcf.attachment.insert")+"</span></li>");m.appendTo(o).children("span.button").click($.proxy(this._insert,this))}if(this._replaceOnLoad.hasOwnProperty(t)){if(!s.hasClass("uploadFailed")){var u=this._replaceOnLoad[t];u&&u.parentNode&&WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","replaceAttachment_"+this._editorId,{attachmentId:a.attachmentID,img:u,src:a.thumbnailURL?a.thumbnailURL:a.url})}this._replaceOnLoad[t]=null}}else{s.children(".fa-spinner").removeClass("fa-spinner").addClass("fa-ban");var p="";if(e.returnValues&&e.returnValues.errors[r]){var _=e.returnValues.errors[r];p=_.errorType,"uploadFailed"===p&&_.additionalData.phpLimitExceeded&&(p="uploadPhpLimit")}else p="uploadFailed";s.find("div > div").append($('<small class="innerError">'+WCF.Language.get("wcf.attachment.upload.error."+p)+"</small>")),s.addClass("uploadFailed")}if(WCF.inArray(t,this._autoInsert)&&(this._autoInsert.splice(this._autoInsert.indexOf(t),1),!s.hasClass("uploadFailed"))){var f=s.find(".jsButtonAttachmentInsertThumbnail");f.length||(f=s.find(".jsButtonAttachmentInsertFull")),f.trigger("click")}}this._makeSortable(),this._fileListSelector.children("li:not(.uploadFailed)").length?this._insertAllButton.show():this._insertAllButton.hide(),WCF.DOMNodeInsertedHandler.execute()},_insert:function(t){WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","insertAttachment_"+this._editorId,{attachmentId:elData(t.currentTarget,"object-id"),url:elData(t.currentTarget,"url")})},_insertAll:function(){var t=this._useThumbnail()?".jsButtonAttachmentInsertThumbnail, .jsButtonAttachmentInsertPlain":".jsButtonAttachmentInsertFull, .jsButtonAttachmentInsertPlain";this._fileListSelector.children("li:not(.uploadFailed)").find(t).trigger("click")},_error:function(t){this._fileListSelector.find("li").each(function(e,a){var i=$(a);i.children(".fa-spinner").length&&(i.addClass("uploadFailed").children(".fa-spinner").removeClass("fa-spinner").addClass("fa-ban"),i.find("div > div").append($('<small class="innerError">'+(t.responseJSON&&t.responseJSON.message?t.responseJSON.message:WCF.Language.get("wcf.attachment.upload.error.uploadFailed"))+"</small>")))})},_makeSortable:function(){var t=this._fileListSelector.children("li:not(.uploadFailed)");t.length&&(t.addClass("sortableAttachment").children("img").addClass("sortableNode"),this._fileListSelector.hasClass("sortableList")||(this._fileListSelector.addClass("sortableList"),require(["Environment"],function(t){"desktop"===t.platform()&&new WCF.Sortable.List(this._fileListSelector.parent().wcfIdentify(),"",0,{axis:!1,items:"li.sortableAttachment",toleranceElement:null,start:function(t,e){e.placeholder[0].style.setProperty("height",e.helper[0].offsetHeight+"px","")},update:function(){var t=[];this._fileListSelector.children("li:not(.uploadFailed)").each(function(e,a){t.push($(a).data("objectID"))}),t.length&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"updatePosition",className:"wcf\\data\\attachment\\AttachmentAction",parameters:{attachmentIDs:t,objectID:this._objectID,objectType:this._objectType,tmpHash:this._tmpHash}}})}.bind(this)},!0)}.bind(this))))}}); })(this);
+(function (window, undefined) { "use strict";WCF.Attachment={},WCF.Attachment.Upload=WCF.Upload.extend({_autoInsert:[],_insertAllButton:null,_objectType:"",_objectID:0,_tmpHash:"",_parentObjectID:0,_editorId:"",_replaceOnLoad:{},_options:{},init:function(t,e,a,i,s,n,r,l,o){if(this._super(t,e,"wcf\\data\\attachment\\AttachmentAction",{multiple:!0,maxUploads:r}),this._autoInsert=[],this._objectType=a,this._objectID=parseInt(i),this._tmpHash=s,this._parentObjectID=parseInt(n),this._editorId=l,this._options=$.extend(!0,this._options,o||{}),this._buttonSelector.children("p.button").click($.proxy(this._validateLimit,this)),this._fileListSelector.find(".jsButtonInsertAttachment").click($.proxy(this._insert,this)),this._fileListSelector.find(".jsButtonAttachmentInsertThumbnail").click($.proxy(this._insert,this)),this._fileListSelector.find(".jsButtonAttachmentInsertFull").click($.proxy(this._insert,this)),WCF.DOMNodeRemovedHandler.addCallback("WCF.Attachment.Upload",$.proxy(this._removeLimitError,this)),WCF.System.Event.addListener("com.woltlab.wcf.action.delete","attachment_"+this._editorId,$.proxy(this._removeLimitError,this)),this._makeSortable(),this._insertAllButton=$('<p class="button jsButtonAttachmentInsertAll">'+WCF.Language.get("wcf.attachment.insertAll")+"</p>").hide().appendTo(this._buttonSelector),this._insertAllButton.click($.proxy(this._insertAll,this)),this._fileListSelector.children("li:not(.uploadFailed)").length&&this._insertAllButton.show(),this._editorId){WCF.System.Event.addListener("com.woltlab.wcf.redactor2","submit_"+this._editorId,this._submitInline.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+this._editorId,this._reset.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","dragAndDrop_"+this._editorId,this._editorUpload.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this._editorId,this._editorUpload.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","autosaveMetaData_"+this._editorId,function(t){t.tmpHashes&&Array.isArray(t.tmpHashes)||(t.tmpHashes=[]);var e=t.tmpHashes.indexOf(s);this._fileListSelector.children("li:not(.uploadFailed)").length>0?-1===e&&t.tmpHashes.push(s):-1!==e&&t.tmpHashes.splice(e)}.bind(this));var d=WCF.System.Event.addListener("com.woltlab.wcf.redactor2","metacode_attach_"+this._editorId,function(t){var e=this._getImageAttachments(),a=t.attributes[0]||0;if(e.hasOwnProperty(a)){var i=t.attributes[2];i=!0===i||"true"===i||~~i>0;var s=elCreate("img");s.className="woltlabAttachment",s.src=e[a][i?"thumbnailUrl":"url"],elData(s,"attachment-id",a);var n=t.attributes[1]||"none";"left"===n?s.classList.add("messageFloatObjectLeft"):"right"===n&&s.classList.add("messageFloatObjectRight");var r=t.metacode;r.parentNode.insertBefore(s,r),elRemove(r),t.cancel=!0}}.bind(this));WCF.System.Event.addListener("com.woltlab.wcf.redactor2","destroy_"+this._editorId,function(){WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","submit_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","reset_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","insertAttachment_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","dragAndDrop_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","pasteFromClipboard_"+this._editorId),WCF.System.Event.removeAllListeners("com.woltlab.wcf.redactor2","autosaveMetaData_"+this._editorId),WCF.System.Event.removeListener("com.woltlab.wcf.redactor2","metacode_attach_"+this._editorId,d)}.bind(this))}},_editorUpload:function(t){var e,a=null;this._fileListSelector.closest(".messageTabMenu").messageTabMenu("showTab","attachments",!0),t.file?e=this._upload(void 0,t.file):(e=this._upload(void 0,void 0,t.blob),a=t.replace||null),null===a?this._autoInsert.push(e):this._replaceOnLoad[e]=a,t.uploadID=e},_getImageAttachments:function(){var t={};return this._fileListSelector.children("li").each(function(e,a){var i=$(a);i.data("isImage")&&(t[~~i.data("objectID")]={thumbnailUrl:i.find(".jsButtonAttachmentInsertThumbnail").data("url"),url:i.find(".jsButtonAttachmentInsertFull").data("url")})}),t},_submitInline:function(t){if(this._tmpHash){t.tmpHash=this._tmpHash;var e={};WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","getMetaData_"+this._editorId,e),e.tmpHashes&&Array.isArray(e.tmpHashes)&&e.tmpHashes.length>0&&(t.tmpHash+=","+e.tmpHashes.join(","))}},_reset:function(){this._fileListSelector.hide().empty(),this._insertAllButton.hide(),this._validateLimit()},_validateLimit:function(){var t=this._buttonSelector.next("small.innerError"),e=this._options.maxUploads-this._fileListSelector.children("li:not(.uploadFailed)").length,a=this._fileUpload?this._fileUpload.prop("files").length:0;if(e<=0||e<a){var i=e<=0?WCF.Language.get("wcf.attachment.upload.error.reachedLimit"):WCF.Language.get("wcf.attachment.upload.error.reachedRemainingLimit").replace(/#remaining#/,e);return t.length||(t=$('<small class="innerError" />').insertAfter(this._buttonSelector)),t.html(i),!1}return t.remove(),!0},_removeLimitError:function(t){var e=this._fileListSelector.children("li");e.filter(":not(.uploadFailed)").length||this._insertAllButton.hide(),e.length||this._fileListSelector.hide(),this._editorId&&t.button&&WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","deleteAttachment_"+this._editorId,{attachmentId:t.button.data("objectID")})},_upload:function(t,e,a){var i=this._super.bind(this);require(["WoltLabSuite/Core/FileUtil","WoltLabSuite/Core/Image/ImageUtil","WoltLabSuite/Core/Image/Resizer","WoltLabSuite/Core/Ajax/Status"],function(s,n,r,l){l.show();var o=[];e?o.push(e):a?o.push(s.blobToFile(a,"pasted-from-clipboard")):o=this._fileUpload.prop("files");var d=Promise.resolve(o);if(this._options.autoScale&&this._options.autoScale.enable){var c=this._buttonSelector.data("maxSize"),h=new r;d=Array.prototype.reduce.call(o,function(t,e){return t.then(function(t){var a=new Promise(function(t,a){setTimeout(function(){t(e)},1e4)}),i=h.loadFile(e).then(function(t){var i=t.exif,s=this._options.autoScale.maxWidth,r=this._options.autoScale.maxHeight,l=this._options.autoScale.quality;if(window.devicePixelRatio>=2){var o=window.screen.width*window.devicePixelRatio,d=window.screen.height*window.devicePixelRatio;o-10<t.image.width&&t.image.width<o+10&&d-10<t.image.height&&(s=Math.min(s,window.screen.width))}return h.resize(t.image,s,r,l,e.size>c,a).then(function(t){if(void 0===t)return e;var a=this._options.autoScale.fileType;return("keep"===this._options.autoScale.fileType||n.containsTransparentPixels(t))&&(a=e.type),h.saveFile({exif:i,image:t},e.name,a,l)}.bind(this)).then(function(t){return t.size>e.size?(console.debug('[WCF.Attachment] File size of "'+e.name+'" increased, uploading untouched image.'),e):t})}.bind(this)).catch(function(t){return console.debug('[WCF.Attachment] Failed to resize image "'+e.name+'":',t),e});return Promise.race([a,i]).then(function(e){return t.push(e),t})}.bind(this))}.bind(this),Promise.resolve([]))}d.then(function(e){var a=void 0;return this._validateLimit()&&(a=i(t,void 0,void 0,e)),this._fileUpload&&(this._removeButton(),this._createButton()),a}.bind(this)).catch(function(t){console.debug("[WCF.Attachment] Failed to upload attachments:",t)}).finally(l.hide)}.bind(this),function(t){console.debug("[WCF.Attachment] Failed to load modules:",t)})},_createUploadMatrix:function(t){return this._fileListSelector.children("li.uploadFailed").remove(),this._super(t)},_getParameters:function(){return{objectType:this._objectType,objectID:this._objectID,tmpHash:this._tmpHash,parentObjectID:this._parentObjectID}},_initFile:function(t){var e=$('<li class="box64"><span class="icon icon64 fa-spinner" /><div><div><p>'+t.name+'</p><small><progress max="100"></progress></small></div><ul></ul></div></li>').data("filename",t.name);return this._fileListSelector.append(e),this._fileListSelector.show(),this._buttonSelector.data("maxSize")<t.size&&(e.find("progress").remove(),e.children(".fa-spinner").removeClass("fa-spinner").addClass("fa-ban"),e.find("div > div").append($('<small class="innerError">'+WCF.Language.get("wcf.attachment.upload.error.tooLarge")+"</small>")),e.addClass("uploadFailed")),e},_useThumbnail:function(){return elDataBool(this._fileListSelector[0],"enable-thumbnails")},_success:function(t,e){var a;for(var i in this._uploadMatrix[t])if(this._uploadMatrix[t].hasOwnProperty(i)){var s=this._uploadMatrix[t][i];s.find("progress").remove();var n=s.data("filename"),r=s.data("internalFileID");if(e.returnValues&&e.returnValues.attachments[r]){a=e.returnValues.attachments[r],a.tinyURL?(s.children(".fa-spinner").replaceWith($('<img src="'+a.tinyURL+'" alt="" class="attachmentTinyThumbnail" />')),s.data("height",a.height),s.data("width",a.width),elData(s[0],"is-image",a.isImage)):s.children(".fa-spinner").removeClass("fa-spinner").addClass("fa-"+a.iconName);var l=$('<a href=""></a>');l.text(n).attr("href",a.url),l[0].target="_blank",0!=a.isImage&&l.addClass("jsImageViewer").attr("title",n),s.find("p").empty().append(l),s.find("small").append(a.formattedFilesize);var o=s.find("ul").addClass("buttonGroup"),d=$('<li><span class="button small jsDeleteButton" data-object-id="'+a.attachmentID+'" data-confirm-message="'+WCF.Language.get("wcf.attachment.delete.sure")+'" data-event-name="attachment_'+this._editorId+'">'+WCF.Language.get("wcf.global.button.delete")+"</span></li>");if(o.append(d),s.data("objectID",a.attachmentID),this._editorId)if(a.tinyURL||!this._useThumbnail()&&a.isImage){if(a.thumbnailURL){var c=$('<li><span class="button small jsButtonAttachmentInsertThumbnail" data-object-id="'+a.attachmentID+'" data-url="'+WCF.String.escapeHTML(a.thumbnailURL)+'">'+WCF.Language.get("wcf.attachment.insertThumbnail")+"</span></li>").appendTo(o);c.children("span.button").click($.proxy(this._insert,this))}var h=$('<li><span class="button small jsButtonAttachmentInsertFull" data-object-id="'+a.attachmentID+'" data-url="'+WCF.String.escapeHTML(a.url)+'">'+WCF.Language.get("wcf.attachment.insertFull")+"</span></li>").appendTo(o);h.children("span.button").click($.proxy(this._insert,this))}else{var m=$('<li><span class="button small jsButtonAttachmentInsertPlain" data-object-id="'+a.attachmentID+'">'+WCF.Language.get("wcf.attachment.insert")+"</span></li>");m.appendTo(o).children("span.button").click($.proxy(this._insert,this))}if(this._replaceOnLoad.hasOwnProperty(t)){if(!s.hasClass("uploadFailed")){var u=this._replaceOnLoad[t];u&&u.parentNode&&WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","replaceAttachment_"+this._editorId,{attachmentId:a.attachmentID,img:u,src:a.thumbnailURL?a.thumbnailURL:a.url})}this._replaceOnLoad[t]=null}}else{s.children(".fa-spinner").removeClass("fa-spinner").addClass("fa-ban");var p="";if(e.returnValues&&e.returnValues.errors[r]){var f=e.returnValues.errors[r];p=f.errorType,"uploadFailed"===p&&f.additionalData.phpLimitExceeded&&(p="uploadPhpLimit")}else p="uploadFailed";s.find("div > div").append($('<small class="innerError">'+WCF.Language.get("wcf.attachment.upload.error."+p)+"</small>")),s.addClass("uploadFailed")}if(WCF.inArray(t,this._autoInsert)&&(this._autoInsert.splice(this._autoInsert.indexOf(t),1),!s.hasClass("uploadFailed"))){var _=s.find(".jsButtonAttachmentInsertThumbnail");_.length||(_=s.find(".jsButtonAttachmentInsertFull")),_.trigger("click")}}this._makeSortable(),this._fileListSelector.children("li:not(.uploadFailed)").length?this._insertAllButton.show():this._insertAllButton.hide(),WCF.DOMNodeInsertedHandler.execute()},_insert:function(t){WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","insertAttachment_"+this._editorId,{attachmentId:elData(t.currentTarget,"object-id"),url:elData(t.currentTarget,"url")})},_insertAll:function(){var t=this._useThumbnail()?".jsButtonAttachmentInsertThumbnail, .jsButtonAttachmentInsertPlain":".jsButtonAttachmentInsertFull, .jsButtonAttachmentInsertPlain";this._fileListSelector.children("li:not(.uploadFailed)").find(t).trigger("click")},_error:function(t){this._fileListSelector.find("li").each(function(e,a){var i=$(a);i.children(".fa-spinner").length&&(i.addClass("uploadFailed").children(".fa-spinner").removeClass("fa-spinner").addClass("fa-ban"),i.find("div > div").append($('<small class="innerError">'+(t.responseJSON&&t.responseJSON.message?t.responseJSON.message:WCF.Language.get("wcf.attachment.upload.error.uploadFailed"))+"</small>")))})},_makeSortable:function(){var t=this._fileListSelector.children("li:not(.uploadFailed)");t.length&&(t.addClass("sortableAttachment").children("img").addClass("sortableNode"),this._fileListSelector.hasClass("sortableList")||(this._fileListSelector.addClass("sortableList"),require(["Environment"],function(t){"desktop"===t.platform()&&new WCF.Sortable.List(this._fileListSelector.parent().wcfIdentify(),"",0,{axis:!1,items:"li.sortableAttachment",toleranceElement:null,start:function(t,e){e.placeholder[0].style.setProperty("height",e.helper[0].offsetHeight+"px","")},update:function(){var t=[];this._fileListSelector.children("li:not(.uploadFailed)").each(function(e,a){t.push($(a).data("objectID"))}),t.length&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"updatePosition",className:"wcf\\data\\attachment\\AttachmentAction",parameters:{attachmentIDs:t,objectID:this._objectID,objectType:this._objectType,tmpHash:this._tmpHash}}})}.bind(this)},!0)}.bind(this))))}}); })(this);
// WCF.ColorPicker.js
(function (window, undefined) { "use strict";WCF.ColorPicker=Class.extend({_bar:null,_barActive:!1,_barSelector:null,_callbackSubmit:null,_dialog:null,_didInit:!1,_elementID:"",_gradient:null,_gradientActive:!1,_gradientSelector:null,_hex:null,_hsv:{},_newColor:null,_oldColor:null,_rgba:{},_rgbaRegExp:null,init:function(t){this._callbackSubmit=null,this._elementID="",this._hsv={h:0,s:100,v:100},this._position={};var a=$(t);if(!a.length)return void console.debug("[WCF.ColorPicker] Selector does not match any element, aborting.");a.click($.proxy(this._open,this))},setCallbackSubmit:function(t){this._callbackSubmit=t},_open:function(t){this._didInit||(this._initColorPicker(),this._didInit=!0);var a=$(t.currentTarget);this._elementID=a.wcfIdentify(),this._parseColor(a);var i=this.hsvToRgb(this._hsv.h,this._hsv.s,this._hsv.v);this._oldColor.css({backgroundColor:"rgba("+i.r+", "+i.g+", "+i.b+", "+this._rgba.a.val()/100+")"}),this._dialog.wcfDialog({backdropCloseOnClick:!1,title:WCF.Language.get("wcf.style.colorPicker")}),window.setTimeout(function(){this._hex.focus()}.bind(this),200)},_parseColor:function(t){if(t.data("hsv")&&t.data("rgb")){var a=t.data("hsv");for(var i in a)this._hsv[i]=a[i];this._updateValues(t.data("rgb"),!0,!0),this._rgba.a.val(parseInt(t.data("alpha")))}else{t.data("color").match(/^rgb\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)$/)&&t.data("color","rgba("+RegExp.$1+", "+RegExp.$2+", "+RegExp.$3+", 1)"),null===this._rgbaRegExp&&(this._rgbaRegExp=new RegExp("^rgba\\((\\d{1,3}), ?(\\d{1,3}), ?(\\d{1,3}), ?(1|1\\.00?|0|0?\\.[0-9]{1,2})\\)$")),this._rgbaRegExp.exec(t.data("color"));var s=RegExp.$4;0===s.indexOf(".")&&(s="0"+s),s*=100,this._updateValues({r:RegExp.$1,g:RegExp.$2,b:RegExp.$3,a:Math.round(s)},!0,!0)}},_initColorPicker:function(){this._dialog=$('<div id="colorPickerContainer" />').hide().appendTo(document.body),this._gradient=$('<div id="colorPickerGradient" />').appendTo(this._dialog),this._gradientSelector=$('<span id="colorPickerGradientSelector"><span></span></span>').appendTo(this._gradient),this._bar=$('<div id="colorPickerBar" />').appendTo(this._dialog),this._barSelector=$('<span id="colorPickerBarSelector" />').appendTo(this._bar),this._gradient.mousedown($.proxy(this._mouseDownGradient,this)),this._bar.mousedown($.proxy(this._mouseDownBar,this));var t=this;$(document).mouseup(function(a){t._barActive?(t._barActive=!1,t._mouseBar(a)):t._gradientActive&&(t._gradientActive=!1,t._mouseGradient(a))}).mousemove(function(a){t._barActive?t._mouseBar(a):t._gradientActive&&t._mouseGradient(a)}),this._initColorPickerForm()},_initColorPickerForm:function(){var t=$('<div id="colorPickerForm" />').appendTo(this._dialog);$("<small>"+WCF.Language.get("wcf.style.colorPicker.new")+"</small>").appendTo(t);var a=$('<ul class="colors" />').appendTo(t);this._newColor=$('<li class="new"><span /></li>').appendTo(a).children("span"),this._oldColor=$('<li class="old"><span /></li>').appendTo(a).children("span"),$("<small>"+WCF.Language.get("wcf.style.colorPicker.current")+"</small>").appendTo(t);var i=$('<ul class="rgba" />').appendTo(t);this._createInputElement("r","R",0,255).appendTo(i),this._createInputElement("g","G",0,255).appendTo(i),this._createInputElement("b","B",0,255).appendTo(i),this._createInputElement("a","a",0,100).appendTo(i);var s=$('<ul class="hex"><li><label><span>#</span></label></li></ul>').appendTo(t);this._hex=$('<input type="text" maxlength="6" />').appendTo(s.find("label")),this._rgba.r.blur($.proxy(this._blurRgba,this)).keyup($.proxy(this._keyUpRGBA,this)),this._rgba.g.blur($.proxy(this._blurRgba,this)).keyup($.proxy(this._keyUpRGBA,this)),this._rgba.b.blur($.proxy(this._blurRgba,this)).keyup($.proxy(this._keyUpRGBA,this)),this._rgba.a.blur($.proxy(this._blurRgba,this)).keyup($.proxy(this._keyUpRGBA,this)),this._hex.blur($.proxy(this._blurHex,this)).keyup($.proxy(this._keyUpHex,this));var e=$('<div class="formSubmit" />').appendTo(this._dialog);$('<button class="buttonPrimary">'+WCF.Language.get("wcf.style.colorPicker.button.apply")+"</button>").appendTo(e).click($.proxy(this._submit,this));var r=this;this._hex.on("paste",function(){r._hex.attr("maxlength","7"),setTimeout(function(){var t=r._hex.val();"#"==t.substring(0,1)&&(t=t.substr(1)),t.length>6&&(t=t.substring(0,6)),r._hex.attr("maxlength","6").val(t)},50)}),t.find("input").focus(function(){this.select()})},_keyUpRGBA:function(t){13==t.which&&(this._blurRgba(),this._submit())},_keyUpHex:function(t){13==t.which&&(this._blurHex(),this._submit())},_submit:function(){var t=this.hsvToRgb(this._hsv.h,this._hsv.s,this._hsv.v),a={};for(var i in this._hsv)a[i]=this._hsv[i];var s=$("#"+this._elementID);s.data("hsv",a).css({backgroundColor:"rgba("+t.r+", "+t.g+", "+t.b+", "+this._rgba.a.val()/100+")"}).data("alpha",parseInt(this._rgba.a.val())),s.data("rgb",{r:this._rgba.r.val(),g:this._rgba.g.val(),b:this._rgba.b.val()}),$("#"+s.data("store")).val("rgba("+this._rgba.r.val()+", "+this._rgba.g.val()+", "+this._rgba.b.val()+", "+this._rgba.a.val()/100+")").trigger("change"),this._dialog.wcfDialog("close"),"function"==typeof this._callbackSubmit&&this._callbackSubmit({r:this._rgba.r.val(),g:this._rgba.g.val(),b:this._rgba.b.val(),a:this._rgba.a.val()/100})},_createInputElement:function(t,a,i,s){var e=$('<li class="'+t+'" />'),r=$("<label />").appendTo(e);return $("<span>"+a+"</span>").appendTo(r),this._rgba[t]=$('<input type="number" value="0" min="'+i+'" max="'+s+'" step="1" />').appendTo(r),e},_mouseDownGradient:function(t){this._gradientActive=!0,this._mouseGradient(t)},_mouseGradient:function(t){var a=this._gradient.getOffsets("offset"),i=Math.max(Math.min(t.pageX-a.left,255),0),s=Math.max(Math.min(t.pageY-a.top,255),0);this._hsv.s=100*Math.max(0,Math.min(1,i/255)),this._hsv.v=100*Math.max(0,Math.min(1,(255-s)/255)),this._updateValues(null)},_mouseDownBar:function(t){this._barActive=!0,this._mouseBar(t)},_mouseBar:function(t){var a=this._bar.getOffsets("offset"),i=Math.max(Math.min(t.pageY-a.top,255),0);this._barSelector.css({top:i+"px"}),this._hsv.h=Math.max(0,Math.min(359,Math.round((255-i)/255*360))),this._updateValues(null)},_blurRgba:function(){for(var t in this._rgba){var a=parseInt(this._rgba[t].val())||0;"a"===t?this._rgba[t].val(Math.max(0,Math.min(100,a))):this._rgba[t].val(Math.max(0,Math.min(255,a)))}this._updateValues({r:this._rgba.r.val(),g:this._rgba.g.val(),b:this._rgba.b.val()},!0,!0)},_blurHex:function(){var t=this.hexToRgb(this._hex.val());t!==Number.NaN&&this._updateValues(t,!0,!0)},_updateValues:function(t,a,i){a=!0===a,i=!0===i,null===t&&(t=this.hsvToRgb(this._hsv.h,this._hsv.s,this._hsv.v),0==this._rgba.a.val()&&(t.a=100)),void 0===t.a&&(t.a=this._rgba.a.val());for(var s in t)this._rgba[s].val(t[s]);if(this._hex.val(this.rgbToHex(t.r,t.g,t.b)),a||i){var e=this.rgbToHsv(t.r,t.g,t.b);a&&(this._hsv.h=e.h),i&&(this._hsv.s=e.s,this._hsv.v=e.v)}var r=Math.max(0,Math.min(255,255-this._hsv.h/360*255));this._barSelector.css({top:r+"px"});var o=Math.max(0,Math.min(255,this._hsv.s/100*255)),r=Math.max(0,Math.min(255,255-this._hsv.v/100*255));this._gradientSelector.css({left:o-6+"px",top:r-6+"px"}),this._newColor.css({backgroundColor:"rgba("+t.r+", "+t.g+", "+t.b+", "+t.a/100+")"});var h=this.hsvToRgb(this._hsv.h,100,100);this._gradient.css({backgroundColor:"rgb("+h.r+", "+h.g+", "+h.b+")"})},hsvToRgb:function(t,a,i){return window.__wcf_bc_colorUtil.hsvToRgb(t,a,i)},rgbToHsv:function(t,a,i){return window.__wcf_bc_colorUtil.rgbToHsv(t,a,i)},hexToRgb:function(t){return window.__wcf_bc_colorUtil.hexToRgb(t)},rgbToHex:function(t,a,i){return window.__wcf_bc_colorUtil.rgbToHex(t,a,i)}}),function(){void 0===window.__wcf_bc_colorUtil&&require(["ColorUtil"],function(t){}),"function"==typeof window.__wcf_bc_colorPickerInit&&window.__wcf_bc_colorPickerInit()}(); })(this);
(function (window, undefined) { "use strict";WCF.Comment={},WCF.Comment.Handler=Class.extend({_commentButtonList:{},_comments:{},_container:null,_containerID:"",_displayedComments:0,_loadNextComments:null,_loadNextResponses:{},_proxy:null,_responses:{},_responseCache:{},_commentData:{},_guestDialog:null,_permalinkComment:null,_permalinkResponse:null,_scrollTarget:null,init:function(e){if(this._commentButtonList={},this._comments={},this._containerID=e,this._displayedComments=0,this._loadNextComments=null,this._loadNextResponses={},this._permalinkComment=null,this._permalinkResponse=null,this._responseAdd=null,this._responseCache={},this._responseRevert=null,this._responses={},this._scrollTarget=null,this._onResponsesLoaded=null,this._container=$("#"+$.wcfEscapeID(this._containerID)),!this._container.length)return void console.debug("[WCF.Comment.Handler] Unable to find container identified by '"+this._containerID+"'");if(this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initComments(),this._initResponses(),this._container.data("canAdd")&&(null===elBySel(".commentListAddComment .wysiwygTextarea",this._container[0])?console.error("Missing WYSIWYG implementation, adding comments is not available."):require(["WoltLabSuite/Core/Ui/Comment/Add","WoltLabSuite/Core/Ui/Comment/Response/Add"],function(e,t){new e(elBySel(".jsCommentAdd",this._container[0])),this._responseAdd=new t(elBySel(".jsCommentResponseAdd",this._container[0]),{callbackInsert:function(){null!==this._responseRevert&&(this._responseRevert(),this._responseRevert=null)}.bind(this)})}.bind(this))),require(["WoltLabSuite/Core/Ui/Comment/Edit","WoltLabSuite/Core/Ui/Comment/Response/Edit"],function(e,t){new e(this._container[0]),new t(this._container[0])}.bind(this)),WCF.DOMNodeInsertedHandler.execute(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Comment.Handler",$.proxy(this._domNodeInserted,this)),WCF.System.ObjectStore.add("WCF.Comment.Handler",this),window.addEventListener("hashchange",function(){var e=window.location.hash;if(e&&e.match(/.+\/(comment\d+)/)){var t=RegExp.$1;window.setTimeout(function(){var e=elById(t);e&&e.scrollIntoView({behavior:"smooth"})},100)}}),window.location.hash.match(/^#(?:[^\/]+\/)?comment(\d+)(?:\/response(\d+))?/)){var t=elById("comment"+RegExp.$1);if(t){var n;RegExp.$2?(n=elById("comment"+RegExp.$1+"response"+RegExp.$2),n?this._scrollTo(n,!0):this._loadResponseSegment(t,RegExp.$1,RegExp.$2)):this._scrollTo(t,!0)}else this._loadCommentSegment(RegExp.$1,RegExp.$2)}},_scrollTo:function(e,t){null===this._scrollTarget&&(this._scrollTarget=elCreate("span"),this._scrollTarget.className="commentScrollTarget",document.body.appendChild(this._scrollTarget)),this._scrollTarget.style.setProperty("top",e.getBoundingClientRect().top+window.pageYOffset-49+"px",""),require(["Ui/Scroll"],function(n){n.element(this._scrollTarget,function(){t&&(e.classList.contains("commentHighlightTarget")&&(e.classList.remove("commentHighlightTarget"),e.offsetTop),e.classList.add("commentHighlightTarget"))})}.bind(this))},_loadCommentSegment:function(e,t){this._permalinkComment=elCreate("li"),this._permalinkComment.className="commentPermalinkContainer loading",this._permalinkComment.innerHTML='<span class="icon icon48 fa-spinner"></span>',this._container[0].insertBefore(this._permalinkComment,this._container[0].firstChild),this._proxy.setOption("data",{actionName:"loadComment",className:"wcf\\data\\comment\\CommentAction",objectIDs:[e],parameters:{data:{objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID"),responseID:~~t}}}),this._proxy.sendRequest()},_loadResponseSegment:function(e,t,n){this._permalinkResponse=elCreate("li"),this._permalinkResponse.className="commentResponsePermalinkContainer loading",this._permalinkResponse.innerHTML='<span class="icon icon32 fa-spinner"></span>';var s=elBySel(".commentResponseList",e);s.insertBefore(this._permalinkResponse,s.firstChild),this._proxy.setOption("data",{actionName:"loadResponse",className:"wcf\\data\\comment\\CommentAction",objectIDs:[t],parameters:{data:{objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID"),responseID:~~n}}}),this._proxy.sendRequest()},_handleLoadNextComments:function(){this._displayedComments<this._container.data("comments")?(null===this._loadNextComments&&(this._loadNextComments=$('<li class="commentLoadNext showMore"><button class="small">'+WCF.Language.get("wcf.comment.more")+"</button></li>").appendTo(this._container),this._loadNextComments.children("button").click($.proxy(this._loadComments,this))),this._loadNextComments.children("button").enable()):null!==this._loadNextComments&&this._loadNextComments.remove()},_handleLoadNextResponses:function(e){var t=this._comments[e];if(t.data("displayedResponses",t.find("ul.commentResponseList > li").length),t.data("displayedResponses")<t.data("responses")){if(void 0===this._loadNextResponses[e]){var n=t.data("responses")-t.data("displayedResponses");this._loadNextResponses[e]=$('<li class="jsCommentLoadNextResponses"><a>'+WCF.Language.get("wcf.comment.response.more",{count:n})+"</a></li>").appendTo(this._commentButtonList[e]),this._loadNextResponses[e].children("a").data("commentID",e).click($.proxy(this._loadResponses,this)),this._commentButtonList[e].parent().show()}}else void 0!==this._loadNextResponses[e]&&this._loadNextResponses[e].remove()},_loadComments:function(){this._loadNextComments.children("button").disable(),this._proxy.setOption("data",{actionName:"loadComments",className:"wcf\\data\\comment\\CommentAction",parameters:{data:{objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID"),lastCommentTime:this._container.data("lastCommentTime")}}}),this._proxy.sendRequest()},_loadResponses:function(e){this._loadResponsesExecute($(e.currentTarget).disable().data("commentID"),!1)},_loadResponsesExecute:function(e,t){this._proxy.setOption("data",{actionName:"loadResponses",className:"wcf\\data\\comment\\response\\CommentResponseAction",parameters:{data:{commentID:e,lastResponseTime:this._comments[e].data("lastResponseTime"),loadAllResponses:t?1:0}}}),this._proxy.sendRequest()},_domNodeInserted:function(){this._initComments(),this._initResponses()},_initComments:function(){var e=elBySel('link[rel="canonical"]');e=e?e.href:window.location.toString().replace(/#.+$/,"");var t=this._container[0].closest(".tabMenuContent");t&&(e+="#"+elData(t,"name"));var n=this,s=!1;this._container.find(".jsComment").each(function(t,o){var i=$(o).removeClass("jsComment"),a=i.data("commentID");n._comments[a]=i,i[0].id="comment"+a;var l=i.find("ul.commentResponseList");l.length||(l=i.find(".commentContent"));var r=$('<div class="commentOptionContainer" />').hide().insertAfter(l);n._commentButtonList[a]=$('<ul class="inlineList dotSeparated" />').appendTo(r),n._handleLoadNextResponses(a),n._initComment(a,i),n._initPermalink(i[0],e),n._displayedComments++,s=!0}),s&&this._handleLoadNextComments()},_initComment:function(e,t){if(this._container.data("canAdd")&&this._initAddResponse(e,t),t.data("canEdit")){$('<li><a href="#" class="jsCommentEditButton jsTooltip" title="'+WCF.Language.get("wcf.global.button.edit")+'"><span class="icon icon16 fa-pencil" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.edit")+"</span></a></li>").appendTo(t.find("ul.buttonList:eq(0)"))}if(t.data("canDelete")){$('<li><a href="#" class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.delete")+'"><span class="icon icon16 fa-times" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.delete")+"</span></a></li>").data("commentID",e).appendTo(t.find("ul.buttonList:eq(0)")).click($.proxy(this._delete,this))}var n=elBySel(".jsEnableComment",t[0]);n&&n.addEventListener(WCF_CLICK_EVENT,this._enableComment.bind(this))},_enableComment:function(e){e.preventDefault();var t=e.currentTarget.closest(".comment");this._proxy.setOption("data",{actionName:"enable",className:"wcf\\data\\comment\\CommentAction",objectIDs:[elData(t,"object-id")]}),this._proxy.sendRequest()},_initPermalink:function(e,t){var n=elCreate("a");n.href=t+(-1===t.indexOf("#")?"#":"/")+"comment"+elData(e,"object-id");var s=elBySel(".commentContent:not(.commentResponseContent) .containerHeadline time",e);s.parentNode.insertBefore(n,s),n.appendChild(s)},_initResponses:function(){var e=elBySel('link[rel="canonical"]');e=e?e.href:window.location.toString().replace(/#.+$/,"");var t=this._container[0].closest(".tabMenuContent");t&&(e+="#"+elData(t,"name"));for(var n in this._comments)this._comments.hasOwnProperty(n)&&elBySelAll(".jsCommentResponse",this._comments[n][0],function(t){var s=$(t).removeClass("jsCommentResponse"),o=s.data("responseID");this._responses[o]=s,t.id="comment"+n+"response"+o,this._initResponse(o,s),this._initPermalinkResponse(n,t,o,e);var i=elBySel(".jsEnableResponse",t);i&&i.addEventListener(WCF_CLICK_EVENT,this._enableCommentResponse.bind(this))}.bind(this))},_enableCommentResponse:function(e){e.preventDefault();var t=e.currentTarget.closest(".commentResponse");this._proxy.setOption("data",{actionName:"enableResponse",className:"wcf\\data\\comment\\CommentAction",parameters:{data:{responseID:elData(t,"object-id")}}}),this._proxy.sendRequest()},_initPermalinkResponse:function(e,t,n,s){var o=elCreate("a");o.href=s+(-1===s.indexOf("#")?"#":"/")+"comment"+e+"/response"+n;var i=elBySel(".commentResponseContent .containerHeadline time",t);i.parentNode.insertBefore(o,i),o.appendChild(i)},_initResponse:function(e,t){if(t.data("canEdit")){$('<li><a href="#" class="jsCommentResponseEditButton jsTooltip" title="'+WCF.Language.get("wcf.global.button.edit")+'"><span class="icon icon16 fa-pencil" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.edit")+"</span></a></li>").data("responseID",e).appendTo(t.find("ul.buttonList:eq(0)"))}if(t.data("canDelete")){var n=this;$('<li><a href="#" class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.delete")+'"><span class="icon icon16 fa-times" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.delete")+"</span></a></li>").data("responseID",e).appendTo(t.find("ul.buttonList:eq(0)")).click(function(e){n._delete(e,!0)})}},_initAddResponse:function(e,t){$('<li class="jsCommentShowAddResponse"><a>'+WCF.Language.get("wcf.comment.button.response.add")+"</a></li>").data("commentID",e).click($.proxy(this._showAddResponse,this)).appendTo(this._commentButtonList[e]);this._commentButtonList[e].parent().show()},_showAddResponse:function(e){if(e.preventDefault(),null===this._onResponsesLoaded){if(null===this._responseAdd)return void console.error("Missing response API.");var t=this._responseAdd.getContainer();if(null!==t){null!==this._responseRevert&&(this._responseRevert(),this._responseRevert=null);var n=$(e.currentTarget),s=n.data("commentID");this._onResponsesLoaded=function(){n.hide(),t.parentNode&&t.parentNode.classList.contains("jsCommentResponseAddContainer")&&elRemove(t.parentNode);var e=this._commentButtonList[s][0].closest(".commentOptionContainer");e.parentNode.insertBefore(t,e.nextSibling),"string"==typeof this._responseCache[s]?this._responseAdd.setContent(this._responseCache[s]):this._responseAdd.setContent(""),this._responseRevert=function(){this._responseCache[s]=this._responseAdd.getContent(),elRemove(t),n.show()}.bind(this),this._onResponsesLoaded=null}.bind(this),n.prev().hasClass("jsCommentLoadNextResponses")?(this._loadResponsesExecute(s,!0),n.parent().children(".button").disable()):this._onResponsesLoaded()}}},_delete:function(e,t){e.preventDefault(),WCF.System.Confirmation.show(WCF.Language.get("wcf.comment.delete.confirmMessage"),$.proxy(function(n){if("confirm"===n){var s={objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID")};!0!==t?s.commentID=$(e.currentTarget).data("commentID"):s.responseID=$(e.currentTarget).data("responseID"),this._proxy.setOption("data",{actionName:"remove",className:"wcf\\data\\comment\\CommentAction",parameters:{data:s}}),this._proxy.sendRequest()}},this))},_success:function(e,t,n){switch(e.actionName){case"enable":this._enable(e);break;case"enableResponse":this._enableResponse(e);break;case"loadComment":this._insertComment(e);break;case"loadComments":this._insertComments(e);break;case"loadResponse":this._insertResponse(e);break;case"loadResponses":this._insertResponses(e);break;case"remove":this._remove(e)}WCF.DOMNodeInsertedHandler.execute()},_enable:function(e){if(e.returnValues.commentID){var t=elBySel('.comment[data-object-id="'+e.returnValues.commentID+'"]',this._container[0]);if(t){elData(t,"is-disabled",0);var n=elBySel(".jsIconDisabled",t);n&&elRemove(n);var s=elBySel(".jsEnableComment",t);s&&elRemove(s.parentNode)}}},_enableResponse:function(e){if(e.returnValues.responseID){var t=elBySel('.commentResponse[data-object-id="'+e.returnValues.responseID+'"]',this._container[0]);if(t){elData(t,"is-disabled",0);var n=elBySel(".jsIconDisabled",t);n&&elRemove(n);var s=elBySel(".jsEnableResponse",t);s&&elRemove(s.parentNode)}}},_insertComment:function(e){if(""===e.returnValues.template)return void elRemove(this._permalinkComment);$(e.returnValues.template).insertBefore(this._permalinkComment);var t=this._permalinkComment.previousElementSibling;if(t.classList.add("commentPermalinkContainer"),elRemove(this._permalinkComment),this._permalinkComment=t,e.returnValues.response){this._permalinkResponse=elCreate("li"),this._permalinkResponse.className="commentResponsePermalinkContainer loading",this._permalinkResponse.innerHTML='<span class="icon icon32 fa-spinner"></span>';var n=elBySel(".commentResponseList",t);n.insertBefore(this._permalinkResponse,n.firstChild),this._insertResponse({returnValues:{template:e.returnValues.response}})}t.offsetTop,t.classList.add("commentHighlightTarget")},_insertResponse:function(e){if(""===e.returnValues.template)return void elRemove(this._permalinkResponse);$(e.returnValues.template).insertBefore(this._permalinkResponse);var t=this._permalinkResponse.previousElementSibling;t.classList.add("commentResponsePermalinkContainer"),elRemove(this._permalinkResponse),this._permalinkResponse=t,t.offsetTop,t.classList.add("commentHighlightTarget")},_insertComments:function(e){if($(e.returnValues.template).insertBefore(this._loadNextComments),this._container.data("lastCommentTime",e.returnValues.lastCommentTime),this._permalinkComment){var t=elData(this._permalinkComment,"object-id");null!==elBySel('.comment[data-object-id="'+t+'"]:not(.commentPermalinkContainer)',this._container[0])&&(elRemove(this._permalinkComment),this._permalinkComment=null)}this._initComments()},_insertResponses:function(e){var t=this._comments[e.returnValues.commentID];if($(e.returnValues.template).appendTo(t.find("ul.commentResponseList")),t.data("lastResponseTime",e.returnValues.lastResponseTime),this._handleLoadNextResponses(e.returnValues.commentID),this._permalinkResponse){var n=elData(this._permalinkResponse,"object-id");null!==elBySel('.commentResponse[data-object-id="'+n+'"]:not(.commentPermalinkContainer)',this._container[0])&&(elRemove(this._permalinkResponse),this._permalinkResponse=null)}null!==this._onResponsesLoaded&&this._onResponsesLoaded()},_remove:function(e){if(e.returnValues.commentID)this._comments[e.returnValues.commentID].remove(),delete this._comments[e.returnValues.commentID];else{var t=this._responses[e.returnValues.responseID],n=this._comments[t.parents("li.comment:eq(0)").data("commentID")];n.data("responses",parseInt(n.data("responses"))-1);var s=t.parent();t.remove(),s.children().length||s.empty(),delete this._responses[e.returnValues.responseID]}},_prepareEdit:function(){console.warn("This method is no longer supported.")},_keyUp:function(){console.warn("This method is no longer supported.")},_save:function(){console.warn("This method is no longer supported.")},_failure:function(){console.warn("This method is no longer supported.")},_edit:function(){console.warn("This method is no longer supported.")},_update:function(){console.warn("This method is no longer supported.")},_createGuestDialog:function(){console.warn("This method is no longer supported.")},_keyDown:function(){console.warn("This method is no longer supported.")},_submit:function(){console.warn("This method is no longer supported.")},_keyUpEdit:function(){console.warn("This method is no longer supported.")},_saveEdit:function(){console.warn("This method is no longer supported.")},_cancelEdit:function(){console.warn("This method is no longer supported.")}}),WCF.Comment.Response={}; })(this);
// WCF.ImageViewer.js
-(function (window, undefined) { "use strict";WCF.ImageViewer=Class.extend({_triggerElement:null,init:function(){this._triggerElement=$('<span class="wcfImageViewerTriggerElement" />').data("disableSlideshow",!0).hide().appendTo(document.body),this._triggerElement.wcfImageViewer({enableSlideshow:0,imageSelector:".jsImageViewerEnabled",staticViewer:!0}),WCF.DOMNodeInsertedHandler.addCallback("WCF.ImageViewer",$.proxy(this._domNodeInserted,this)),WCF.DOMNodeInsertedHandler.execute()},_domNodeInserted:function(){this._initImageSizeCheck(),this._rebuildImageViewer()},_rebuildImageViewer:function(){var i=$("a.jsImageViewer");i.length&&i.removeClass("jsImageViewer").addClass("jsImageViewerEnabled").click($.proxy(this._click,this))},_click:function(i){i.ctrlKey||(i.preventDefault(),i.stopPropagation(),$(i.currentTarget).closest(".popover").length||this._triggerElement.wcfImageViewer("open",null,$(i.currentTarget).wcfIdentify()))},_initImageSizeCheck:function(){$(".jsResizeImage").each($.proxy(function(i,e){e.complete&&this._checkImageSize({currentTarget:e})},this)),$(".jsResizeImage").on("load",$.proxy(this._checkImageSize,this))},_checkImageSize:function(i){var e=$(i.currentTarget);if(!e.is(":visible"))return void e.off("load");if(e.removeClass("jsResizeImage"),!e.closest(".messageSignature").length){var t=new Image;t.src=e.attr("src");e.closest("div.messageText, div.messageTextPreview").width()<t.width?e.parents("a").length||(e.wrap('<a href="'+e.attr("src")+'" class="jsImageViewerEnabled embeddedImageLink" />'),e.parent().click($.proxy(this._click,this)),"right"==e.css("float")?e.parent().addClass("messageFloatObjectRight"):"left"==e.css("float")&&e.parent().addClass("messageFloatObjectLeft"),e[0].style.removeProperty("float"),e[0].style.removeProperty("margin")):e.removeClass("embeddedAttachmentLink")}}}),$.widget("ui.wcfImageViewer",{_active:-1,_activeImage:null,_container:null,_didInit:!1,_disableSlideshow:!1,_eventNamespace:"",_images:[],_isMobile:!1,_isOpen:!1,_items:-1,_maxDimensions:{height:0,width:0},_proxy:null,_slideshowEnabled:!1,_thumbnailContainerWidth:0,_thumbnailMarginRight:0,_thumbnailOffset:0,_thumbnailWidth:0,_timer:null,_ui:{buttonNext:null,buttonPrevious:null,header:null,image:null,imageContainer:null,imageList:null,slideshow:{container:null,enlarge:null,next:null,previous:null,toggle:null}},options:{shiftBy:5,enableSlideshow:1,speed:5,className:"",imageSelector:"",staticViewer:!1},_create:function(){this._active=-1,this._activeImage=null,this._container=null,this._didInit=!1,this._disableSlideshow=this.element.data("disableSlideshow"),this._eventNamespace=this.element.wcfIdentify(),this._images=[],this._isMobile=!1,this._isOpen=!1,this._items=-1,this._maxDimensions={height:document.documentElement.clientHeight,width:document.documentElement.clientWidth},this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._slideshowEnabled=!1,this._thumbnailContainerWidth=0,this._thumbnailMarginRight=0,this._thumbnailOffset=0,this._thumbnaiLWidth=0,this._timer=null,this._ui={},this.element.click($.proxy(this.open,this)),window.addEventListener("popstate",function(i){if(null!=i.state&&"imageViewer"===i.state.name&&i.state.container===this._eventNamespace)return this.open(i),void this.showImage(i.state.image);this.close(i)}.bind(this))},open:function(i,e){if(i&&i.preventDefault(),this._isOpen)return!1;if(i&&"popstate"===i.type||window.history.pushState({name:"imageViewer"},"",""),this.options.staticViewer){var t=this._getStaticImages();this._initUI(),this._createThumbnails(t,!0),this._render(!0,void 0,e),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable(),$.browser.touch&&setTimeout($.proxy(function(){this._isMobile&&!this._container.hasClass("maximized")&&this._toggleView()},this),500)}else 0===this._images.length?this._loadNextImages(!0):(this._render(!1,this.element.data("targetImageID")),this._items>1&&this._slideshowEnabled&&this.startSlideshow(),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable());return this._bindListener(),document.documentElement.classList.add("pageOverlayActive"),!0},close:function(i){return i&&i.preventDefault(),i&&"popstate"===i.type?!!this._isOpen&&(this._container.removeClass("open"),null!==this._timer&&this._timer.stop(),this._unbindListener(),this._isOpen=!1,WCF.System.DisableScrolling.enable(),WCF.System.DisableZoom.enable(),document.documentElement.classList.remove("pageOverlayActive"),!0):void window.history.back()},startSlideshow:function(){return!this._disableSlideshow&&!this._slideshowEnabled&&(null===this._timer?this._timer=new WCF.PeriodicalExecuter($.proxy(function(){var i=this._active+1;i==this._items&&(i=0),this.showImage(i)},this),1e3*this.options.speed):this._timer.resume(),this._slideshowEnabled=!0,this._ui.slideshow.toggle.children("span").removeClass("fa-play").addClass("fa-pause"),!0)},stopSlideshow:function(i){return!!this._slideshowEnabled&&(this._timer.stop(),i&&this._ui.slideshow.toggle.children("span").removeClass("fa-pause").addClass("fa-play"),this._slideshowEnabled=!1,!0)},_bindListener:function(){$(document).on("keydown."+this._eventNamespace,$.proxy(this._keyDown,this)),$(window).on("resize."+this._eventNamespace,$.proxy(this._renderImage,this))},_unbindListener:function(){$(document).off("keydown."+this._eventNamespace),$(window).off("resize."+this._eventNamespace)},_keyDown:function(i){switch(i.which){case $.ui.keyCode.ESCAPE:this.close();break;case $.ui.keyCode.LEFT:this._previousImage();break;case $.ui.keyCode.RIGHT:this._nextImage();break;case $.ui.keyCode.UP:this._container.hasClass("maximized")||this._toggleView();break;case $.ui.keyCode.DOWN:this._container.hasClass("maximized")&&this._toggleView();break;case $.ui.keyCode.ENTER:var e=this._ui.header.find("h1 > a");1==e.length?window.location=e.prop("href"):this._ui.slideshow.full.trigger("click");break;case 80:this._ui.slideshow.toggle.trigger("click");break;default:return!0}return!1},_render:function(i,e,t){this._container.addClass("open");var s=null;if(i&&(s=this._ui.imageList.children("li:eq(0)"),this._thumbnailMarginRight=parseInt(s.css("marginRight").replace(/px$/,""))||0,this._thumbnailWidth=s.outerWidth(!0),this._thumbnailContainerWidth=this._ui.imageList.parent().innerWidth(),this._items>1&&this.options.enableSlideshow&&!e&&!t&&this.startSlideshow()),e)this._ui.imageList.children("li").each($.proxy(function(i,t){var s=$(t);if(s.data("objectID")==e)return s.trigger("click"),this.moveToImage(s.data("index")),!1},this));else if(t){var a=0;$(this.options.imageSelector).each(function(i,e){if($(e).wcfIdentify()==t)return a=i,!1});var n=this._ui.imageList.children("li:eq("+a+")");if(-1!==this._active){var h=!1;this._active!=n.data("index")&&(h=!0),this._ui.images[this._activeImage].prop("src")!=this._images[this._active].image.url&&(h=!0),h&&(this._active=-1)}n.trigger("click"),this.moveToImage(n.data("index"))}else null!==s&&s.trigger("click");this._toggleButtons(),this._preload()},_preload:function(){if(this._images.length<this._items){this._images.length*this._thumbnailWidth-this._thumbnailOffset<this._thumbnailContainerWidth&&this._loadNextImages(!1)}},_showImage:function(i){this.showImage($(i.currentTarget).data("index"),!0)},showImage:function(i,e){if(this._active==i)return!1;this.stopSlideshow(e||!1),-1!=this._active&&this._images[this._active].listItem.removeClass("active"),this._active=i,window.history.replaceState({name:"imageViewer",container:this._eventNamespace,image:this._active},"","");var t=this._images[i];this._ui.imageList.children("li").removeClass("active"),t.listItem.addClass("active");var s=this._ui.imageContainer.getDimensions("inner"),a=this._activeImage?0:1;null!==this._activeImage&&this._ui.images[this._activeImage].removeClass("active"),this._activeImage=a;var n=this._active;if(this._ui.imageContainer.addClass("loading"),this._ui.images[a].off("load").prop("src","data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="),this._ui.images[a].on("load",$.proxy(function(){this._imageOnLoad(n,a)},this)),this._renderImage(a,t,s),!this.options.staticViewer){this._ui.header.find("> div > a").prop("href",t.user.link).prop("title",t.user.username).children("img").prop("src",t.user.avatarURL)}var h=WCF.String.escapeHTML(t.image.title);if(t.image.link&&(h='<a href="'+t.image.link+'">'+h+"</a>"),this._ui.header.find("h1").html(h),!this.options.staticViewer){var o=t.series&&t.series.title?WCF.String.escapeHTML(t.series.title):"";t.series.link&&(o='<a href="'+t.series.link+'">'+o+"</a>"),this._ui.header.find("h2").html(o)}return this._ui.header.find("h3").text(WCF.Language.get("wcf.imageViewer.seriesIndex").replace(/{x}/,t.listItem.data("index")+1).replace(/{y}/,this._items)),this._ui.slideshow.full.data("link",t.image.fullURL?t.image.fullURL:t.image.url),this.moveToImage(t.listItem.data("index")),this._toggleButtons(),!0},_imageOnLoad:function(i,e){i==this._active&&(this._ui.imageContainer.removeClass("loading"),this._ui.images[e].addClass("active"),this.options.staticViewer&&this._renderImage(e,null),this.startSlideshow())},_renderImage:function(i,e,t){var s=!0;e||(i=this._activeImage,e=this._images[this._active],t={height:$(window).height()-(this._container.hasClass("maximized")||this._container.hasClass("wcfImageViewerMobile")?0:200),width:this._ui.imageContainer.innerWidth()},s=!1),t.height-=22,t.width-=20;var a=this._ui.images[i];if(a.prop("src")!==e.image.url&&a.prop("src",e.image.url),s&&a[0].complete&&a.trigger("load"),this.options.staticViewer&&!e.image.height&&a[0].complete)if($.browser.mozilla||$.browser.safari){var n=new Image;n.src=e.image.url,e.image.height=n.height||a[0].naturalHeight,e.image.width=n.width||a[0].naturalWidth}else a.css({height:"auto",width:"auto"}),e.image.height=a[0].height,e.image.width=a[0].width;var h=e.image.height,o=e.image.width,l=0;h>t.height&&(l=t.height/h,h=t.height,o=Math.floor(o*l)),o>t.width&&(l=t.width/o,o=t.width,h=Math.floor(h*l));var r=Math.floor((t.width-o)/2);this._ui.images[i].css({height:h+"px",left:r+10+"px",marginTop:-1*Math.round(h/2)+"px",width:o+"px"})},_initUI:function(){if(this._didInit)return!1;this._didInit=!0,this._container=$('<div class="wcfImageViewer'+(this.options.staticViewer?" wcfImageViewerStatic":"")+'" />').appendTo(document.body);var i=$("<div><img /><img /></div>").appendTo(this._container),e=$('<footer><span class="wcfImageViewerButtonPrevious icon fa-angle-double-left" /><div><ul /></div><span class="wcfImageViewerButtonNext icon fa-angle-double-right" /></footer>').appendTo(this._container),t=$("<ul />").appendTo(i),s=$('<li class="wcfImageViewerSlideshowButtonPrevious"><span class="icon icon48 fa-angle-left" /></li>').appendTo(t),a=$('<li class="wcfImageViewerSlideshowButtonToggle pointer"><span class="icon icon48 fa-play" /></li>').appendTo(t),n=$('<li class="wcfImageViewerSlideshowButtonNext"><span class="icon icon48 fa-angle-right" /></li>').appendTo(t),h=$('<li class="wcfImageViewerSlideshowButtonEnlarge pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.enlarge")+'"><span class="icon icon48 fa-expand" /></li>').appendTo(t),o=$('<li class="wcfImageViewerSlideshowButtonFull pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.full")+'"><span class="icon icon48 fa-external-link" /></li>').appendTo(t);return this._ui={buttonNext:e.children("span.wcfImageViewerButtonNext"),buttonPrevious:e.children("span.wcfImageViewerButtonPrevious"),header:$("<header><div"+(this.options.staticViewer?">":' class="box64"><a class="jsTooltip"><img /></a>')+"<div><h1 /><h2 /><h3 /></div></div></header>").appendTo(this._container),imageContainer:i,images:[i.children("img:eq(0)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")}),i.children("img:eq(1)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")})],imageList:e.find("> div > ul"),slideshow:{container:t,enlarge:h,full:o,next:n,previous:s,toggle:a}},this._ui.buttonNext.click($.proxy(this._next,this)),this._ui.buttonPrevious.click($.proxy(this._previous,this)),n.click($.proxy(this._nextImage,this)),s.click($.proxy(this._previousImage,this)),h.click($.proxy(this._toggleView,this)),a.click($.proxy(function(){this._items<2||(this._slideshowEnabled?this.stopSlideshow(!0):(this._disableSlideshow=!1,this.startSlideshow()))},this)),o.click(function(i){window.location=$(i.currentTarget).data("link")}),$('<span class="wcfImageViewerButtonClose icon icon48 fa-times pointer jsTooltip" title="'+WCF.Language.get("wcf.global.button.close")+'" />').appendTo(this._ui.header).click($.proxy(this.close,this)),$.browser.mobile||i.click(function(e){e.target===i[0]&&this.close()}.bind(this)),WCF.DOMNodeInsertedHandler.execute(),enquire.register("(max-width: 767px)",{match:$.proxy(this._enableMobileView,this),unmatch:$.proxy(this._disableMobileView,this)}),!0},_enableMobileView:function(){this._container.addClass("wcfImageViewerMobile");var i=this;this._ui.imageContainer.swipe({swipeLeft:function(e){i._container.hasClass("maximized")&&i._nextImage(e)},swipeRight:function(e){i._container.hasClass("maximized")&&i._previousImage(e)},tap:function(e,t){switch(t.tagName){case"DIV":case"IMG":i._toggleView()}}}),this._isMobile=!0},_disableMobileView:function(){this._container.removeClass("wcfImageViewerMobile"),this._ui.imageContainer.swipe("destroy"),this._isMobile=!1},_toggleView:function(){this._ui.images[this._activeImage].addClass("animateTransformation"),this._container.toggleClass("maximized"),this._ui.slideshow.enlarge.toggleClass("active").children("span").toggleClass("fa-expand").toggleClass("fa-compress"),this._renderImage(null,void 0,null)},_next:function(i,e){if(this._ui.buttonNext.hasClass("pointer")){void 0==e&&this.stopSlideshow(!0);var t=Math.max(this._items*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight,0);this._thumbnailOffset=Math.min(this._thumbnailOffset+this._thumbnailWidth*(e||this.options.shiftBy),t),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)}this._preload(),this._toggleButtons()},_previous:function(i,e){this._ui.buttonPrevious.hasClass("pointer")&&(void 0==e&&this.stopSlideshow(!0),this._thumbnailOffset=Math.max(this._thumbnailOffset-this._thumbnailWidth*(e||this.options.shiftBy),0),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)),this._toggleButtons()},_nextImage:function(i){this._ui.slideshow.next.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active+1),i&&(i.preventDefault(),i.stopPropagation()))},_previousImage:function(i){this._ui.slideshow.previous.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active-1),i&&(i.preventDefault(),i.stopPropagation()))},moveToImage:function(i){var e=(i-3)*this._thumbnailWidth,t=e+5*this._thumbnailWidth,s=this._thumbnailOffset,a=this._thumbnailOffset+this._thumbnailContainerWidth,n=!1;if((e<s||t>a)&&(n=!0),n){var h=0;if(e<s){for(;e<s;)h++,s-=this._thumbnailWidth;this._previous(null,h)}else{for(;t>a;)h++,a+=this._thumbnailWidth;this._next(null,h)}}},_toggleButtons:function(){this._thumbnailOffset>0?this._ui.buttonPrevious.addClass("pointer"):this._ui.buttonPrevious.removeClass("pointer");var i=this._images.length*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight;this._thumbnailOffset>=i?this._ui.buttonNext.removeClass("pointer"):this._ui.buttonNext.addClass("pointer"),this._active>0?this._ui.slideshow.previous.addClass("pointer"):this._ui.slideshow.previous.removeClass("pointer"),this._active+1<this._images.length?this._ui.slideshow.next.addClass("pointer"):this._ui.slideshow.next.removeClass("pointer"),this._items<2?this._ui.slideshow.toggle.removeClass("pointer"):this._ui.slideshow.toggle.addClass("pointer")},_createThumbnails:function(i){this.options.staticViewer&&(this._images=[],this._ui.imageList.empty());for(var e=0,t=i.length;e<t;e++){var s=i[e],a=$('<li class="loading pointer"><img src="'+s.thumbnail.url+'" /></li>').appendTo(this._ui.imageList);a.data("index",this._images.length).data("objectID",s.objectID).click($.proxy(this._showImage,this));var n=a.children("img");if(n.get(0).complete)a.removeClass("loading"),this.options.staticViewer&&this._fixThumbnailDimensions(n);else{var h=this;n.on("load",function(){var i=$(this);i.parent().removeClass("loading"),h.options.staticViewer&&h._fixThumbnailDimensions(i)})}s.listItem=a,this._images.push(s)}},_fixThumbnailDimensions:function(i){var e=new Image;e.src=i.prop("src");var t=e.height,s=e.width;if(t==s)t=s=80;else if(t<s){var a=80/s;s=80,t*=a}else{var a=80/t;t=80,s*=a}i.css({height:t+"px",width:s+"px"})},_loadNextImages:function(i){this._proxy.setOption("data",{actionName:"loadNextImages",className:this.options.className,interfaceName:"wcf\\data\\IImageViewerAction",objectIDs:[this.element.data("objectID")],parameters:{maximumHeight:this._maxDimensions.height,maximumWidth:this._maxDimensions.width,offset:this._images.length,targetImageID:i&&this.element.data("targetImageID")?this.element.data("targetImageID"):0}}),this._proxy.setOption("showLoadingOverlay",!1),this._proxy.sendRequest()},_getStaticImages:function(){var i=[];return $(this.options.imageSelector).each(function(e,t){var s=$(t),a=s.find("> img, .attachmentThumbnailImage > img").first();a.length||(a=s.parentsUntil(".formAttachmentList").last().find(".attachmentTinyThumbnail")),i.push({image:{fullURL:a.data("source")?a.data("source").replace(/\\\//g,"/"):s.prop("href"),link:"",title:s.prop("title"),url:s.prop("href")},series:null,thumbnail:{url:a.prop("src")},user:null})}),this._items=i.length,i},_success:function(i,e,t){i.returnValues.items&&(this._items=i.returnValues.items);var s=this._initUI();this._createThumbnails(i.returnValues.images);var a=i.returnValues.targetImageID?i.returnValues.targetImageID:0;this._render(s,a),this._isOpen||(this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable())}}); })(this);
+(function (window, undefined) { "use strict";WCF.ImageViewer=Class.extend({_triggerElement:null,init:function(){this._triggerElement=$('<span class="wcfImageViewerTriggerElement" />').data("disableSlideshow",!0).hide().appendTo(document.body),this._triggerElement.wcfImageViewer({enableSlideshow:0,imageSelector:".jsImageViewerEnabled",staticViewer:!0}),WCF.DOMNodeInsertedHandler.addCallback("WCF.ImageViewer",$.proxy(this._domNodeInserted,this)),WCF.DOMNodeInsertedHandler.execute()},_domNodeInserted:function(){this._initImageSizeCheck(),this._rebuildImageViewer()},_rebuildImageViewer:function(){var i=$("a.jsImageViewer");i.length&&i.removeClass("jsImageViewer").addClass("jsImageViewerEnabled").click($.proxy(this._click,this))},_click:function(i){i.ctrlKey||(i.preventDefault(),i.stopPropagation(),$(i.currentTarget).closest(".popover").length||this._triggerElement.wcfImageViewer("open",null,$(i.currentTarget).wcfIdentify()))},_initImageSizeCheck:function(){$(".jsResizeImage").each($.proxy(function(i,e){e.complete&&this._checkImageSize({currentTarget:e})},this)),$(".jsResizeImage").on("load",$.proxy(this._checkImageSize,this))},_checkImageSize:function(i){var e=$(i.currentTarget);if(!e.is(":visible"))return void e.off("load");if(e.removeClass("jsResizeImage"),!e.closest(".messageSignature").length){var t=new Image;t.src=e.attr("src");e.closest("div.messageText, div.messageTextPreview").width()<t.width?e.parents("a").length||(e.wrap('<a href="'+e.attr("src")+'" class="jsImageViewerEnabled embeddedImageLink" />'),e.parent().click($.proxy(this._click,this)),"right"==e.css("float")?e.parent().addClass("messageFloatObjectRight"):"left"==e.css("float")&&e.parent().addClass("messageFloatObjectLeft"),e[0].style.removeProperty("float"),e[0].style.removeProperty("margin")):e.removeClass("embeddedAttachmentLink")}}}),$.widget("ui.wcfImageViewer",{_active:-1,_activeImage:null,_container:null,_didInit:!1,_disableSlideshow:!1,_eventNamespace:"",_images:[],_isMobile:!1,_isOpen:!1,_items:-1,_maxDimensions:{height:0,width:0},_proxy:null,_slideshowEnabled:!1,_thumbnailContainerWidth:0,_thumbnailMarginRight:0,_thumbnailOffset:0,_thumbnailWidth:0,_timer:null,_ui:{buttonNext:null,buttonPrevious:null,header:null,image:null,imageContainer:null,imageList:null,slideshow:{container:null,enlarge:null,next:null,previous:null,toggle:null}},options:{shiftBy:5,enableSlideshow:1,speed:5,className:"",imageSelector:"",staticViewer:!1},_create:function(){this._active=-1,this._activeImage=null,this._container=null,this._didInit=!1,this._disableSlideshow=this.element.data("disableSlideshow"),this._eventNamespace=this.element.wcfIdentify(),this._images=[],this._isMobile=!1,this._isOpen=!1,this._items=-1,this._maxDimensions={height:document.documentElement.clientHeight,width:document.documentElement.clientWidth},this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._slideshowEnabled=!1,this._thumbnailContainerWidth=0,this._thumbnailMarginRight=0,this._thumbnailOffset=0,this._thumbnaiLWidth=0,this._timer=null,this._ui={},this.element.click($.proxy(this.open,this)),window.addEventListener("popstate",function(i){if(null!=i.state&&"imageViewer"===i.state.name&&i.state.container===this._eventNamespace)return this.open(i),void this.showImage(i.state.image);this.close(i)}.bind(this))},open:function(i,e){if(i&&i.preventDefault(),this._isOpen)return!1;if(i&&"popstate"===i.type||window.history.pushState({name:"imageViewer"},"",""),this.options.staticViewer){var t=this._getStaticImages();this._initUI(),this._createThumbnails(t,!0),this._render(!0,void 0,e),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable(),$.browser.touch&&setTimeout($.proxy(function(){this._isMobile&&!this._container.hasClass("maximized")&&this._toggleView()},this),500)}else 0===this._images.length?this._loadNextImages(!0):(this._render(!1,this.element.data("targetImageID")),this._items>1&&this._slideshowEnabled&&this.startSlideshow(),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable());return this._bindListener(),require(["Ui/Screen"],function(i){i.pageOverlayOpen()}),!0},close:function(i){return i&&i.preventDefault(),i&&"popstate"===i.type?!!this._isOpen&&(this._container.removeClass("open"),null!==this._timer&&this._timer.stop(),this._unbindListener(),this._isOpen=!1,WCF.System.DisableScrolling.enable(),WCF.System.DisableZoom.enable(),require(["Ui/Screen"],function(i){i.pageOverlayClose()}),!0):void window.history.back()},startSlideshow:function(){return!this._disableSlideshow&&!this._slideshowEnabled&&(null===this._timer?this._timer=new WCF.PeriodicalExecuter($.proxy(function(){var i=this._active+1;i==this._items&&(i=0),this.showImage(i)},this),1e3*this.options.speed):this._timer.resume(),this._slideshowEnabled=!0,this._ui.slideshow.toggle.children("span").removeClass("fa-play").addClass("fa-pause"),!0)},stopSlideshow:function(i){return!!this._slideshowEnabled&&(this._timer.stop(),i&&this._ui.slideshow.toggle.children("span").removeClass("fa-pause").addClass("fa-play"),this._slideshowEnabled=!1,!0)},_bindListener:function(){$(document).on("keydown."+this._eventNamespace,$.proxy(this._keyDown,this)),$(window).on("resize."+this._eventNamespace,$.proxy(this._renderImage,this))},_unbindListener:function(){$(document).off("keydown."+this._eventNamespace),$(window).off("resize."+this._eventNamespace)},_keyDown:function(i){switch(i.which){case $.ui.keyCode.ESCAPE:this.close();break;case $.ui.keyCode.LEFT:this._previousImage();break;case $.ui.keyCode.RIGHT:this._nextImage();break;case $.ui.keyCode.UP:this._container.hasClass("maximized")||this._toggleView();break;case $.ui.keyCode.DOWN:this._container.hasClass("maximized")&&this._toggleView();break;case $.ui.keyCode.ENTER:var e=this._ui.header.find("h1 > a");1==e.length?window.location=e.prop("href"):this._ui.slideshow.full.trigger("click");break;case 80:this._ui.slideshow.toggle.trigger("click");break;default:return!0}return!1},_render:function(i,e,t){this._container.addClass("open");var s=null;if(i&&(s=this._ui.imageList.children("li:eq(0)"),this._thumbnailMarginRight=parseInt(s.css("marginRight").replace(/px$/,""))||0,this._thumbnailWidth=s.outerWidth(!0),this._thumbnailContainerWidth=this._ui.imageList.parent().innerWidth(),this._items>1&&this.options.enableSlideshow&&!e&&!t&&this.startSlideshow()),e)this._ui.imageList.children("li").each($.proxy(function(i,t){var s=$(t);if(s.data("objectID")==e)return s.trigger("click"),this.moveToImage(s.data("index")),!1},this));else if(t){var a=0;$(this.options.imageSelector).each(function(i,e){if($(e).wcfIdentify()==t)return a=i,!1});var n=this._ui.imageList.children("li:eq("+a+")");if(-1!==this._active){var h=!1;this._active!=n.data("index")&&(h=!0),this._ui.images[this._activeImage].prop("src")!=this._images[this._active].image.url&&(h=!0),h&&(this._active=-1)}n.trigger("click"),this.moveToImage(n.data("index"))}else null!==s&&s.trigger("click");this._toggleButtons(),this._preload()},_preload:function(){if(this._images.length<this._items){this._images.length*this._thumbnailWidth-this._thumbnailOffset<this._thumbnailContainerWidth&&this._loadNextImages(!1)}},_showImage:function(i){this.showImage($(i.currentTarget).data("index"),!0)},showImage:function(i,e){if(this._active==i)return!1;this.stopSlideshow(e||!1),-1!=this._active&&this._images[this._active].listItem.removeClass("active"),this._active=i,window.history.replaceState({name:"imageViewer",container:this._eventNamespace,image:this._active},"","");var t=this._images[i];this._ui.imageList.children("li").removeClass("active"),t.listItem.addClass("active");var s=this._ui.imageContainer.getDimensions("inner"),a=this._activeImage?0:1;null!==this._activeImage&&this._ui.images[this._activeImage].removeClass("active"),this._activeImage=a;var n=this._active;if(this._ui.imageContainer.addClass("loading"),this._ui.images[a].off("load").prop("src","data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="),this._ui.images[a].on("load",$.proxy(function(){this._imageOnLoad(n,a)},this)),this._renderImage(a,t,s),!this.options.staticViewer){this._ui.header.find("> div > a").prop("href",t.user.link).prop("title",t.user.username).children("img").prop("src",t.user.avatarURL)}var h=WCF.String.escapeHTML(t.image.title);if(t.image.link&&(h='<a href="'+t.image.link+'">'+h+"</a>"),this._ui.header.find("h1").html(h),!this.options.staticViewer){var o=t.series&&t.series.title?WCF.String.escapeHTML(t.series.title):"";t.series.link&&(o='<a href="'+t.series.link+'">'+o+"</a>"),this._ui.header.find("h2").html(o)}return this._ui.header.find("h3").text(WCF.Language.get("wcf.imageViewer.seriesIndex").replace(/{x}/,t.listItem.data("index")+1).replace(/{y}/,this._items)),this._ui.slideshow.full.data("link",t.image.fullURL?t.image.fullURL:t.image.url),this.moveToImage(t.listItem.data("index")),this._toggleButtons(),!0},_imageOnLoad:function(i,e){i==this._active&&(this._ui.imageContainer.removeClass("loading"),this._ui.images[e].addClass("active"),this.options.staticViewer&&this._renderImage(e,null),this.startSlideshow())},_renderImage:function(i,e,t){var s=!0;e||(i=this._activeImage,e=this._images[this._active],t={height:$(window).height()-(this._container.hasClass("maximized")||this._container.hasClass("wcfImageViewerMobile")?0:200),width:this._ui.imageContainer.innerWidth()},s=!1),t.height-=22,t.width-=20;var a=this._ui.images[i];if(a.prop("src")!==e.image.url&&a.prop("src",e.image.url),s&&a[0].complete&&a.trigger("load"),this.options.staticViewer&&!e.image.height&&a[0].complete)if($.browser.mozilla||$.browser.safari){var n=new Image;n.src=e.image.url,e.image.height=n.height||a[0].naturalHeight,e.image.width=n.width||a[0].naturalWidth}else a.css({height:"auto",width:"auto"}),e.image.height=a[0].height,e.image.width=a[0].width;var h=e.image.height,o=e.image.width,l=0;h>t.height&&(l=t.height/h,h=t.height,o=Math.floor(o*l)),o>t.width&&(l=t.width/o,o=t.width,h=Math.floor(h*l));var r=Math.floor((t.width-o)/2);this._ui.images[i].css({height:h+"px",left:r+10+"px",marginTop:-1*Math.round(h/2)+"px",width:o+"px"})},_initUI:function(){if(this._didInit)return!1;this._didInit=!0,this._container=$('<div class="wcfImageViewer'+(this.options.staticViewer?" wcfImageViewerStatic":"")+'" />').appendTo(document.body);var i=$("<div><img /><img /></div>").appendTo(this._container),e=$('<footer><span class="wcfImageViewerButtonPrevious icon fa-angle-double-left" /><div><ul /></div><span class="wcfImageViewerButtonNext icon fa-angle-double-right" /></footer>').appendTo(this._container),t=$("<ul />").appendTo(i),s=$('<li class="wcfImageViewerSlideshowButtonPrevious"><span class="icon icon48 fa-angle-left" /></li>').appendTo(t),a=$('<li class="wcfImageViewerSlideshowButtonToggle pointer"><span class="icon icon48 fa-play" /></li>').appendTo(t),n=$('<li class="wcfImageViewerSlideshowButtonNext"><span class="icon icon48 fa-angle-right" /></li>').appendTo(t),h=$('<li class="wcfImageViewerSlideshowButtonEnlarge pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.enlarge")+'"><span class="icon icon48 fa-expand" /></li>').appendTo(t),o=$('<li class="wcfImageViewerSlideshowButtonFull pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.full")+'"><span class="icon icon48 fa-external-link" /></li>').appendTo(t);return this._ui={buttonNext:e.children("span.wcfImageViewerButtonNext"),buttonPrevious:e.children("span.wcfImageViewerButtonPrevious"),header:$("<header><div"+(this.options.staticViewer?">":' class="box64"><a class="jsTooltip"><img /></a>')+"<div><h1 /><h2 /><h3 /></div></div></header>").appendTo(this._container),imageContainer:i,images:[i.children("img:eq(0)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")}),i.children("img:eq(1)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")})],imageList:e.find("> div > ul"),slideshow:{container:t,enlarge:h,full:o,next:n,previous:s,toggle:a}},this._ui.buttonNext.click($.proxy(this._next,this)),this._ui.buttonPrevious.click($.proxy(this._previous,this)),n.click($.proxy(this._nextImage,this)),s.click($.proxy(this._previousImage,this)),h.click($.proxy(this._toggleView,this)),a.click($.proxy(function(){this._items<2||(this._slideshowEnabled?this.stopSlideshow(!0):(this._disableSlideshow=!1,this.startSlideshow()))},this)),o.click(function(i){window.location=$(i.currentTarget).data("link")}),$('<span class="wcfImageViewerButtonClose icon icon48 fa-times pointer jsTooltip" title="'+WCF.Language.get("wcf.global.button.close")+'" />').appendTo(this._ui.header).click($.proxy(this.close,this)),$.browser.mobile||i.click(function(e){e.target===i[0]&&this.close()}.bind(this)),WCF.DOMNodeInsertedHandler.execute(),require(["Ui/Screen"],function(i){i.on("screen-sm-down",{match:$.proxy(this._enableMobileView,this),unmatch:$.proxy(this._disableMobileView,this)})}.bind(this)),!0},_enableMobileView:function(){this._container.addClass("wcfImageViewerMobile");var i=this;this._ui.imageContainer.swipe({swipeLeft:function(e){i._container.hasClass("maximized")&&i._nextImage(e)},swipeRight:function(e){i._container.hasClass("maximized")&&i._previousImage(e)},tap:function(e,t){switch(t.tagName){case"DIV":case"IMG":i._toggleView()}}}),this._isMobile=!0},_disableMobileView:function(){this._container.removeClass("wcfImageViewerMobile"),this._ui.imageContainer.swipe("destroy"),this._isMobile=!1},_toggleView:function(){this._ui.images[this._activeImage].addClass("animateTransformation"),this._container.toggleClass("maximized"),this._ui.slideshow.enlarge.toggleClass("active").children("span").toggleClass("fa-expand").toggleClass("fa-compress"),this._renderImage(null,void 0,null)},_next:function(i,e){if(this._ui.buttonNext.hasClass("pointer")){void 0==e&&this.stopSlideshow(!0);var t=Math.max(this._items*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight,0);this._thumbnailOffset=Math.min(this._thumbnailOffset+this._thumbnailWidth*(e||this.options.shiftBy),t),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)}this._preload(),this._toggleButtons()},_previous:function(i,e){this._ui.buttonPrevious.hasClass("pointer")&&(void 0==e&&this.stopSlideshow(!0),this._thumbnailOffset=Math.max(this._thumbnailOffset-this._thumbnailWidth*(e||this.options.shiftBy),0),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)),this._toggleButtons()},_nextImage:function(i){this._ui.slideshow.next.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active+1),i&&(i.preventDefault(),i.stopPropagation()))},_previousImage:function(i){this._ui.slideshow.previous.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active-1),i&&(i.preventDefault(),i.stopPropagation()))},moveToImage:function(i){var e=(i-3)*this._thumbnailWidth,t=e+5*this._thumbnailWidth,s=this._thumbnailOffset,a=this._thumbnailOffset+this._thumbnailContainerWidth,n=!1;if((e<s||t>a)&&(n=!0),n){var h=0;if(e<s){for(;e<s;)h++,s-=this._thumbnailWidth;this._previous(null,h)}else{for(;t>a;)h++,a+=this._thumbnailWidth;this._next(null,h)}}},_toggleButtons:function(){this._thumbnailOffset>0?this._ui.buttonPrevious.addClass("pointer"):this._ui.buttonPrevious.removeClass("pointer");var i=this._images.length*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight;this._thumbnailOffset>=i?this._ui.buttonNext.removeClass("pointer"):this._ui.buttonNext.addClass("pointer"),this._active>0?this._ui.slideshow.previous.addClass("pointer"):this._ui.slideshow.previous.removeClass("pointer"),this._active+1<this._images.length?this._ui.slideshow.next.addClass("pointer"):this._ui.slideshow.next.removeClass("pointer"),this._items<2?this._ui.slideshow.toggle.removeClass("pointer"):this._ui.slideshow.toggle.addClass("pointer")},_createThumbnails:function(i){this.options.staticViewer&&(this._images=[],this._ui.imageList.empty());for(var e=0,t=i.length;e<t;e++){var s=i[e],a=$('<li class="loading pointer"><img src="'+s.thumbnail.url+'" /></li>').appendTo(this._ui.imageList);a.data("index",this._images.length).data("objectID",s.objectID).click($.proxy(this._showImage,this));var n=a.children("img");if(n.get(0).complete)a.removeClass("loading"),this.options.staticViewer&&this._fixThumbnailDimensions(n);else{var h=this;n.on("load",function(){var i=$(this);i.parent().removeClass("loading"),h.options.staticViewer&&h._fixThumbnailDimensions(i)})}s.listItem=a,this._images.push(s)}},_fixThumbnailDimensions:function(i){var e=new Image;e.src=i.prop("src");var t=e.height,s=e.width;if(t==s)t=s=80;else if(t<s){var a=80/s;s=80,t*=a}else{var a=80/t;t=80,s*=a}i.css({height:t+"px",width:s+"px"})},_loadNextImages:function(i){this._proxy.setOption("data",{actionName:"loadNextImages",className:this.options.className,interfaceName:"wcf\\data\\IImageViewerAction",objectIDs:[this.element.data("objectID")],parameters:{maximumHeight:this._maxDimensions.height,maximumWidth:this._maxDimensions.width,offset:this._images.length,targetImageID:i&&this.element.data("targetImageID")?this.element.data("targetImageID"):0}}),this._proxy.setOption("showLoadingOverlay",!1),this._proxy.sendRequest()},_getStaticImages:function(){var i=[];return $(this.options.imageSelector).each(function(e,t){var s=$(t),a=s.find("> img, .attachmentThumbnailImage > img").first();a.length||(a=s.parentsUntil(".formAttachmentList").last().find(".attachmentTinyThumbnail")),i.push({image:{fullURL:a.data("source")?a.data("source").replace(/\\\//g,"/"):s.prop("href"),link:"",title:s.prop("title"),url:s.prop("href")},series:null,thumbnail:{url:a.prop("src")},user:null})}),this._items=i.length,i},_success:function(i,e,t){i.returnValues.items&&(this._items=i.returnValues.items);var s=this._initUI();this._createThumbnails(i.returnValues.images);var a=i.returnValues.targetImageID?i.returnValues.targetImageID:0;this._render(s,a),this._isOpen||(this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable())}}); })(this);
// WCF.Label.js
-(function (window, undefined) { "use strict";WCF.Label={},WCF.Label.ACPList=Class.extend({_labelInput:null,_labelList:[],init:function(){this._labelInput=$("#label").keydown($.proxy(this._keyPressed,this)).keyup($.proxy(this._keyPressed,this)).blur($.proxy(this._keyPressed,this)),$.browser.mozilla&&$.browser.touch&&this._labelInput.on("input",$.proxy(this._keyPressed,this)),$("#labelList").find('input[type="radio"]').each($.proxy(function(e,t){var i=$(t);"custom"!==i.prop("value")&&this._labelList.push($(i.next("span")))},this)),this._labelInput[0].value.length>0&&this._keyPressed()},_keyPressed:function(){var e=this._labelInput.prop("value");""===e&&(e=WCF.Language.get("wcf.acp.label.defaultValue"));for(var t=0,i=this._labelList.length;t<i;t++)this._labelList[t].text(e)}}),WCF.Label.ACPList.Connect=Class.extend({init:function(){var e=$("#connect .structuredList li");e.length&&e.each($.proxy(function(e,t){$(t).find('input[type="checkbox"]').click($.proxy(this._click,this))},this))},_click:function(e){var t=$(e.currentTarget);if(t.is(":checked")){t=t.parents("li");for(var i=t.data("depth");;){if(t=t.next(),!t.length)return!0;if(t.data("depth")<=i)return!0;t.find('input[type="checkbox"]').prop("checked","checked")}}}}),WCF.Label.Chooser=Class.extend({_container:null,_groups:{},_showWithoutSelection:!1,init:function(e,t,i,a){if(this._container=null,this._groups={},this._showWithoutSelection=!0===a,this._initContainers(t),$.getLength(e))for(var n in e){var s=this._groups[n];s&&WCF.Dropdown.getDropdownMenu(s.wcfIdentify()).find("> ul > li:not(.dropdownDivider)").each($.proxy(function(t,i){var a=$(i),s=a.data("labelID")||0;s&&e[n]==s&&this._selectLabel(a,!0)},this))}for(var o in this._containers){var l=this._containers[o];void 0===l.data("labelID")&&l.data("labelID",0)}this._container=$(t),i?$(i).click($.proxy(this._submit,this)):this._container.is("form")&&this._container.submit($.proxy(this._submit,this))},_initContainers:function(e){function t(e){e.addEventListener("wheel",function(e){e.preventDefault()},{passive:!1})}$(e).find(".labelChooser").each($.proxy(function(e,i){var a=$(i),n=a.data("groupID");if(!this._groups[n]){var s=a.wcfIdentify(),o=WCF.Dropdown.getDropdownMenu(s);null===o&&(WCF.Dropdown.initDropdown(a.find(".dropdownToggle")),o=WCF.Dropdown.getDropdownMenu(s));var l=o;if("div"==o.getTagName()&&o.children(".scrollableDropdownMenu").length&&(l=$("<ul />").appendTo(o),o=o.children(".scrollableDropdownMenu")),this._groups[n]=a,o.children("li").data("groupID",n).click($.proxy(this._click,this)),a.data("forceSelection")&&!this._showWithoutSelection||$('<li class="dropdownDivider" />').appendTo(l),this._showWithoutSelection){t($('<li data-label-id="-1"><span><span class="badge label">'+WCF.Language.get("wcf.label.withoutSelection")+"</span></span></li>").data("groupID",n).appendTo(l).click($.proxy(this._click,this))[0])}if(!a.data("forceSelection")){var r=$('<li data-label-id="0"><span><span class="badge label">'+WCF.Language.get("wcf.label.none")+"</span></span></li>").data("groupID",n).appendTo(l);r.click($.proxy(this._click,this)),t(r[0])}}},this))},_click:function(e){this._selectLabel($(e.currentTarget),!1)},_selectLabel:function(e,t){var i=this._groups[e.data("groupID")];t&&void 0!==i.data("labelID")||(e.data("labelID")?i.data("labelID",e.data("labelID")):i.data("labelID",0),e=e.find("span > span"),i.find(".dropdownToggle > span").removeClass().addClass(e.attr("class")).text(e.text()))},_submit:function(){var e=this._container.find(".formSubmit");e.find('input[type="hidden"]').each(function(e,t){var i=$(t);0===i.attr("name").indexOf("labelIDs[")&&i.remove()});for(var t in this._groups){var i=this._groups[t];i.data("labelID")&&$('<input type="hidden" name="labelIDs['+t+']" value="'+i.data("labelID")+'" />').appendTo(e)}},destroy:function(){for(var e in this._groups)WCF.Dropdown.destroy(this._groups[e].wcfIdentify())}}),WCF.Label.ArticleLabelChooser=WCF.Label.Chooser.extend({_labelGroupsToCategories:null,init:function(e,t,i,a,n){this._super(t,i,a,n),this._labelGroupsToCategories=e,this._updateLabelGroups(),$("#categoryID").change($.proxy(this._updateLabelGroups,this))},_updateLabelGroups:function(){$(".labelChooser").each(function(e,t){$(t).parents("dl:eq(0)").hide()});var e=parseInt($("#categoryID").val());if(this._labelGroupsToCategories[e])for(var t=0,i=this._labelGroupsToCategories[e].length;t<i;t++)$("#labelGroup"+this._labelGroupsToCategories[e][t]).parents("dl:eq(0)").show()},_submit:function(){for(var e in this._groups)this._groups[e].is(":visible")||delete this._groups[e];this._super()}}); })(this);
+(function (window, undefined) { "use strict";WCF.Label={},WCF.Label.ACPList=Class.extend({_labelInput:null,_labelList:[],init:function(){this._labelInput=$("#label").keydown($.proxy(this._keyPressed,this)).keyup($.proxy(this._keyPressed,this)).blur($.proxy(this._keyPressed,this)),$.browser.mozilla&&$.browser.touch&&this._labelInput.on("input",$.proxy(this._keyPressed,this)),$("#labelList").find('input[type="radio"]').each($.proxy(function(e,t){var i=$(t);"custom"!==i.prop("value")&&this._labelList.push($(i.next("span")))},this)),this._labelInput[0].value.length>0&&this._keyPressed()},_keyPressed:function(){var e=this._labelInput.prop("value");""===e&&(e=WCF.Language.get("wcf.acp.label.defaultValue"));for(var t=0,i=this._labelList.length;t<i;t++)this._labelList[t].text(e)}}),WCF.Label.ACPList.Connect=Class.extend({init:function(){var e=$("#connect .structuredList li");e.length&&e.each($.proxy(function(e,t){$(t).find('input[type="checkbox"]').click($.proxy(this._click,this))},this))},_click:function(e){var t=$(e.currentTarget);if(t.is(":checked")){t=t.parents("li");for(var i=t.data("depth");;){if(t=t.next(),!t.length)return!0;if(t.data("depth")<=i)return!0;t.find('input[type="checkbox"]').prop("checked","checked")}}}}),WCF.Label.Chooser=Class.extend({_container:null,_groups:{},_showWithoutSelection:!1,init:function(e,t,i,a){if(this._container=null,this._groups={},this._showWithoutSelection=!0===a,this._initContainers(t),$.getLength(e))for(var n in e){var s=this._groups[n];s&&WCF.Dropdown.getDropdownMenu(s.wcfIdentify()).find("> ul > li:not(.dropdownDivider)").each($.proxy(function(t,i){var a=$(i),s=a.data("labelID")||0;s&&e[n]==s&&this._selectLabel(a,!0)},this))}for(var o in this._containers){var l=this._containers[o];void 0===l.data("labelID")&&l.data("labelID",0)}this._container=$(t),i?$(i).click($.proxy(this._submit,this)):this._container.is("form")&&this._container.submit($.proxy(this._submit,this))},_initContainers:function(e){function t(e){e.addEventListener("wheel",function(e){e.preventDefault()},{passive:!1})}$(e).find(".labelChooser").each($.proxy(function(e,i){var a=$(i),n=a.data("groupID");if(!this._groups[n]){var s=a.wcfIdentify(),o=WCF.Dropdown.getDropdownMenu(s);null===o&&(WCF.Dropdown.initDropdown(a.find(".dropdownToggle")),o=WCF.Dropdown.getDropdownMenu(s));var l=o;if("div"==o.getTagName()&&o.children(".scrollableDropdownMenu").length&&(l=$("<ul />").appendTo(o),o=o.children(".scrollableDropdownMenu")),this._groups[n]=a,o.children("li").data("groupID",n).click($.proxy(this._click,this)),a.data("forceSelection")&&!this._showWithoutSelection||$('<li class="dropdownDivider" />').appendTo(l),this._showWithoutSelection){t($('<li data-label-id="-1"><span><span class="badge label">'+WCF.Language.get("wcf.label.withoutSelection")+"</span></span></li>").data("groupID",n).appendTo(l).click($.proxy(this._click,this))[0])}if(!a.data("forceSelection")){var r=$('<li data-label-id="0"><span><span class="badge label">'+WCF.Language.get("wcf.label.none")+"</span></span></li>").data("groupID",n).appendTo(l);r.click($.proxy(this._click,this)),t(r[0])}}},this))},_click:function(e){this._selectLabel($(e.currentTarget),!1)},_selectLabel:function(e,t){var i=this._groups[e.data("groupID")];t&&void 0!==i.data("labelID")||(e.data("labelID")?i.data("labelID",e.data("labelID")):i.data("labelID",0),e=e.find("span > span"),i.find(".dropdownToggle > span").removeClass().addClass(e.attr("class")).text(e.text()),!t&&this._container[0]&&"FORM"===this._container[0].nodeName&&null===elBySel('input:not([type="hidden"]):not([type="submit"]):not([type="reset"]), select, textarea',this._container[0])&&setTimeout(function(){this._container.trigger("submit")}.bind(this),100))},_submit:function(){var e=this._container.find(".formSubmit");e.find('input[type="hidden"]').each(function(e,t){var i=$(t);0===i.attr("name").indexOf("labelIDs[")&&i.remove()});for(var t in this._groups){var i=this._groups[t];i.data("labelID")&&$('<input type="hidden" name="labelIDs['+t+']" value="'+i.data("labelID")+'" />').appendTo(e)}},destroy:function(){for(var e in this._groups)WCF.Dropdown.destroy(this._groups[e].wcfIdentify())}}),WCF.Label.ArticleLabelChooser=WCF.Label.Chooser.extend({_labelGroupsToCategories:null,init:function(e,t,i,a,n){this._super(t,i,a,n),this._labelGroupsToCategories=e,this._updateLabelGroups(),$("#categoryID").change($.proxy(this._updateLabelGroups,this))},_updateLabelGroups:function(){$(".labelChooser").each(function(e,t){$(t).parents("dl:eq(0)").hide()});var e=parseInt($("#categoryID").val());if(this._labelGroupsToCategories[e])for(var t=0,i=this._labelGroupsToCategories[e].length;t<i;t++)$("#labelGroup"+this._labelGroupsToCategories[e][t]).parents("dl:eq(0)").show()},_submit:function(){for(var e in this._groups)this._groups[e].is(":visible")||delete this._groups[e];this._super()}}); })(this);
// WCF.Location.js
-(function (window, undefined) { "use strict";function gm_authFailure(){WCF.System.Event.fireEvent("com.woltlab.wcf.googleMaps","authenticationFailure")}WCF.Location={},WCF.Location.Util={getLocation:function(t,e){var o=WCF.Location.GoogleMaps.Settings.get("accessUserLocation");navigator.geolocation&&null!==o&&o?navigator.geolocation.getCurrentPosition(function(e){t(e.coords.latitude,e.coords.longitude)},function(){t(void 0,void 0)},{timeout:e||5e3}):t(void 0,void 0)}},WCF.Location.GoogleMaps={},WCF.Location.GoogleMaps.Settings={_settings:{},get:function(t){return void 0===t?this._settings:void 0!==this._settings[t]?this._settings[t]:null},set:function(t,e){if($.isPlainObject(t))for(var o in t)this._settings[o]=t[o];else this._settings[t]=e}},WCF.Location.GoogleMaps.Map=Class.extend({_map:null,_markers:[],init:function(t,e){this._mapContainer=$("#"+t),this._mapOptions=$.extend(!0,this._getDefaultMapOptions(),e),this._map=new google.maps.Map(this._mapContainer[0],this._mapOptions),this._markers=[],this._mapContainer.parents(".sidebar").length&&enquire.register("(max-width: 767px)",{setup:$.proxy(this._addSidebarMapListener,this),deferSetup:!0}),this.refresh()},_addInfoWindowEventListener:function(t,e){google.maps.event.addListener(t,"click",$.proxy(function(){e.open(this._map,t)},this))},_addSidebarMapListener:function(){$(".content > .mobileSidebarToggleButton").click($.proxy(this.refresh,this))},_getDefaultMapOptions:function(){var t={};switch(t.center=new google.maps.LatLng(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),t.disableDoubleClickZoom=WCF.Location.GoogleMaps.Settings.get("disableDoubleClickZoom"),t.draggable=WCF.Location.GoogleMaps.Settings.get("draggable"),WCF.Location.GoogleMaps.Settings.get("mapType")){case"map":t.mapTypeId=google.maps.MapTypeId.ROADMAP;break;case"satellite":t.mapTypeId=google.maps.MapTypeId.SATELLITE;break;case"physical":t.mapTypeId=google.maps.MapTypeId.TERRAIN;break;case"hybrid":default:t.mapTypeId=google.maps.MapTypeId.HYBRID}if(t.mapTypeControl="off"!=WCF.Location.GoogleMaps.Settings.get("mapTypeControl"),t.mapTypeControl)switch(WCF.Location.GoogleMaps.Settings.get("mapTypeControl")){case"dropdown":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DROPDOWN_MENU};break;case"horizontalBar":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.HORIZONTAL_BAR};break;default:t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DEFAULT}}return t.scaleControl=WCF.Location.GoogleMaps.Settings.get("scaleControl"),t.scrollwheel=WCF.Location.GoogleMaps.Settings.get("scrollwheel"),t.zoom=WCF.Location.GoogleMaps.Settings.get("zoom"),t},addDraggableMarker:function(t,e){var o=new google.maps.Marker({clickable:!1,draggable:!0,map:this._map,position:new google.maps.LatLng(t,e),zIndex:1});return this._markers.push(o),o},addMarker:function(t,e,o,s,i){var a=new google.maps.Marker({map:this._map,position:new google.maps.LatLng(t,e),title:o});if(s&&a.setIcon(s),i){var n=new google.maps.InfoWindow({content:i});this._addInfoWindowEventListener(a,n),a.infoWindow=n}return this._markers.push(a),a},getMarkers:function(){return this._markers},getMap:function(){return this._map},refresh:function(){var t=this._map.getCenter();google.maps.event.trigger(this._map,"resize"),this._map.setCenter(t)},refreshBounds:function(){var t=null,e=null,o=null,s=null;for(var i in this._markers){var a=this._markers[i],n=a.getPosition().lat(),r=a.getPosition().lng();null===t?(t=e=n,o=s=r):(t>n?t=n:e<n&&(e=n),o>n?o=n:s<r&&(s=r))}this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(t,o),new google.maps.LatLng(e,s)))},removeMarkers:function(){for(var t in this._markers)this._markers[t].setMap(null);this._markers=[]},setBounds:function(t,e){this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(e.latitude,e.longitude),new google.maps.LatLng(t.latitude,t.longitude)))},setCenter:function(t,e){this._map.setCenter(new google.maps.LatLng(t,e))}}),WCF.Location.GoogleMaps.LargeMap=WCF.Location.GoogleMaps.Map.extend({_actionClassName:null,_additionalParameters:{},_locationSearch:null,_locationSearchInputSelector:null,_markerClusterer:null,_objectIDs:[],_previousNorthEast:null,_previousSouthWest:null,_stringifyExcludedObjectIds:!1,init:function(t,e,o,s,i){this._stringifyExcludedObjectIds=!1,e&&e.stringifyExcludedObjectIds&&(this._stringifyExcludedObjectIds=e.stringifyExcludedObjectIds,delete e.stringifyExcludedObjectIds),this._super(t,e),this._actionClassName=o,this._locationSearchInputSelector=s||"",this._additionalParameters=i||{},this._objectIDs=[],this._locationSearchInputSelector&&(this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(s,$.proxy(this._centerMap,this))),this._markerClusterer=new MarkerClusterer(this._map,this._markers,{maxZoom:17,imagePath:WCF.Location.GoogleMaps.Settings.get("markerClustererImagePath")+"m"}),this._markerSpiderfier=new OverlappingMarkerSpiderfier(this._map,{keepSpiderfied:!0,markersWontHide:!0,markersWontMove:!0}),this._markerSpiderfier.addListener("click",$.proxy(function(t){t.infoWindow&&t.infoWindow.open(this._map,t)},this)),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._previousNorthEast=null,this._previousSouthWest=null,google.maps.event.addListener(this._map,"idle",$.proxy(this._loadMarkers,this))},_addInfoWindowEventListener:function(t,e){},_centerMap:function(t){this.setCenter(t.location.lat(),t.location.lng()),$(this._locationSearchInputSelector).val(t.label)},_loadMarkers:function(){var t=this._map.getBounds().getNorthEast(),e=this._map.getBounds().getSouthWest();return!(this._previousNorthEast&&this._previousNorthEast.lat()>=t.lat()&&this._previousNorthEast.lng()>=t.lng()&&this._previousSouthWest.lat()<=e.lat()&&this._previousSouthWest.lng()<=e.lng())&&(this._previousNorthEast=t,this._previousSouthWest=e,this._proxy.setOption("data",{actionName:"getMapMarkers",className:this._actionClassName,parameters:$.extend(this._additionalParameters,{excludedObjectIDs:this._stringifyExcludedObjectIds?JSON.stringify(this._objectIDs):this._objectIDs,eastLongitude:t.lng(),northLatitude:t.lat(),southLatitude:e.lat(),westLongitude:e.lng()})}),this._proxy.sendRequest(),!0)},_success:function(t,e,o){if(t.returnValues&&t.returnValues.markers)for(var s in t.returnValues.markers){var i=t.returnValues.markers[s];this.addMarker(i.latitude,i.longitude,i.title,null,i.infoWindow),i.objectID?this._objectIDs.push(i.objectID):i.objectIDs&&(this._objectIDs=this._objectIDs.concat(i.objectIDs))}},addMarker:function(t,e,o,s,i){var a=this._super(t,e,o,s,i);return this._markerClusterer.addMarker(a),this._markerSpiderfier.addMarker(a),a}}),WCF.Location.GoogleMaps.SuggestionMap=WCF.Location.GoogleMaps.LargeMap.extend({_locationSuggestionsButton:null,_suggestionSelectionCallback:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i);var a=$('<div class="gmnoprint googleMapsCustomControlContainer"><div class="gm-style-mtc"><div class="googleMapsCustomControl">'+WCF.Language.get("wcf.map.showLocationSuggestions")+"</div></div></div>");this._locationSuggestionsButton=a.find(".googleMapsCustomControl").click($.proxy(this._toggleLocationSuggestions,this)),this._map.controls[google.maps.ControlPosition.TOP_RIGHT].push(a.get(0))},_loadMarkers:function(){this._locationSuggestionsButton.hasClass("active")&&(this._super()||(this._loadSuggestions=!1))},_success:function(t,e,o){var s=this._markers.length;this._super(t,e,o),this._loadSuggestions&&s==this._markers.length&&(this._loadSuggestions=!1,new WCF.System.Notification(WCF.Language.get("wcf.map.noLocationSuggestions"),"info").show())},_toggleLocationSuggestions:function(){var t=!this._locationSuggestionsButton.hasClass("active");t&&(this._loadSuggestions=!0),this.showSuggestions(t)},addMarker:function(t,e,o,s,i){var a=$(i),n=$('<a class="googleMapsUseLocationSuggestionLink" />').text(WCF.Language.get("wcf.map.useLocationSuggestion")).click(this._suggestionSelectionCallback);a.append($("<p />").append(n));var r=this._super(t,e,o,"//mt.google.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png",a.get(0));return n.data("marker",r),r},setSuggestionSelectionCallback:function(t){this._suggestionSelectionCallback=t},showSuggestions:function(t){void 0===t&&(t=!0),this._locationSuggestionsButton.toggleClass("active",t);for(var e=[],o=0,s=this._markers.length;o<s;o++){var i=this._markers[o];i.draggable||(i.setVisible(t),t&&e.push(i))}this._markerClusterer.clearMarkers(),t&&this._markerClusterer.addMarkers(e),this._loadMarkers()}}),WCF.Location.GoogleMaps.LocationSearch=WCF.Search.Base.extend({_geocoder:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i),this.setDelay(500),this._geocoder=new google.maps.Geocoder},_createListItem:function(t){var e=$("<li><span>"+WCF.String.escapeHTML(t.formatted_address)+"</span></li>").appendTo(this._list);return e.data("location",t.geometry.location).data("label",t.formatted_address).click($.proxy(this._executeCallback,this)),this._itemCount++,e},_keyUp:function(t){switch(t.which){case $.ui.keyCode.LEFT:case $.ui.keyCode.RIGHT:return;case $.ui.keyCode.UP:return void this._selectPreviousItem();case $.ui.keyCode.DOWN:return void this._selectNextItem();case $.ui.keyCode.ENTER:return this._selectElement(t)}var e=this._getSearchString(t);""===e?this._clearList(!0):e.length>=this._triggerLength?this._delay?(null!==this._timer&&this._timer.stop(),this._timer=new WCF.PeriodicalExecuter($.proxy(function(){this._geocoder.geocode({address:e},$.proxy(this._success,this)),this._timer.stop(),this._timer=null},this),this._delay)):this._geocoder.geocode({address:e},$.proxy(this._success,this)):this._clearList(!1)},_success:function(t,e){if(this._clearList(!1),e==google.maps.GeocoderStatus.OK){if($.getLength(t)){var o=0;for(var s in t)if(this._createListItem(t[s]),10==++o)break}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var i=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(i).hasClass("dropdownOpen")||WCF.Dropdown.toggleDropdown(i),this._itemIndex=-1,WCF.Dropdown.getDropdown(i).data("disableAutoFocus")||this._selectNextItem()}}}),WCF.Location.GoogleMaps.LocationInput=Class.extend({_locationSearch:null,_map:null,_marker:null,init:function(t,e,o,s,i,a){this._searchInput=o,a?(this._map=new WCF.Location.GoogleMaps.SuggestionMap(t,e,a),this._map.setSuggestionSelectionCallback($.proxy(this._useSuggestion,this))):this._map=new WCF.Location.GoogleMaps.Map(t,e),this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(o,$.proxy(this._setMarkerByLocation,this)),s&&i?this._marker=this._map.addDraggableMarker(s,i):(this._marker=this._map.addDraggableMarker(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),WCF.Location.Util.getLocation($.proxy(function(t,e){void 0!==t&&void 0!==e&&(WCF.Location.GoogleMaps.Util.moveMarker(this._marker,t,e),WCF.Location.GoogleMaps.Util.focusMarker(this._marker))},this))),this._marker.addListener("dragend",$.proxy(this._updateLocation,this))},_useSuggestion:function(t){var e=$(t.currentTarget).data("marker");this._marker.setPosition(e.getPosition()),this._updateLocation(),this._map.showSuggestions(!1)},_updateLocation:function(){WCF.Location.GoogleMaps.Util.reverseGeocoding($.proxy(function(t){null!==t&&$(this._searchInput).val(t)},this),this._marker)},_setMarkerByLocation:function(t){this._marker.setPosition(t.location),WCF.Location.GoogleMaps.Util.focusMarker(this._marker),$(this._searchInput).val(t.label)},getMap:function(){return this._map},getMarker:function(){return this._marker}}),WCF.Location.GoogleMaps.Util={_geocoder:null,focusMarker:function(t){t.getMap().setCenter(t.getPosition())},getMarkerPosition:function(t){return{latitude:t.getPosition().lat(),longitude:t.getPosition().lng()}},moveMarker:function(t,e,o,s){t.setPosition(new google.maps.LatLng(e,o)),s&&google.maps.event.trigger(t,"dragend")},reverseGeocoding:function(t,e,o,s,i){e&&(o=e.getPosition().lat(),s=e.getPosition().lng()),null===this._geocoder&&(this._geocoder=new google.maps.Geocoder);var a=new google.maps.LatLng(o,s);this._geocoder.geocode({latLng:a},function(e,o){t(o==google.maps.GeocoderStatus.OK?i?e:e[0].formatted_address:null)})}}; })(this);
+(function (window, undefined) { "use strict";function gm_authFailure(){WCF.System.Event.fireEvent("com.woltlab.wcf.googleMaps","authenticationFailure")}WCF.Location={},WCF.Location.Util={getLocation:function(t,e){var o=WCF.Location.GoogleMaps.Settings.get("accessUserLocation");navigator.geolocation&&null!==o&&o?navigator.geolocation.getCurrentPosition(function(e){t(e.coords.latitude,e.coords.longitude)},function(){t(void 0,void 0)},{timeout:e||5e3}):t(void 0,void 0)}},WCF.Location.GoogleMaps={},WCF.Location.GoogleMaps.Settings={_settings:{},get:function(t){return void 0===t?this._settings:void 0!==this._settings[t]?this._settings[t]:null},set:function(t,e){if($.isPlainObject(t))for(var o in t)this._settings[o]=t[o];else this._settings[t]=e}},WCF.Location.GoogleMaps.Map=Class.extend({_map:null,_markers:[],init:function(t,e){this._mapContainer=$("#"+t),this._mapOptions=$.extend(!0,this._getDefaultMapOptions(),e),this._map=new google.maps.Map(this._mapContainer[0],this._mapOptions),this._markers=[],this._mapContainer.parents(".sidebar").length&&require(["Ui/Screen"],function(t){t.on("screen-sm-down",{setup:$.proxy(this._addSidebarMapListener,this)})}.bind(this)),this.refresh()},_addInfoWindowEventListener:function(t,e){google.maps.event.addListener(t,"click",$.proxy(function(){e.open(this._map,t)},this))},_addSidebarMapListener:function(){$(".content > .mobileSidebarToggleButton").click($.proxy(this.refresh,this))},_getDefaultMapOptions:function(){var t={};switch(t.center=new google.maps.LatLng(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),t.disableDoubleClickZoom=WCF.Location.GoogleMaps.Settings.get("disableDoubleClickZoom"),t.draggable=WCF.Location.GoogleMaps.Settings.get("draggable"),WCF.Location.GoogleMaps.Settings.get("mapType")){case"map":t.mapTypeId=google.maps.MapTypeId.ROADMAP;break;case"satellite":t.mapTypeId=google.maps.MapTypeId.SATELLITE;break;case"physical":t.mapTypeId=google.maps.MapTypeId.TERRAIN;break;case"hybrid":default:t.mapTypeId=google.maps.MapTypeId.HYBRID}if(t.mapTypeControl="off"!=WCF.Location.GoogleMaps.Settings.get("mapTypeControl"),t.mapTypeControl)switch(WCF.Location.GoogleMaps.Settings.get("mapTypeControl")){case"dropdown":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DROPDOWN_MENU};break;case"horizontalBar":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.HORIZONTAL_BAR};break;default:t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DEFAULT}}return t.scaleControl=WCF.Location.GoogleMaps.Settings.get("scaleControl"),t.scrollwheel=WCF.Location.GoogleMaps.Settings.get("scrollwheel"),t.zoom=WCF.Location.GoogleMaps.Settings.get("zoom"),t},addDraggableMarker:function(t,e){var o=new google.maps.Marker({clickable:!1,draggable:!0,map:this._map,position:new google.maps.LatLng(t,e),zIndex:1});return this._markers.push(o),o},addMarker:function(t,e,o,s,i){var a=new google.maps.Marker({map:this._map,position:new google.maps.LatLng(t,e),title:o});if(s&&a.setIcon(s),i){var n=new google.maps.InfoWindow({content:i});this._addInfoWindowEventListener(a,n),a.infoWindow=n}return this._markers.push(a),a},getMarkers:function(){return this._markers},getMap:function(){return this._map},refresh:function(){var t=this._map.getCenter();google.maps.event.trigger(this._map,"resize"),this._map.setCenter(t)},refreshBounds:function(){var t=null,e=null,o=null,s=null;for(var i in this._markers){var a=this._markers[i],n=a.getPosition().lat(),r=a.getPosition().lng();null===t?(t=e=n,o=s=r):(t>n?t=n:e<n&&(e=n),o>n?o=n:s<r&&(s=r))}this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(t,o),new google.maps.LatLng(e,s)))},removeMarkers:function(){for(var t in this._markers)this._markers[t].setMap(null);this._markers=[]},setBounds:function(t,e){this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(e.latitude,e.longitude),new google.maps.LatLng(t.latitude,t.longitude)))},setCenter:function(t,e){this._map.setCenter(new google.maps.LatLng(t,e))}}),WCF.Location.GoogleMaps.LargeMap=WCF.Location.GoogleMaps.Map.extend({_actionClassName:null,_additionalParameters:{},_locationSearch:null,_locationSearchInputSelector:null,_markerClusterer:null,_objectIDs:[],_previousNorthEast:null,_previousSouthWest:null,_stringifyExcludedObjectIds:!1,init:function(t,e,o,s,i){this._stringifyExcludedObjectIds=!1,e&&e.stringifyExcludedObjectIds&&(this._stringifyExcludedObjectIds=e.stringifyExcludedObjectIds,delete e.stringifyExcludedObjectIds),this._super(t,e),this._actionClassName=o,this._locationSearchInputSelector=s||"",this._additionalParameters=i||{},this._objectIDs=[],this._locationSearchInputSelector&&(this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(s,$.proxy(this._centerMap,this))),this._markerClusterer=new MarkerClusterer(this._map,this._markers,{maxZoom:17,imagePath:WCF.Location.GoogleMaps.Settings.get("markerClustererImagePath")+"m"}),this._markerSpiderfier=new OverlappingMarkerSpiderfier(this._map,{keepSpiderfied:!0,markersWontHide:!0,markersWontMove:!0}),this._markerSpiderfier.addListener("click",$.proxy(function(t){t.infoWindow&&t.infoWindow.open(this._map,t)},this)),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._previousNorthEast=null,this._previousSouthWest=null,google.maps.event.addListener(this._map,"idle",$.proxy(this._loadMarkers,this))},_addInfoWindowEventListener:function(t,e){},_centerMap:function(t){this.setCenter(t.location.lat(),t.location.lng()),$(this._locationSearchInputSelector).val(t.label)},_loadMarkers:function(){var t=this._map.getBounds().getNorthEast(),e=this._map.getBounds().getSouthWest();return!(this._previousNorthEast&&this._previousNorthEast.lat()>=t.lat()&&this._previousNorthEast.lng()>=t.lng()&&this._previousSouthWest.lat()<=e.lat()&&this._previousSouthWest.lng()<=e.lng())&&(this._previousNorthEast=t,this._previousSouthWest=e,this._proxy.setOption("data",{actionName:"getMapMarkers",className:this._actionClassName,parameters:$.extend(this._additionalParameters,{excludedObjectIDs:this._stringifyExcludedObjectIds?JSON.stringify(this._objectIDs):this._objectIDs,eastLongitude:t.lng(),northLatitude:t.lat(),southLatitude:e.lat(),westLongitude:e.lng()})}),this._proxy.sendRequest(),!0)},_success:function(t,e,o){if(t.returnValues&&t.returnValues.markers)for(var s in t.returnValues.markers){var i=t.returnValues.markers[s];this.addMarker(i.latitude,i.longitude,i.title,null,i.infoWindow),i.objectID?this._objectIDs.push(i.objectID):i.objectIDs&&(this._objectIDs=this._objectIDs.concat(i.objectIDs))}},addMarker:function(t,e,o,s,i){var a=this._super(t,e,o,s,i);return this._markerClusterer.addMarker(a),this._markerSpiderfier.addMarker(a),a}}),WCF.Location.GoogleMaps.SuggestionMap=WCF.Location.GoogleMaps.LargeMap.extend({_locationSuggestionsButton:null,_suggestionSelectionCallback:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i);var a=$('<div class="gmnoprint googleMapsCustomControlContainer"><div class="gm-style-mtc"><div class="googleMapsCustomControl">'+WCF.Language.get("wcf.map.showLocationSuggestions")+"</div></div></div>");this._locationSuggestionsButton=a.find(".googleMapsCustomControl").click($.proxy(this._toggleLocationSuggestions,this)),this._map.controls[google.maps.ControlPosition.TOP_RIGHT].push(a.get(0))},_loadMarkers:function(){this._locationSuggestionsButton.hasClass("active")&&(this._super()||(this._loadSuggestions=!1))},_success:function(t,e,o){var s=this._markers.length;this._super(t,e,o),this._loadSuggestions&&s==this._markers.length&&(this._loadSuggestions=!1,new WCF.System.Notification(WCF.Language.get("wcf.map.noLocationSuggestions"),"info").show())},_toggleLocationSuggestions:function(){var t=!this._locationSuggestionsButton.hasClass("active");t&&(this._loadSuggestions=!0),this.showSuggestions(t)},addMarker:function(t,e,o,s,i){var a=$(i),n=$('<a class="googleMapsUseLocationSuggestionLink" />').text(WCF.Language.get("wcf.map.useLocationSuggestion")).click(this._suggestionSelectionCallback);a.append($("<p />").append(n));var r=this._super(t,e,o,"//mt.google.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png",a.get(0));return n.data("marker",r),r},setSuggestionSelectionCallback:function(t){this._suggestionSelectionCallback=t},showSuggestions:function(t){void 0===t&&(t=!0),this._locationSuggestionsButton.toggleClass("active",t);for(var e=[],o=0,s=this._markers.length;o<s;o++){var i=this._markers[o];i.draggable||(i.setVisible(t),t&&e.push(i))}this._markerClusterer.clearMarkers(),t&&this._markerClusterer.addMarkers(e),this._loadMarkers()}}),WCF.Location.GoogleMaps.LocationSearch=WCF.Search.Base.extend({_geocoder:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i),this.setDelay(500),this._geocoder=new google.maps.Geocoder},_createListItem:function(t){var e=$("<li><span>"+WCF.String.escapeHTML(t.formatted_address)+"</span></li>").appendTo(this._list);return e.data("location",t.geometry.location).data("label",t.formatted_address).click($.proxy(this._executeCallback,this)),this._itemCount++,e},_keyUp:function(t){switch(t.which){case $.ui.keyCode.LEFT:case $.ui.keyCode.RIGHT:return;case $.ui.keyCode.UP:return void this._selectPreviousItem();case $.ui.keyCode.DOWN:return void this._selectNextItem();case $.ui.keyCode.ENTER:return this._selectElement(t)}var e=this._getSearchString(t);""===e?this._clearList(!0):e.length>=this._triggerLength?this._delay?(null!==this._timer&&this._timer.stop(),this._timer=new WCF.PeriodicalExecuter($.proxy(function(){this._geocoder.geocode({address:e},$.proxy(this._success,this)),this._timer.stop(),this._timer=null},this),this._delay)):this._geocoder.geocode({address:e},$.proxy(this._success,this)):this._clearList(!1)},_success:function(t,e){if(this._clearList(!1),e==google.maps.GeocoderStatus.OK){if($.getLength(t)){var o=0;for(var s in t)if(this._createListItem(t[s]),10==++o)break}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var i=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(i).hasClass("dropdownOpen")||WCF.Dropdown.toggleDropdown(i),this._itemIndex=-1,WCF.Dropdown.getDropdown(i).data("disableAutoFocus")||this._selectNextItem()}}}),WCF.Location.GoogleMaps.LocationInput=Class.extend({_locationSearch:null,_map:null,_marker:null,init:function(t,e,o,s,i,a){this._searchInput=o,a?(this._map=new WCF.Location.GoogleMaps.SuggestionMap(t,e,a),this._map.setSuggestionSelectionCallback($.proxy(this._useSuggestion,this))):this._map=new WCF.Location.GoogleMaps.Map(t,e),this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(o,$.proxy(this._setMarkerByLocation,this)),s&&i?this._marker=this._map.addDraggableMarker(s,i):(this._marker=this._map.addDraggableMarker(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),WCF.Location.Util.getLocation($.proxy(function(t,e){void 0!==t&&void 0!==e&&(WCF.Location.GoogleMaps.Util.moveMarker(this._marker,t,e),WCF.Location.GoogleMaps.Util.focusMarker(this._marker))},this))),this._marker.addListener("dragend",$.proxy(this._updateLocation,this))},_useSuggestion:function(t){var e=$(t.currentTarget).data("marker");this._marker.setPosition(e.getPosition()),this._updateLocation(),this._map.showSuggestions(!1)},_updateLocation:function(){WCF.Location.GoogleMaps.Util.reverseGeocoding($.proxy(function(t){null!==t&&$(this._searchInput).val(t)},this),this._marker)},_setMarkerByLocation:function(t){this._marker.setPosition(t.location),WCF.Location.GoogleMaps.Util.focusMarker(this._marker),$(this._searchInput).val(t.label)},getMap:function(){return this._map},getMarker:function(){return this._marker}}),WCF.Location.GoogleMaps.Util={_geocoder:null,focusMarker:function(t){t.getMap().setCenter(t.getPosition())},getMarkerPosition:function(t){return{latitude:t.getPosition().lat(),longitude:t.getPosition().lng()}},moveMarker:function(t,e,o,s){t.setPosition(new google.maps.LatLng(e,o)),s&&google.maps.event.trigger(t,"dragend")},reverseGeocoding:function(t,e,o,s,i){e&&(o=e.getPosition().lat(),s=e.getPosition().lng()),null===this._geocoder&&(this._geocoder=new google.maps.Geocoder);var a=new google.maps.LatLng(o,s);this._geocoder.geocode({latLng:a},function(e,o){t(o==google.maps.GeocoderStatus.OK?i?e:e[0].formatted_address:null)})}}; })(this);
// WCF.Message.js
-(function (window, undefined) { "use strict";WCF.Message={},WCF.Message.BBCode={},WCF.Message.BBCode.CodeViewer=Class.extend({_dialog:null,init:function(){this._dialog=null,this._initCodeBoxes(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.BBCode.CodeViewer",$.proxy(this._initCodeBoxes,this)),WCF.DOMNodeInsertedHandler.execute()},_initCodeBoxes:function(){$(".codeBox:not(.jsCodeViewer)").each($.proxy(function(e,t){var i=$(t).addClass("jsCodeViewer");$('<span class="codeBoxPlainSource icon icon24 fa-files-o pointer jsTooltip" title="'+WCF.Language.get("wcf.message.bbcode.code.copy")+'" />').appendTo(i.find(".codeBoxHeader")).click($.proxy(this._click,this))},this))},_click:function(e){var t="";$(e.currentTarget).parents("div").next("ol").children("li").each(function(e,i){t&&(t+="\n"),t+=$(i).text().replace(/\n+$/,"").replace(/\u200B/g,"").replace(/\xa0/," ")}),null===this._dialog?(this._dialog=$('<div><textarea cols="60" rows="12" readonly></textarea></div>').hide().appendTo(document.body),this._dialog.children("textarea").val(t),this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.bbcode.code.copy")})):(this._dialog.children("textarea").val(t),this._dialog.wcfDialog("open"));var i=this._dialog.children("textarea")[0];"rtl"===document.documentElement.dir&&(i.dir="ltr",i.style.setProperty("text-align","left",""));var s=function(){i.selectionStart=0,i.selectionEnd=i.value.length};i.addEventListener("mouseup",s),window.setTimeout(s,10)}}),WCF.Message.EditHistory=Class.extend({_oldIDInputs:null,_newIDInputs:null,_containerSelector:"",_buttonSelector:".jsRevertButton",init:function(e,t,i,s,a){this._oldIDInputs=e,this._newIDInputs=t,this._containerSelector=i,this._buttonSelector=s||".jsRevertButton",this._options=$.extend({isVersionTracker:!1,versionTrackerObjectType:"",versionTrackerObjectId:0,redirectUrl:""},a),this.proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initInputs(),this._initElements()},_initInputs:function(){var e=this;this._newIDInputs.change(function(t){var i=parseInt($(this).val());"current"===$(this).val()&&(i=1/0),e._oldIDInputs.each(function(e){var t=parseInt($(this).val());"current"===$(this).val()&&(t=1/0),t>=i?$(this).disable():$(this).enable()})}),this._oldIDInputs.change(function(t){var i=parseInt($(this).val());"current"===$(this).val()&&(i=1/0),e._newIDInputs.each(function(e){var t=parseInt($(this).val());"current"===$(this).val()&&(t=1/0),t<=i?$(this).disable():$(this).enable()})}),this._oldIDInputs.filter(":checked").change(),this._newIDInputs.filter(":checked").change()},_initElements:function(){var e=this;$(this._containerSelector).each(function(t,i){$(i).find(e._buttonSelector).click($.proxy(e._click,e))})},_click:function(e){var t=$(e.currentTarget);if(e.preventDefault(),t.data("confirmMessage")){var i=this;WCF.System.Confirmation.show(t.data("confirmMessage"),function(e){"cancel"!==e&&i._sendRequest(t)},void 0,void 0,!0)}else this._sendRequest(t)},_sendRequest:function(e){this._options.isVersionTracker?(this.proxy.setOption("url",window.WSC_API_URL+"index.php?ajax-invoke/&t="+window.SECURITY_TOKEN),this.proxy.setOption("data",{actionName:"revert",className:"wcf\\system\\version\\VersionTracker",parameters:{objectType:this._options.versionTrackerObjectType,objectID:this._options.versionTrackerObjectId,versionID:$(e).data("objectID")}})):this.proxy.setOption("data",{actionName:"revert",className:"wcf\\data\\edit\\history\\entry\\EditHistoryEntryAction",objectIDs:[$(e).data("objectID")]}),this.proxy.sendRequest()},_success:function(e,t,i){this._options.redirectUrl?(new WCF.System.Notification).show(function(){window.location=this._options.redirectUrl}.bind(this)):window.location.reload(!0)}}),WCF.Message.FormGuard=Class.extend({init:function(){var e=$("form.jsFormGuard").removeClass("jsFormGuard").submit(function(){$(this).find(".formSubmit input[type=submit]").disable()});$(window).on("unload",function(){e.find(".formSubmit input[type=submit]").enable()})}}),WCF.Message.Preview=Class.extend({_className:"",_messageFieldID:"",_messageField:null,_proxy:null,_previewButton:null,_previewButtonLabel:"",init:function(e,t,i){return this._className=e,this._messageFieldID=$.wcfEscapeID(t),this._textarea=$("#"+this._messageFieldID),this._textarea.length?(i=$.wcfEscapeID(i),this._previewButton=$("#"+i),this._previewButton.length?(this._previewButton.click($.proxy(this._click,this)),void(this._proxy=new WCF.Action.Proxy({failure:$.proxy(this._failure,this),success:$.proxy(this._success,this)}))):void console.debug("[WCF.Message.Preview] Unable to find preview button identified by '"+i+"'")):void console.debug("[WCF.Message.Preview] Unable to find message field identified by '"+this._messageFieldID+"'")},_click:function(e){var t=this._getMessage();return null===t?void console.debug("[WCF.Message.Preview] Unable to access Redactor instance of '"+this._messageFieldID+"'"):(this._proxy.setOption("data",{actionName:"getMessagePreview",className:this._className,parameters:this._getParameters(t)}),this._proxy.sendRequest(),this._previewButtonLabel=this._previewButton.html(),this._previewButton.html(WCF.Language.get("wcf.global.loading")).disable(),e.stopPropagation(),!1)},_getParameters:function(e){var t={};return $("#settings_"+this._messageFieldID).find("input[type=checkbox]").each(function(e,i){var s=$(i);s.is(":checked")&&(t[s.prop("name")]=s.prop("value"))}),{data:{message:e},options:t}},_getMessage:function(){return this._textarea.redactor("code.get")},_success:function(e,t,i){this._previewButton.html(this._previewButtonLabel).enable(),this._textarea.parent().children("small.innerError").remove(),this._handleResponse(e)},_handleResponse:function(e){},_failure:function(e){if(null===e||void 0===e.returnValues||void 0===e.returnValues.errorType)return!0;this._previewButton.html(this._previewButtonLabel).enable();var t=this._textarea.parent().children("small.innerError").empty();t.length||(t=$('<small class="innerError" />').appendTo(this._textarea.parent()));var i="empty"===e.returnValues.errorType?WCF.Language.get("wcf.global.form.error.empty"):e.returnValues.errorMessage;return e.returnValues.realErrorMessage&&(i=e.returnValues.realErrorMessage),t.html(i),!1}}),WCF.Message.DefaultPreview=WCF.Message.Preview.extend({_dialog:null,_options:{},init:function(e){if(arguments.length>1&&"string"==typeof e)throw new Error("Outdated API call, please update your implementation.");if(this._options=$.extend({disallowedBBCodesPermission:"user.message.disallowedBBCodes",messageFieldID:"",previewButtonID:"",messageObjectType:"",messageObjectID:0},e),!this._options.messageObjectType)throw new Error("Field 'messageObjectType' cannot be empty.");this._super("wcf\\data\\bbcode\\MessagePreviewAction",this._options.messageFieldID,this._options.previewButtonID)},_handleResponse:function(e){require(["WoltLabSuite/Core/Ui/Dialog"],function(t){t.open(this,'<div class="htmlContent">'+e.returnValues.message+"</div>")}.bind(this))},_getParameters:function(e){var t=this._super(e);for(var i in this._options)this._options.hasOwnProperty(i)&&"messageFieldID"!==i&&"previewButtonID"!==i&&(t[i]=this._options[i]);return t},_dialogSetup:function(){return{id:"messagePreview",options:{title:WCF.Language.get("wcf.global.preview")},source:null}}}),WCF.Message.Multilingualism=Class.extend({_availableLanguages:{},_languageID:0,_languageInput:null,init:function(e,t,i){if(this._availableLanguages=t,this._languageID=e||0,this._languageInput=$("#languageID"),this._updateLabel(),this._languageInput.find(".dropdownMenu > li").click($.proxy(this._click,this)),!i){var s=this._languageInput.find(".dropdownMenu");$('<li class="dropdownDivider" />').appendTo(s),$('<li><span><span class="badge">'+this._availableLanguages[0]+"</span></span></li>").click($.proxy(this._disable,this)).appendTo(s)}this._languageInput.parents("form").submit($.proxy(this._submit,this))},_click:function(e){this._languageID=$(e.currentTarget).data("languageID"),this._updateLabel()},_disable:function(){this._languageID=0,this._updateLabel()},_updateLabel:function(){this._languageInput.find(".dropdownToggle > span").text(this._availableLanguages[this._languageID])},_submit:function(){this._languageInput.next("input[name=languageID]").prop("value",this._languageID)}}),WCF.Message.SmileyCategories=Class.extend({_cache:[],_proxy:null,_wysiwygSelector:"",init:function(e){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._wysiwygSelector=e,$("#smilies-"+this._wysiwygSelector).on("messagetabmenushow",$.proxy(this._click,this))},_click:function(e,t){e.preventDefault();var i=parseInt(t.activeTab.tab.data("smileyCategoryID"));if(i&&!t.activeTab.container.children("ul.smileyList").length){if(void 0!==this._cache[i])return void t.activeTab.container.html(this._cache[i]);this._proxy.setOption("data",{actionName:"getSmilies",className:"wcf\\data\\smiley\\category\\SmileyCategoryAction",objectIDs:[i]}),this._proxy.sendRequest()}},_success:function(e,t,i){var s=parseInt(e.returnValues.smileyCategoryID);this._cache[s]=e.returnValues.template,$("#smilies-"+this._wysiwygSelector+"-"+s).html(e.returnValues.template)}}),WCF.Message.Smilies=Class.extend({_editorId:"",init:function(e){this._editorId=e,$(".messageTabMenu[data-wysiwyg-container-id="+this._editorId+"]").on("mousedown",".jsSmiley",this._smileyClick.bind(this))},_smileyClick:function(e){e.preventDefault(),require(["EventHandler"],function(t){t.fire("com.woltlab.wcf.redactor2","insertSmiley_"+this._editorId,{img:e.currentTarget.children[0]})}.bind(this))}}),WCF.Message.InlineEditor=Class.extend({_container:{},_containerID:0,_dropdowns:{},_messageContainerSelector:".jsMessage",_messageEditorIDPrefix:"messageEditor",init:function(e,t,i){require(["WoltLabSuite/Core/Ui/Message/InlineEditor"],function(t){new t({className:this._getClassName(),containerId:e,editorPrefix:this._messageEditorIDPrefix,messageSelector:this._messageContainerSelector,quoteManager:i||null,callbackDropdownInit:this._callbackDropdownInit.bind(this)})}.bind(this))},_click:function(e,t){t=null===e?~~t:~~elData(e.currentTarget,"container-id"),require(["WoltLabSuite/Core/Ui/Message/InlineEditor"],function(e){e.legacyEdit(t)}.bind(this)),e&&e.preventDefault()},_initDropdownMenu:function(e,t){},_callbackDropdownInit:function(e,t){return this._initDropdownMenu($(e).wcfIdentify(),$(t)),null},_getClassName:function(){return""}}),WCF.Message.Submit={_buttons:{},registerButton:function(e,t){WCF.Browser.isChrome()&&(this._buttons[e]=$(t))},execute:function(e){this._buttons[e]&&this._buttons[e].trigger("click")}},WCF.Message.Quote={},WCF.Message.Quote.Handler=Class.extend({_activeContainerID:"",_className:"",_containers:{},_containerSelector:"",_copyQuote:null,_message:"",_messageBodySelector:"",_objectID:0,_objectType:"",_proxy:null,_quoteManager:null,init:function(e,t,i,s,a,n,o){return this._className=t,""===this._className?void console.debug("[WCF.Message.QuoteManager] Empty class name given, aborting."):(this._objectType=i,""===this._objectType?void console.debug("[WCF.Message.QuoteManager] Empty object type name given, aborting."):(this._containerSelector=s,this._message="",this._messageBodySelector=a,this._objectID=0,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initContainers(),o=o&&e.supportPaste(),this._initCopyQuote(o),$(document).mouseup($.proxy(this._mouseUp,this)),this._quoteManager=e,this._quoteManager.register(this._objectType,this),void WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.Quote.Handler"+i.hashCode(),$.proxy(this._initContainers,this))))},_initContainers:function(){var e=this;$(this._containerSelector).each(function(t,i){var s=$(i),a=s.wcfIdentify();if(!e._containers[a]){if(e._containers[a]=s,s.hasClass("jsInvalidQuoteTarget"))return!0;e._messageBodySelector&&s.data("body",s.find(e._messageBodySelector).data("containerID",a)),s.mousedown($.proxy(e._mouseDown,e)),e._containers[a].find(".jsQuoteMessage").click($.proxy(e._saveFullQuote,e))}})},_mouseDown:function(e){this._copyQuote.removeClass("active"),this._activeContainerID=e.currentTarget.classList.contains("jsInvalidQuoteTarget")?"":e.currentTarget.id},_getNodeText:function(e){var t=function(e){switch(e.tagName){case"BLOCKQUOTE":case"SCRIPT":return NodeFilter.FILTER_REJECT;case"IMG":if(!e.classList.contains("smiley")||0===e.alt.length)return NodeFilter.FILTER_REJECT;default:return NodeFilter.FILTER_ACCEPT}};t.acceptNode=t;for(var i,s=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,t,!0),a="",n=[];s.nextNode();){var o=s.currentNode;if(o.nodeType===Node.ELEMENT_NODE)switch(o.tagName){case"A":if(i=o.textContent,i.indexOf("…")>0){var r=i.split(/\u2026/);if(2===r.length){var l=o.href;0===l.indexOf(r[0])&&l.substr(-1*r[1].length)===r[1]&&(a+=l,n.push(o))}}break;case"BR":case"LI":case"UL":a+="\n";break;case"TD":$.browser.msie||(a+="\n");break;case"P":a+="\n\n";break;case"IMG":a+=" "+o.alt+" "}else{if("A"===o.parentNode.nodeName&&-1!==n.indexOf(o.parentNode))continue;a+=o.nodeValue.replace(/\n/g,"")}}return a},_mouseUp:function(){if(""===this._activeContainerID)return void this._copyQuote.removeClass("active");var e=window.getSelection();if(1!==e.rangeCount||e.isCollapsed)return void this._copyQuote.removeClass("active");var t=this._containers[this._activeContainerID],i=t.data("objectID");t=t.data("body")||t;for(var s=e.anchorNode;s&&s!==t[0];)s=s.parentNode;if(s!==t[0])return void this._copyQuote.removeClass("active");var a=this._getSelectedText(),n=$.trim(a);if(""==n)return void this._copyQuote.removeClass("active");var o=e.getRangeAt(0),r=o.startContainer.nodeType===Node.TEXT_NODE?o.startContainer.parentNode:o.startContainer,l=o.endContainer.nodeType===Node.TEXT_NODE?o.endContainer.parentNode:o.endContainer;if(r.closest("blockquote")||l.closest("blockquote"))return void this._copyQuote.removeClass("active");var c=this._getNodeText(t[0]);if(-1!==this._normalize(c).indexOf(this._normalize(n))){this._copyQuote.addClass("active");var u=this._getBoundingRectangle(t,window.getSelection()),d=this._copyQuote.getDimensions("outer"),h=(u.right-u.left)/2-d.width/2+u.left;this._copyQuote.css({top:u.top-d.height-7+"px",left:h+"px"}),this._copyQuote.removeClass("active"),this._activeContainerID="";var _=this;window.setTimeout(function(){var e=$.trim(_._getSelectedText());""!=e&&(_._copyQuote.addClass("active"),_._message=e,_._objectID=i)},10)}},_normalize:function(e){return e.replace(/\r?\n|\r/g,"\n").replace(/\s/g," ").replace(/\s{2,}/g," ")},_getBoundingRectangle:function(e,t){var i=null;if(t.rangeCount>0){var s=t.getRangeAt(0).getBoundingClientRect();i={left:s.left,right:s.right,top:s.top+$(document).scrollTop()}}return i},_initCopyQuote:function(e){if(this._copyQuote=$("#quoteManagerCopy"),!this._copyQuote.length){this._copyQuote=$('<div id="quoteManagerCopy" class="balloonTooltip interactive"><span class="jsQuoteManagerStore">'+WCF.Language.get("wcf.message.quote.quoteSelected")+"</span></div>").appendTo(document.body);var t=this._copyQuote.children("span.jsQuoteManagerStore").click($.proxy(this._saveQuote,this));e&&$('<span class="jsQuoteManagerQuoteAndInsert">'+WCF.Language.get("wcf.message.quote.quoteAndReply")+"</span>").click($.proxy(this._saveAndInsertQuote,this)).insertAfter(t)}},_getSelectedText:function(){var e=window.getSelection();return e.rangeCount?this._getNodeText(e.getRangeAt(0).cloneContents()):""},_saveFullQuote:function(e){e.preventDefault();var t=$(e.currentTarget);this._proxy.setOption("data",{actionName:"saveFullQuote",className:this._className,interfaceName:"wcf\\data\\IMessageQuoteAction",objectIDs:[t.data("objectID")]}),this._proxy.sendRequest(),t.data("isQuoted")?t.data("isQuoted",!1).children("a").removeClass("active"):t.data("isQuoted",!0).children("a").addClass("active");var i=t.parents(".buttonGroupNavigation");i.hasClass("jsMobileButtonGroupNavigation")&&i.children(".dropdownLabel").trigger("click")},_saveQuote:function(e){this._proxy.setOption("data",{actionName:"saveQuote",className:this._className,interfaceName:"wcf\\data\\IMessageQuoteAction",objectIDs:[this._objectID],parameters:{message:this._message,renderQuote:!0===e}}),this._proxy.sendRequest()},_saveAndInsertQuote:function(){this._saveQuote(!0)},_success:function(e){if(void 0!==e.returnValues.count){void 0!==e.returnValues.fullQuoteMessageIDs&&(e.returnValues.fullQuoteObjectIDs=e.returnValues.fullQuoteMessageIDs);var t=void 0!==e.returnValues.fullQuoteObjectIDs?e.returnValues.fullQuoteObjectIDs:{};this._quoteManager.updateCount(e.returnValues.count,t)}switch(e.actionName){case"saveQuote":case"saveFullQuote":e.returnValues.renderedQuote&&WCF.System.Event.fireEvent("com.woltlab.wcf.message.quote","insert",{forceInsert:"saveQuote"===e.actionName,quote:e.returnValues.renderedQuote})}},updateFullQuoteObjectIDs:function(e){for(var t in this._containers)this._containers[t].find(".jsQuoteMessage").each(function(t,i){var s=$(i).data("isQuoted",0);s.children("a").removeClass("active"),WCF.inArray(s.data("objectID"),e)&&s.data("isQuoted",1).children("a").addClass("active")})}}),WCF.Message.Quote.Manager=Class.extend({_buttons:{},_count:0,_dialog:null,_editorId:"",_editorIdAlternative:"",_form:null,_handlers:{},_hasTemplate:!1,_insertQuotes:!0,_proxy:null,_removeOnSubmit:[],_supportPaste:!1,_supportPasteOverride:!1,init:function(e,t,i,s){if(this._buttons={insert:null,remove:null},this._count=parseInt(e)||0,this._dialog=null,this._editorId="",this._editorIdAlternative="",this._form=null,this._handlers={},this._hasTemplate=!1,this._insertQuotes=!0,this._removeOnSubmit=[],this._supportPaste=!1,this._supportPasteOverride=!1,t){var a=$("#"+t);a.length&&(this._editorId=t,this._supportPaste=!0,this._form=a.parents("form:eq(0)"),this._form.length?(this._form.submit(this._submit.bind(this)),this._removeOnSubmit=s||[]):(this._form=null,this._supportPaste=!0===i))}this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this),url:"index.php?message-quote/&t="+SECURITY_TOKEN}),this._toggleShowQuotes(),WCF.System.Event.addListener("com.woltlab.wcf.quote","reload",this.countQuotes.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.message.quote","insert",function(e){WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","insertQuote_"+(this._editorIdAlternative?this._editorIdAlternative:this._editorId),{author:e.quote.username,content:e.quote.text,isText:!e.quote.isFullQuote,link:e.quote.link})}.bind(this))},setAlternativeEditor:function(e){this._editorIdAlternative||this._supportPaste||(this._hasTemplate=!1,this._supportPaste=!0,this._supportPasteOverride=!0),"object"==typeof e&&(e=e[0].id),this._editorIdAlternative=e},clearAlternativeEditor:function(){this._supportPasteOverride&&(this._hasTemplate=!1,this._supportPaste=!1,this._supportPasteOverride=!1),this._editorIdAlternative=""},register:function(e,t){this._handlers[e]=t},updateCount:function(e,t){this._count=parseInt(e)||0,this._toggleShowQuotes();for(var i in this._handlers)if(this._handlers.hasOwnProperty(i)){var s=t[i]||[];this._handlers[i].updateFullQuoteObjectIDs(s)}},insertQuotes:function(e,t,i){if(!this._insertQuotes)return void(this._insertQuotes=!0);new WCF.Action.Proxy({autoSend:!0,data:{actionName:"getRenderedQuotes",className:e,interfaceName:"wcf\\data\\IMessageQuoteAction",parameters:{parentObjectID:t}},success:i})},_toggleShowQuotes:function(){require(["WoltLabSuite/Core/Ui/Page/Action"],function(e){if(this._count){var t=e.get("showQuotes");void 0===t&&(t=elCreate("a"),t.addEventListener("mousedown",this._click.bind(this)),e.add("showQuotes",t)),t.textContent=WCF.Language.get("wcf.message.quote.showQuotes").replace(/#count#/,this._count),e.show("showQuotes")}else e.hide("showQuotes");this._hasTemplate=!1}.bind(this))},_click:function(){var e=document.activeElement;e.classList.contains("redactor-layer")&&$("#"+elData(e,"element-id")).redactor("selection.save"),this._hasTemplate?this._dialog.wcfDialog("open"):(this._proxy.showLoadingOverlayOnce(),this._proxy.setOption("data",{actionName:"getQuotes",supportPaste:this._supportPaste}),this._proxy.sendRequest())},renderDialog:function(e){null===this._dialog&&(this._dialog=$("#messageQuoteList"),this._dialog.length||(this._dialog=$('<div id="messageQuoteList" />').hide().appendTo(document.body))),this._dialog.html(e);var t=$('<div class="formSubmit" />').appendTo(this._dialog);this._supportPaste&&(this._buttons.insert=$('<button class="buttonPrimary">'+WCF.Language.get("wcf.message.quote.insertAllQuotes")+"</button>").click($.proxy(this._insertSelected,this)).appendTo(t)),this._buttons.remove=$("<button>"+WCF.Language.get("wcf.message.quote.removeAllQuotes")+"</button>").click($.proxy(this._removeSelected,this)).appendTo(t),this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.quote.manageQuotes")}),this._dialog.wcfDialog("render"),this._hasTemplate=!0;var i=this._dialog.find(".jsInsertQuote");if(this._supportPaste?i.click($.proxy(this._insertQuote,this)):i.hide(),this._dialog.find("input.jsCheckbox").change($.proxy(this._changeButtons,this)),this._removeOnSubmit.length){var s=this;this._dialog.find("input.jsRemoveQuote").each(function(e,t){var i=$(t).change($.proxy(this._change,this));WCF.inArray(i.parent("li").attr("data-quote-id"),s._removeOnSubmit)&&i.attr("checked","checked")})}},_changeButtons:function(){this._dialog.find("input.jsCheckbox:checked").length?(this._supportPaste&&this._buttons.insert.html(WCF.Language.get("wcf.message.quote.insertSelectedQuotes")),this._buttons.remove.html(WCF.Language.get("wcf.message.quote.removeSelectedQuotes"))):(this._supportPaste&&this._buttons.insert.html(WCF.Language.get("wcf.message.quote.insertAllQuotes")),this._buttons.remove.html(WCF.Language.get("wcf.message.quote.removeAllQuotes")))},_change:function(e){var t=$(e.currentTarget),i=t.parent("li").attr("data-quote-id");if(t.prop("checked"))this._removeOnSubmit.push(i);else{var s=this._removeOnSubmit.indexOf(i);-1!==s&&this._removeOnSubmit.splice(s,1)}},_insertSelected:function(){this._dialog.find("input.jsCheckbox:checked").length||this._dialog.find("input.jsCheckbox").prop("checked","checked"),this._dialog.find("input.jsCheckbox:checked").each($.proxy(function(e,t){this._insertQuote(null,t)},this)),this._dialog.wcfDialog("close")},_insertQuote:function(e,t){var i=$(e?e.currentTarget:t).parents("li:eq(0)"),s=i.children(".jsFullQuote")[0].textContent.trim(),a=i.parents(".message:eq(0)"),n=a.data("username"),o=a.data("link"),r=!elDataBool(i[0],"is-full-quote");WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","insertQuote_"+(this._editorIdAlternative?this._editorIdAlternative:this._editorId),{author:n,content:s,isText:r,link:o}),this._removeOnSubmit.push(i.data("quote-id")),null!==e&&this._dialog.wcfDialog("close")},_removeSelected:function(){this._dialog.find("input.jsCheckbox:checked").length||this._dialog.find("input.jsCheckbox").prop("checked","checked");var e=[];if(this._dialog.find("input.jsCheckbox:checked").each(function(t,i){e.push($(i).parents("li").attr("data-quote-id"))}),e.length){var t=[];for(var i in this._handlers)this._handlers.hasOwnProperty(i)&&t.push(i);this._proxy.setOption("data",{actionName:"remove",getFullQuoteObjectIDs:this._handlers.length>0,objectTypes:t,quoteIDs:e}),this._proxy.sendRequest(),this._dialog.wcfDialog("close")}},_submit:function(){if(this._supportPaste&&this._removeOnSubmit.length>0)for(var e=this._form.find(".formSubmit"),t=0,i=this._removeOnSubmit.length;t<i;t++)$('<input type="hidden" name="__removeQuoteIDs[]" value="'+this._removeOnSubmit[t]+'" />').appendTo(e)},getQuotesMarkedForRemoval:function(){return this._removeOnSubmit},markQuotesForRemoval:function(){this._removeOnSubmit.length&&(this._proxy.setOption("data",{actionName:"markForRemoval",quoteIDs:this._removeOnSubmit}),this._proxy.suppressErrors(),this._proxy.sendRequest())},removeMarkedQuotes:function(){this._removeOnSubmit.length&&(this._proxy.setOption("data",{actionName:"removeMarkedQuotes",getFullQuoteObjectIDs:this._handlers.length>0}),this._proxy.sendRequest())},countQuotes:function(){var e=[];for(var t in this._handlers)this._handlers.hasOwnProperty(t)&&e.push(t);this._proxy.setOption("data",{actionName:"count",getFullQuoteObjectIDs:e.length>0,objectTypes:e}),this._proxy.sendRequest()},_success:function(e){if(null!==e){if(void 0!==e.count){var t=void 0!==e.fullQuoteObjectIDs?e.fullQuoteObjectIDs:{};this.updateCount(e.count,t)}void 0!==e.template&&(""==$.trim(e.template)?this.updateCount(0,{}):this.renderDialog(e.template))}},supportPaste:function(){return this._supportPaste}}),WCF.Message.Share={},WCF.Message.Share.Content=Class.extend({_cache:{},_dialog:null,init:function(){this._cache={},this._dialog=null,this._initLinks(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.Share.Content",$.proxy(this._initLinks,this))},_initLinks:function(){$("a.jsButtonShare").removeClass("jsButtonShare").click($.proxy(this._click,this))},_click:function(e){e.preventDefault();var t=$(e.currentTarget),i=t.prop("href"),s=t.data("linkTitle")?t.data("linkTitle"):i,a=i.hashCode();if(void 0===this._cache[a]){var n=!1;null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),n=!0):this._dialog.empty();var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalink">'+WCF.Language.get("wcf.message.share.permalink")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalink" class="long" readonly />').attr("value",i).appendTo(o);var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkBBCode">'+WCF.Language.get("wcf.message.share.permalink.bbcode")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkBBCode" class="long" readonly />').attr("value","[url='"+i+"']"+s+"[/url]").appendTo(o);var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkHTML">'+WCF.Language.get("wcf.message.share.permalink.html")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkHTML" class="long" readonly />').attr("value",'<a href="'+i+'">'+WCF.String.escapeHTML(s)+"</a>").appendTo(o),this._cache[a]=this._dialog.html(),n?this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.share")}):this._dialog.wcfDialog("open")}else this._dialog.html(this._cache[a]).wcfDialog("open");this._enableSelection()},_enableSelection:function(){var e=this._dialog.find("input").click(function(){$(this).select()});navigator.userAgent.match(/iP(ad|hone|od)/)&&e.keydown(function(){return!1}).removeAttr("readonly").click(function(){this.setSelectionRange(0,9999)})}}),WCF.Message.Share.Page=Class.extend({init:function(){require(["WoltLabSuite/Core/Ui/Message/Share"],function(e){e.init()})}}),WCF.Message.UserMention=Class.extend({init:function(){throw new Error("Support for mentions in Redactor are now enabled by adding the attribute 'data-support-mention=\"true\"' to the textarea element.")}}),$.widget("wcf.messageTabMenu",{_tabs:[],_tabsByName:{},options:{collapsible:!0},_create:function(){var e=this.element.find("> nav"),t=e.find("> ul > li:not(.jsFlexibleMenuDropdown)"),i=this.element.find("> div, > fieldset");if(t.length!=i.length)return void console.debug("[wcf.messageTabMenu] Amount of tabs does not equal amount of tab containers, aborting.");var s=this.element.data("preselect");i.each(function(e,i){if(null!==elBySel(".innerError",i))return s=$(t[e]).data("name"),!1}),"true"===s&&(s=!0),this._tabs=[],this._tabsByName={};for(var a=0;a<t.length;a++){var n=$(t[a]),o=$(i[a]),r=n.data("name");if(void 0===r){var l=n.children("a").prop("href");void 0!==l&&l.match(/#([a-zA-Z_-]+)$/)&&(r=RegExp.$1),void 0===r&&(r=n.wcfIdentify(),console.debug("[wcf.messageTabMenu] Missing name attribute, assuming generic ID '"+r+"'"))}this._tabs.push({container:o,name:r,tab:n}),this._tabsByName[r]=a;var c=n.children("a").data("index",a).on("mousedown",this._showTab.bind(this));(s===r||!0===s&&0===a)&&c.trigger("mousedown")}!0===s&&this._tabs.length&&!window.matchMedia("(max-width: 544px)").matches&&this._tabs[0].tab.children("a").trigger("click");var u=this.element.data("collapsible");void 0!==u&&(this.options.collapsible=u);var d=elData(this.element[0],"wysiwyg-container-id");d&&WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+d,function(){for(var e=0,t=this._tabs.length;e<t;e++)this._tabs[e].container.removeClass("active"),this._tabs[e].tab.removeClass("active")}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this.element.remove()},_showTab:function(e,t,i){var s=null===e?t:$(e.currentTarget).data("index");i=!this.options.collapsible||!0===i;for(var a=null,n=0;n<this._tabs.length;n++){var o=this._tabs[n];if(n==s){if(!o.tab.hasClass("active")){o.tab.addClass("active"),o.container.addClass("active"),a=o;var r=o.container[0];if(null===elBySel(".messageTabMenuContent.active",r)&&null!==elBySel(".messageTabMenuContent",r)){var l=elBySel("nav > ul > li[data-name] > a",r);null!==l&&$(l).trigger("mousedown")}continue}if(!0===i)continue}o.tab.removeClass("active"),o.container.removeClass("active")}null!==e&&(e.preventDefault(),e.stopPropagation()),null!==a&&this._trigger("show",{},{activeTab:a}),$(window).trigger("resize")},showTab:function(e,t){if($.isNumeric(e)||void 0!==this._tabsByName[e]&&(e=this._tabsByName[e]),void 0===this._tabs[e])return void console.debug("[wcf.messageTabMenu] Cannot locate tab identified by '"+e+"'");this._showTab(null,e,t)},getTab:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].tab:null},getContainer:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].container:null}}); })(this);
+(function (window, undefined) { "use strict";WCF.Message={},WCF.Message.BBCode={},WCF.Message.BBCode.CodeViewer=Class.extend({init:function(){}}),WCF.Message.EditHistory=Class.extend({_oldIDInputs:null,_newIDInputs:null,_containerSelector:"",_buttonSelector:".jsRevertButton",init:function(e,t,s,i,n){this._oldIDInputs=e,this._newIDInputs=t,this._containerSelector=s,this._buttonSelector=i||".jsRevertButton",this._options=$.extend({isVersionTracker:!1,versionTrackerObjectType:"",versionTrackerObjectId:0,redirectUrl:""},n),this.proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initInputs(),this._initElements()},_initInputs:function(){var e=this;this._newIDInputs.change(function(t){var s=parseInt($(this).val());"current"===$(this).val()&&(s=1/0),e._oldIDInputs.each(function(e){var t=parseInt($(this).val());"current"===$(this).val()&&(t=1/0),t>=s?$(this).disable():$(this).enable()})}),this._oldIDInputs.change(function(t){var s=parseInt($(this).val());"current"===$(this).val()&&(s=1/0),e._newIDInputs.each(function(e){var t=parseInt($(this).val());"current"===$(this).val()&&(t=1/0),t<=s?$(this).disable():$(this).enable()})}),this._oldIDInputs.filter(":checked").change(),this._newIDInputs.filter(":checked").change()},_initElements:function(){var e=this;$(this._containerSelector).each(function(t,s){$(s).find(e._buttonSelector).click($.proxy(e._click,e))})},_click:function(e){var t=$(e.currentTarget);if(e.preventDefault(),t.data("confirmMessage")){var s=this;WCF.System.Confirmation.show(t.data("confirmMessage"),function(e){"cancel"!==e&&s._sendRequest(t)},void 0,void 0,!0)}else this._sendRequest(t)},_sendRequest:function(e){this._options.isVersionTracker?(this.proxy.setOption("url",window.WSC_API_URL+"index.php?ajax-invoke/&t="+window.SECURITY_TOKEN),this.proxy.setOption("data",{actionName:"revert",className:"wcf\\system\\version\\VersionTracker",parameters:{objectType:this._options.versionTrackerObjectType,objectID:this._options.versionTrackerObjectId,versionID:$(e).data("objectID")}})):this.proxy.setOption("data",{actionName:"revert",className:"wcf\\data\\edit\\history\\entry\\EditHistoryEntryAction",objectIDs:[$(e).data("objectID")]}),this.proxy.sendRequest()},_success:function(e,t,s){this._options.redirectUrl?(new WCF.System.Notification).show(function(){window.location=this._options.redirectUrl}.bind(this)):window.location.reload(!0)}}),WCF.Message.FormGuard=Class.extend({init:function(){var e=$("form.jsFormGuard").removeClass("jsFormGuard").submit(function(){$(this).find(".formSubmit input[type=submit]").disable()});$(window).on("unload",function(){e.find(".formSubmit input[type=submit]").enable()})}}),WCF.Message.Preview=Class.extend({_className:"",_messageFieldID:"",_messageField:null,_proxy:null,_previewButton:null,_previewButtonLabel:"",init:function(e,t,s){return this._className=e,this._messageFieldID=$.wcfEscapeID(t),this._textarea=$("#"+this._messageFieldID),this._textarea.length?(s=$.wcfEscapeID(s),this._previewButton=$("#"+s),this._previewButton.length?(this._previewButton.click($.proxy(this._click,this)),void(this._proxy=new WCF.Action.Proxy({failure:$.proxy(this._failure,this),success:$.proxy(this._success,this)}))):void console.debug("[WCF.Message.Preview] Unable to find preview button identified by '"+s+"'")):void console.debug("[WCF.Message.Preview] Unable to find message field identified by '"+this._messageFieldID+"'")},_click:function(e){var t=this._getMessage();return null===t?void console.debug("[WCF.Message.Preview] Unable to access Redactor instance of '"+this._messageFieldID+"'"):(this._proxy.setOption("data",{actionName:"getMessagePreview",className:this._className,parameters:this._getParameters(t)}),this._proxy.sendRequest(),this._previewButtonLabel=this._previewButton.html(),this._previewButton.html(WCF.Language.get("wcf.global.loading")).disable(),e.stopPropagation(),!1)},_getParameters:function(e){var t={};return $("#settings_"+this._messageFieldID).find("input[type=checkbox]").each(function(e,s){var i=$(s);i.is(":checked")&&(t[i.prop("name")]=i.prop("value"))}),{data:{message:e},options:t}},_getMessage:function(){return this._textarea.redactor("code.get")},_success:function(e,t,s){this._previewButton.html(this._previewButtonLabel).enable(),this._textarea.parent().children("small.innerError").remove(),this._handleResponse(e)},_handleResponse:function(e){},_failure:function(e){if(null===e||void 0===e.returnValues||void 0===e.returnValues.errorType)return!0;this._previewButton.html(this._previewButtonLabel).enable();var t=this._textarea.parent().children("small.innerError").empty();t.length||(t=$('<small class="innerError" />').appendTo(this._textarea.parent()));var s="empty"===e.returnValues.errorType?WCF.Language.get("wcf.global.form.error.empty"):e.returnValues.errorMessage;return e.returnValues.realErrorMessage&&(s=e.returnValues.realErrorMessage),t.html(s),!1}}),WCF.Message.DefaultPreview=WCF.Message.Preview.extend({_dialog:null,_options:{},init:function(e){if(arguments.length>1&&"string"==typeof e)throw new Error("Outdated API call, please update your implementation.");if(this._options=$.extend({disallowedBBCodesPermission:"user.message.disallowedBBCodes",messageFieldID:"",previewButtonID:"",messageObjectType:"",messageObjectID:0},e),!this._options.messageObjectType)throw new Error("Field 'messageObjectType' cannot be empty.");this._super("wcf\\data\\bbcode\\MessagePreviewAction",this._options.messageFieldID,this._options.previewButtonID)},_handleResponse:function(e){require(["WoltLabSuite/Core/Ui/Dialog"],function(t){t.open(this,'<div class="htmlContent">'+e.returnValues.message+"</div>")}.bind(this))},_getParameters:function(e){var t=this._super(e);for(var s in this._options)this._options.hasOwnProperty(s)&&"messageFieldID"!==s&&"previewButtonID"!==s&&(t[s]=this._options[s]);return t},_dialogSetup:function(){return{id:"messagePreview",options:{title:WCF.Language.get("wcf.global.preview")},source:null}}}),WCF.Message.Multilingualism=Class.extend({_availableLanguages:{},_languageID:0,_languageInput:null,init:function(e,t,s){if(this._availableLanguages=t,this._languageID=e||0,this._languageInput=$("#languageID"),this._updateLabel(),this._languageInput.find(".dropdownMenu > li").click($.proxy(this._click,this)),!s){var i=this._languageInput.find(".dropdownMenu");$('<li class="dropdownDivider" />').appendTo(i),$('<li><span><span class="badge">'+this._availableLanguages[0]+"</span></span></li>").click($.proxy(this._disable,this)).appendTo(i)}this._languageInput.parents("form").submit($.proxy(this._submit,this))},_click:function(e){this._languageID=$(e.currentTarget).data("languageID"),this._updateLabel()},_disable:function(){this._languageID=0,this._updateLabel()},_updateLabel:function(){this._languageInput.find(".dropdownToggle > span").text(this._availableLanguages[this._languageID])},_submit:function(){this._languageInput.next("input[name=languageID]").prop("value",this._languageID)}}),WCF.Message.SmileyCategories=Class.extend({_cache:[],_proxy:null,_wysiwygSelector:"",init:function(e){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._wysiwygSelector=e,$("#smilies-"+this._wysiwygSelector).on("messagetabmenushow",$.proxy(this._click,this))},_click:function(e,t){e.preventDefault();var s=parseInt(t.activeTab.tab.data("smileyCategoryID"));if(s&&!t.activeTab.container.children("ul.smileyList").length){if(void 0!==this._cache[s])return void t.activeTab.container.html(this._cache[s]);this._proxy.setOption("data",{actionName:"getSmilies",className:"wcf\\data\\smiley\\category\\SmileyCategoryAction",objectIDs:[s]}),this._proxy.sendRequest()}},_success:function(e,t,s){var i=parseInt(e.returnValues.smileyCategoryID);this._cache[i]=e.returnValues.template,$("#smilies-"+this._wysiwygSelector+"-"+i).html(e.returnValues.template)}}),WCF.Message.Smilies=Class.extend({init:function(e){require(["WoltLabSuite/Core/Ui/Smiley/Insert"],function(t){new t(e)})}}),WCF.Message.InlineEditor=Class.extend({_container:{},_containerID:0,_dropdowns:{},_messageContainerSelector:".jsMessage",_messageEditorIDPrefix:"messageEditor",init:function(e,t,s){require(["WoltLabSuite/Core/Ui/Message/InlineEditor"],function(t){new t({className:this._getClassName(),containerId:e,editorPrefix:this._messageEditorIDPrefix,messageSelector:this._messageContainerSelector,quoteManager:s||null,callbackDropdownInit:this._callbackDropdownInit.bind(this)})}.bind(this))},_click:function(e,t){t=null===e?~~t:~~elData(e.currentTarget,"container-id"),require(["WoltLabSuite/Core/Ui/Message/InlineEditor"],function(e){e.legacyEdit(t)}.bind(this)),e&&e.preventDefault()},_initDropdownMenu:function(e,t){},_callbackDropdownInit:function(e,t){return this._initDropdownMenu($(e).wcfIdentify(),$(t)),null},_getClassName:function(){return""}}),WCF.Message.Submit={_buttons:{},registerButton:function(e,t){WCF.Browser.isChrome()&&(this._buttons[e]=$(t))},execute:function(e){this._buttons[e]&&this._buttons[e].trigger("click")}},WCF.Message.Quote={},WCF.Message.Quote.Handler=Class.extend({_activeContainerID:"",_className:"",_containers:{},_containerSelector:"",_copyQuote:null,_message:"",_messageBodySelector:"",_objectID:0,_objectType:"",_proxy:null,_quoteManager:null,_selectionChangeTimer:null,_isMouseDown:!1,init:function(e,t,s,i,n,a,o){return this._className=t,""===this._className?void console.debug("[WCF.Message.QuoteManager] Empty class name given, aborting."):(this._objectType=s,""===this._objectType?void console.debug("[WCF.Message.QuoteManager] Empty object type name given, aborting."):(this._containerSelector=i,this._message="",this._messageBodySelector=n,this._objectID=0,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._selectionChangeTimer=null,this._isMouseDown=!1,this._initContainers(),o=o&&e.supportPaste(),this._initCopyQuote(o),$(document).mouseup($.proxy(this._mouseUp,this)),document.addEventListener("selectionchange",this._selectionchange.bind(this)),this._quoteManager=e,this._quoteManager.register(this._objectType,this),void WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.Quote.Handler"+s.hashCode(),$.proxy(this._initContainers,this))))},_initContainers:function(){var e=this;$(this._containerSelector).each(function(t,s){var i=$(s),n=i.wcfIdentify();if(!e._containers[n]){if(e._containers[n]=i,i.hasClass("jsInvalidQuoteTarget"))return!0;e._messageBodySelector&&i.data("body",i.find(e._messageBodySelector).data("containerID",n)),i.mousedown($.proxy(e._mouseDown,e)),i[0].classList.add("jsQuoteMessageContainer"),e._containers[n].find(".jsQuoteMessage").click($.proxy(e._saveFullQuote,e))}})},_selectionchange:function(){if(!this._isMouseDown){if(""===this._activeContainerID){var e=window.getSelection();if(1!==e.rangeCount||e.isCollapsed)return;var t=e.getRangeAt(0),s=elClosest(t.startContainer,".jsQuoteMessageContainer"),i=elClosest(t.endContainer,".jsQuoteMessageContainer");s&&s===i&&!s.classList.contains("jsInvalidQuoteTarget")&&(this._activeContainerID=s.id)}null!==this._selectionChangeTimer&&window.clearTimeout(this._selectionChangeTimer),this._selectionChangeTimer=window.setTimeout(this._mouseUp.bind(this),100)}},_mouseDown:function(e){this._copyQuote.removeClass("active"),this._activeContainerID=e.currentTarget.classList.contains("jsInvalidQuoteTarget")?"":e.currentTarget.id,null!==this._selectionChangeTimer&&(window.clearTimeout(this._selectionChangeTimer),this._selectionChangeTimer=null),this._isMouseDown=!0},_getNodeText:function(e){var t=function(e){switch(e.tagName){case"BLOCKQUOTE":case"SCRIPT":return NodeFilter.FILTER_REJECT;case"IMG":if(!e.classList.contains("smiley")||0===e.alt.length)return NodeFilter.FILTER_REJECT;default:return NodeFilter.FILTER_ACCEPT}};t.acceptNode=t;for(var s,i=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,t,!0),n="",a=[];i.nextNode();){var o=i.currentNode;if(o.nodeType===Node.ELEMENT_NODE)switch(o.tagName){case"A":if(s=o.textContent,s.indexOf("…")>0){var r=s.split(/\u2026/);if(2===r.length){var l=o.href;0===l.indexOf(r[0])&&l.substr(-1*r[1].length)===r[1]&&(n+=l,a.push(o))}}break;case"BR":case"LI":case"UL":n+="\n";break;case"TD":$.browser.msie||(n+="\n");break;case"P":n+="\n\n";break;case"IMG":n+=" "+o.alt+" ";break;case"DIV":(o.classList.contains("codeBoxHeadline")||o.classList.contains("codeBoxLine"))&&(n+="\n")}else{if("A"===o.parentNode.nodeName&&-1!==a.indexOf(o.parentNode))continue;n+=o.nodeValue.replace(/\n/g,"")}}return n},_mouseUp:function(e){if(e&&e.originalEvent instanceof Event&&(null!==this._selectionChangeTimer&&(window.clearTimeout(this._selectionChangeTimer),this._selectionChangeTimer=null),this._isMouseDown=!1),""===this._activeContainerID)return void this._copyQuote.removeClass("active");var t=window.getSelection();if(1!==t.rangeCount||t.isCollapsed)return void this._copyQuote.removeClass("active");var s=this._containers[this._activeContainerID],i=s.data("objectID");s=s.data("body")||s;for(var n=t.anchorNode;n&&n!==s[0];)n=n.parentNode;if(n!==s[0])return void this._copyQuote.removeClass("active");var a=this._getSelectedText(),o=$.trim(a);if(""===o)return void this._copyQuote.removeClass("active");var r=t.getRangeAt(0),l=r.startContainer.nodeType===Node.TEXT_NODE?r.startContainer.parentNode:r.startContainer,u=r.endContainer.nodeType===Node.TEXT_NODE?r.endContainer.parentNode:r.endContainer;if(l.closest("blockquote")||u.closest("blockquote"))return void this._copyQuote.removeClass("active");var c=this._getNodeText(s[0]);if(-1!==this._normalize(c).indexOf(this._normalize(o))){this._copyQuote.addClass("active");var h=this._getBoundingRectangle(s,window.getSelection()),d=this._copyQuote.getDimensions("outer"),_=(h.right-h.left)/2-d.width/2+h.left;this._copyQuote.css({top:h.bottom+7+"px",left:_+"px"}),this._copyQuote.removeClass("active"),null===this._selectionChangeTimer?this._activeContainerID="":(window.clearTimeout(this._selectionChangeTimer),this._selectionChangeTimer=null);var g=this;window.setTimeout(function(){var e=$.trim(g._getSelectedText());""!==e&&(g._copyQuote.addClass("active"),g._message=e,g._objectID=i)},10)}},_normalize:function(e){return e.replace(/\r?\n|\r/g,"\n").replace(/\s/g," ").replace(/\s{2,}/g," ")},_getBoundingRectangle:function(e,t){var s=null;if(t.rangeCount>0){var i=t.getRangeAt(0).getBoundingClientRect(),n=$(document).scrollTop();s={bottom:i.bottom+n,left:i.left,right:i.right,top:i.top+n}}return s},_initCopyQuote:function(e){if(this._copyQuote=$("#quoteManagerCopy"),!this._copyQuote.length){this._copyQuote=$('<div id="quoteManagerCopy" class="balloonTooltip interactive"><span class="jsQuoteManagerStore">'+WCF.Language.get("wcf.message.quote.quoteSelected")+"</span></div>").appendTo(document.body);var t=this._copyQuote.children("span.jsQuoteManagerStore").click($.proxy(this._saveQuote,this));e&&$('<span class="jsQuoteManagerQuoteAndInsert">'+WCF.Language.get("wcf.message.quote.quoteAndReply")+"</span>").click($.proxy(this._saveAndInsertQuote,this)).insertAfter(t)}},_getSelectedText:function(){var e=window.getSelection();return e.rangeCount?this._getNodeText(e.getRangeAt(0).cloneContents()):""},_saveFullQuote:function(e){e.preventDefault();var t=$(e.currentTarget);this._proxy.setOption("data",{actionName:"saveFullQuote",className:this._className,interfaceName:"wcf\\data\\IMessageQuoteAction",objectIDs:[t.data("objectID")]}),this._proxy.sendRequest(),t.data("isQuoted")?t.data("isQuoted",!1).children("a").removeClass("active"):t.data("isQuoted",!0).children("a").addClass("active");var s=t.parents(".buttonGroupNavigation");s.hasClass("jsMobileButtonGroupNavigation")&&s.children(".dropdownLabel").trigger("click")},_saveQuote:function(e){this._proxy.setOption("data",{actionName:"saveQuote",className:this._className,interfaceName:"wcf\\data\\IMessageQuoteAction",objectIDs:[this._objectID],parameters:{message:this._message,renderQuote:!0===e}}),this._proxy.sendRequest()},_saveAndInsertQuote:function(){this._saveQuote(!0)},_success:function(e){if(void 0!==e.returnValues.count){void 0!==e.returnValues.fullQuoteMessageIDs&&(e.returnValues.fullQuoteObjectIDs=e.returnValues.fullQuoteMessageIDs);var t=void 0!==e.returnValues.fullQuoteObjectIDs?e.returnValues.fullQuoteObjectIDs:{};this._quoteManager.updateCount(e.returnValues.count,t)}switch(e.actionName){case"saveQuote":case"saveFullQuote":e.returnValues.renderedQuote&&WCF.System.Event.fireEvent("com.woltlab.wcf.message.quote","insert",{forceInsert:"saveQuote"===e.actionName,quote:e.returnValues.renderedQuote})}},updateFullQuoteObjectIDs:function(e){for(var t in this._containers)this._containers[t].find(".jsQuoteMessage").each(function(t,s){var i=$(s).data("isQuoted",0);i.children("a").removeClass("active"),WCF.inArray(i.data("objectID"),e)&&i.data("isQuoted",1).children("a").addClass("active")})}}),WCF.Message.Quote.Manager=Class.extend({_buttons:{},_count:0,_dialog:null,_editorId:"",_editorIdAlternative:"",_form:null,_handlers:{},_hasTemplate:!1,_insertQuotes:!0,_proxy:null,_removeOnSubmit:[],_supportPaste:!1,_supportPasteOverride:!1,init:function(e,t,s,i){if(this._buttons={insert:null,remove:null},this._count=parseInt(e)||0,this._dialog=null,this._editorId="",this._editorIdAlternative="",this._form=null,this._handlers={},this._hasTemplate=!1,this._insertQuotes=!0,this._removeOnSubmit=[],this._supportPaste=!1,this._supportPasteOverride=!1,t){var n=$("#"+t);n.length&&(this._editorId=t,this._supportPaste=!0,this._form=n.parents("form:eq(0)"),this._form.length?(this._form.submit(this._submit.bind(this)),this._removeOnSubmit=i||[]):(this._form=null,this._supportPaste=!0===s))}this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this),url:"index.php?message-quote/&t="+SECURITY_TOKEN}),this._toggleShowQuotes(),WCF.System.Event.addListener("com.woltlab.wcf.quote","reload",this.countQuotes.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.message.quote","insert",function(e){WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","insertQuote_"+(this._editorIdAlternative?this._editorIdAlternative:this._editorId),{author:e.quote.username,content:e.quote.text,isText:!e.quote.isFullQuote,link:e.quote.link})}.bind(this))},setAlternativeEditor:function(e){this._editorIdAlternative||this._supportPaste||(this._hasTemplate=!1,this._supportPaste=!0,this._supportPasteOverride=!0),"object"==typeof e&&(e=e[0].id),this._editorIdAlternative=e},clearAlternativeEditor:function(){this._supportPasteOverride&&(this._hasTemplate=!1,this._supportPaste=!1,this._supportPasteOverride=!1),this._editorIdAlternative=""},register:function(e,t){this._handlers[e]=t},updateCount:function(e,t){this._count=parseInt(e)||0,this._toggleShowQuotes();for(var s in this._handlers)if(this._handlers.hasOwnProperty(s)){var i=t[s]||[];this._handlers[s].updateFullQuoteObjectIDs(i)}},insertQuotes:function(e,t,s){if(!this._insertQuotes)return void(this._insertQuotes=!0);new WCF.Action.Proxy({autoSend:!0,data:{actionName:"getRenderedQuotes",className:e,interfaceName:"wcf\\data\\IMessageQuoteAction",parameters:{parentObjectID:t}},success:s})},_toggleShowQuotes:function(){require(["WoltLabSuite/Core/Ui/Page/Action"],function(e){if(this._count){var t=e.get("showQuotes");void 0===t&&(t=elCreate("a"),t.addEventListener("mousedown",this._click.bind(this)),e.add("showQuotes",t)),t.textContent=WCF.Language.get("wcf.message.quote.showQuotes").replace(/#count#/,this._count),e.show("showQuotes")}else e.hide("showQuotes");this._hasTemplate=!1}.bind(this))},_click:function(){var e=document.activeElement;e.classList.contains("redactor-layer")&&$("#"+elData(e,"element-id")).redactor("selection.save"),this._hasTemplate?this._dialog.wcfDialog("open"):(this._proxy.showLoadingOverlayOnce(),this._proxy.setOption("data",{actionName:"getQuotes",supportPaste:this._supportPaste}),this._proxy.sendRequest())},renderDialog:function(e){null===this._dialog&&(this._dialog=$("#messageQuoteList"),this._dialog.length||(this._dialog=$('<div id="messageQuoteList" />').hide().appendTo(document.body))),this._dialog.html(e);var t=$('<div class="formSubmit" />').appendTo(this._dialog);this._supportPaste&&(this._buttons.insert=$('<button class="buttonPrimary">'+WCF.Language.get("wcf.message.quote.insertAllQuotes")+"</button>").click($.proxy(this._insertSelected,this)).appendTo(t)),this._buttons.remove=$("<button>"+WCF.Language.get("wcf.message.quote.removeAllQuotes")+"</button>").click($.proxy(this._removeSelected,this)).appendTo(t),this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.quote.manageQuotes")}),this._dialog.wcfDialog("render"),this._hasTemplate=!0;var s=this._dialog.find(".jsInsertQuote");if(this._supportPaste?s.click($.proxy(this._insertQuote,this)):s.hide(),this._dialog.find("input.jsCheckbox").change($.proxy(this._changeButtons,this)),this._removeOnSubmit.length){var i=this;this._dialog.find("input.jsRemoveQuote").each(function(e,t){var s=$(t).change($.proxy(this._change,this));WCF.inArray(s.parent("li").attr("data-quote-id"),i._removeOnSubmit)&&s.attr("checked","checked")})}},_changeButtons:function(){this._dialog.find("input.jsCheckbox:checked").length?(this._supportPaste&&this._buttons.insert.html(WCF.Language.get("wcf.message.quote.insertSelectedQuotes")),this._buttons.remove.html(WCF.Language.get("wcf.message.quote.removeSelectedQuotes"))):(this._supportPaste&&this._buttons.insert.html(WCF.Language.get("wcf.message.quote.insertAllQuotes")),this._buttons.remove.html(WCF.Language.get("wcf.message.quote.removeAllQuotes")))},_change:function(e){var t=$(e.currentTarget),s=t.parent("li").attr("data-quote-id");if(t.prop("checked"))this._removeOnSubmit.push(s);else{var i=this._removeOnSubmit.indexOf(s);-1!==i&&this._removeOnSubmit.splice(i,1)}},_insertSelected:function(){this._dialog.find("input.jsCheckbox:checked").length||this._dialog.find("input.jsCheckbox").prop("checked","checked"),this._dialog.find("input.jsCheckbox:checked").each($.proxy(function(e,t){this._insertQuote(null,t)},this)),this._dialog.wcfDialog("close")},_insertQuote:function(e,t){var s=$(e?e.currentTarget:t).parents("li:eq(0)"),i=s.children(".jsFullQuote")[0].textContent.trim(),n=s.parents(".message:eq(0)"),a=n.data("username"),o=n.data("link"),r=!elDataBool(s[0],"is-full-quote");WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","insertQuote_"+(this._editorIdAlternative?this._editorIdAlternative:this._editorId),{author:a,content:i,isText:r,link:o}),this._removeOnSubmit.push(s.data("quote-id")),null!==e&&this._dialog.wcfDialog("close")},_removeSelected:function(){this._dialog.find("input.jsCheckbox:checked").length||this._dialog.find("input.jsCheckbox").prop("checked","checked");var e=[];if(this._dialog.find("input.jsCheckbox:checked").each(function(t,s){e.push($(s).parents("li").attr("data-quote-id"))}),e.length){var t=[];for(var s in this._handlers)this._handlers.hasOwnProperty(s)&&t.push(s);this._proxy.setOption("data",{actionName:"remove",getFullQuoteObjectIDs:this._handlers.length>0,objectTypes:t,quoteIDs:e}),this._proxy.sendRequest(),this._dialog.wcfDialog("close")}},_submit:function(){if(this._supportPaste&&this._removeOnSubmit.length>0)for(var e=this._form.find(".formSubmit"),t=0,s=this._removeOnSubmit.length;t<s;t++)$('<input type="hidden" name="__removeQuoteIDs[]" value="'+this._removeOnSubmit[t]+'" />').appendTo(e)},getQuotesMarkedForRemoval:function(){return this._removeOnSubmit},markQuotesForRemoval:function(){this._removeOnSubmit.length&&(this._proxy.setOption("data",{actionName:"markForRemoval",quoteIDs:this._removeOnSubmit}),this._proxy.suppressErrors(),this._proxy.sendRequest())},removeMarkedQuotes:function(){this._removeOnSubmit.length&&(this._proxy.setOption("data",{actionName:"removeMarkedQuotes",getFullQuoteObjectIDs:this._handlers.length>0}),this._proxy.sendRequest())},countQuotes:function(){var e=[];for(var t in this._handlers)this._handlers.hasOwnProperty(t)&&e.push(t);this._proxy.setOption("data",{actionName:"count",getFullQuoteObjectIDs:e.length>0,objectTypes:e}),this._proxy.sendRequest()},_success:function(e){if(null!==e){if(void 0!==e.count){var t=void 0!==e.fullQuoteObjectIDs?e.fullQuoteObjectIDs:{};this.updateCount(e.count,t)}void 0!==e.template&&(""==$.trim(e.template)?this.updateCount(0,{}):this.renderDialog(e.template))}},supportPaste:function(){return this._supportPaste}}),WCF.Message.Share={},WCF.Message.Share.Content=Class.extend({_cache:{},_dialog:null,_shareButtonsTemplate:"",init:function(e){this._shareButtonsTemplate=e||"",this._cache={},this._dialog=null,this._initLinks(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.Share.Content",$.proxy(this._initLinks,this))},_initLinks:function(){$("a.jsButtonShare").removeClass("jsButtonShare").click($.proxy(this._click,this))},_click:function(e){e.preventDefault();var t=$(e.currentTarget),s=t.prop("href"),i=t.data("linkTitle")?t.data("linkTitle"):s,n=s.hashCode();if(void 0===this._cache[n]){var a=!1;null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),a=!0):this._dialog.empty();var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalink">'+WCF.Language.get("wcf.message.share.permalink")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalink" class="long" readonly />').attr("value",s).appendTo(o);var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkBBCode">'+WCF.Language.get("wcf.message.share.permalink.bbcode")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkBBCode" class="long" readonly />').attr("value","[url='"+s+"']"+i+"[/url]").appendTo(o);var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkHTML">'+WCF.Language.get("wcf.message.share.permalink.html")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkHTML" class="long" readonly />').attr("value",'<a href="'+s+'">'+WCF.String.escapeHTML(i)+"</a>").appendTo(o),""!==this._shareButtonsTemplate&&(o=$('<section class="section"><h2 class="sectionTitle">'+WCF.Language.get("wcf.message.share")+"</h2>"+this._shareButtonsTemplate+"</section>").appendTo(this._dialog),elData(o.children(".jsMessageShareButtons")[0],"url",WCF.String.escapeHTML(s))),this._cache[n]=this._dialog.html(),a?this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.share")}):this._dialog.wcfDialog("open")}else this._dialog.html(this._cache[n]).wcfDialog("open");this._enableSelection()},_enableSelection:function(){var e=this._dialog.find("input").click(function(){$(this).select()});navigator.userAgent.match(/iP(ad|hone|od)/)&&e.keydown(function(){return!1}).removeAttr("readonly").click(function(){this.setSelectionRange(0,9999)})}}),WCF.Message.Share.Page=Class.extend({init:function(){require(["WoltLabSuite/Core/Ui/Message/Share"],function(e){e.init()})}}),WCF.Message.UserMention=Class.extend({init:function(){throw new Error("Support for mentions in Redactor are now enabled by adding the attribute 'data-support-mention=\"true\"' to the textarea element.")}}),$.widget("wcf.messageTabMenu",{_tabs:[],_tabsByName:{},options:{collapsible:!0},_create:function(){var e=this.element.find("> nav"),t=e.find("> ul > li:not(.jsFlexibleMenuDropdown)"),s=this.element.find("> div, > fieldset");if(t.length!=s.length)return void console.debug("[wcf.messageTabMenu] Amount of tabs does not equal amount of tab containers, aborting.");var i=this.element.data("preselect");s.each(function(e,s){if(null!==elBySel(".innerError",s))return i=$(t[e]).data("name"),!1}),"true"===i&&(i=!0),this._tabs=[],this._tabsByName={};for(var n=0;n<t.length;n++){var a=$(t[n]),o=$(s[n]),r=a.data("name");if(void 0===r){var l=a.children("a").prop("href");void 0!==l&&l.match(/#([a-zA-Z_-]+)$/)&&(r=RegExp.$1),void 0===r&&(r=a.wcfIdentify())}this._tabs.push({container:o,name:r,tab:a}),this._tabsByName[r]=n;var u=a.children("a").data("index",n).on("mousedown",this._showTab.bind(this));u.attr("role","button").attr("tabindex","0").attr("aria-haspopup",!0).attr("aria-expanded",!1).attr("aria-controls",o[0].id),u.on("keydown",function(e){13!==e.which&&32!==e.which||(e.preventDefault(),this._showTab(e))}.bind(this)),(i===r||!0===i&&0===n)&&u.trigger("mousedown")}!0===i&&this._tabs.length&&!window.matchMedia("(max-width: 544px)").matches&&this._tabs[0].tab.children("a").trigger("click");var c=this.element.data("collapsible");void 0!==c&&(this.options.collapsible=c);var h=elData(this.element[0],"wysiwyg-container-id");h&&WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+h,function(){for(var e=0,t=this._tabs.length;e<t;e++)this._tabs[e].container.removeClass("active"),this._tabs[e].tab.removeClass("active")}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this.element.remove()},_showTab:function(e,t,s){var i=null===e?t:$(e.currentTarget).data("index");s=!this.options.collapsible||!0===s;for(var n=null,a=0;a<this._tabs.length;a++){var o=this._tabs[a];if(a==i){if(!o.tab.hasClass("active")){o.tab.addClass("active"),o.container.addClass("active"),n=o,o.tab.children("a").attr("aria-expanded",!0);var r=o.container[0];if(null===elBySel(".messageTabMenuContent.active",r)&&null!==elBySel(".messageTabMenuContent",r)){var l=elBySel("nav > ul > li[data-name] > a",r);null!==l&&$(l).trigger("mousedown")}continue}if(!0===s)continue}o.tab.removeClass("active"),o.container.removeClass("active"),o.tab.children("a").attr("aria-expanded",!1)}null!==e&&(e.preventDefault(),e.stopPropagation()),null!==n&&this._trigger("show",{},{activeTab:n}),$(window).trigger("resize")},showTab:function(e,t){if($.isNumeric(e)||void 0!==this._tabsByName[e]&&(e=this._tabsByName[e]),void 0===this._tabs[e])return void console.debug("[wcf.messageTabMenu] Cannot locate tab identified by '"+e+"'");this._showTab(null,e,t)},getTab:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].tab:null},getContainer:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].container:null}}); })(this);
// WCF.Poll.js
-(function (window, undefined) { "use strict";WCF.Poll={},WCF.Poll.Management=Class.extend({_container:null,_count:0,_editorId:"",_maxOptions:0,init:function(t,e,i,n){if(this._count=0,this._maxOptions=i||-1,this._container=$("#"+t).children("ol:eq(0)"),!this._container.length)return void console.debug("[WCF.Poll.Management] Invalid container id given, aborting.");e=e||[],this._createOptionList(e),n?(this._editorId=n,WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+n,this._reset.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","submit_"+n,this._submit.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","validate_"+n,this._validate.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","handleError_"+n,this._handleError.bind(this))):this._container.closest("form").submit($.proxy(this._submit,this)),require(["WoltLabSuite/Core/Ui/Sortable/List"],function(e){new e({containerId:t,options:{toleranceElement:"> div"}})})},_createOptionList:function(t){for(var e=0,i=t.length;e<i;e++){var n=t[e];this._createOption(n.optionValue,n.optionID)}t.length<this._maxOptions&&this._createOption()},_createOption:function(t,e,i){t=t||"",e=parseInt(e)||0,i=i||null;var n=$('<li class="sortableNode" />').data("optionID",e);null===i?n.appendTo(this._container):n.insertAfter(i);var o=$('<div class="pollOptionInput" />').appendTo(n);$('<span class="icon icon16 fa-arrows sortableNodeHandle" />').appendTo(o),$('<span class="icon icon16 fa-plus jsTooltip jsAddOption pointer" title="'+WCF.Language.get("wcf.poll.button.addOption")+'" />').click($.proxy(this._addOption,this)).appendTo(o),$('<span class="icon icon16 fa-times jsTooltip jsDeleteOption pointer" title="'+WCF.Language.get("wcf.poll.button.removeOption")+'" />').click($.proxy(this._removeOption,this)).appendTo(o);var s=$('<input type="text" value="'+t+'" maxlength="255" />').keydown($.proxy(this._keyDown,this)).appendTo(o);s.click(function(){document.activeElement!==this&&this.focus()}),null!==i&&s.focus(),WCF.DOMNodeInsertedHandler.execute(),++this._count===this._maxOptions&&this._container.find("span.jsAddOption").removeClass("pointer").addClass("disabled")},_keyDown:function(t){13===t.which&&($(t.currentTarget).parent().children(".jsAddOption").trigger("click"),t.preventDefault())},_addOption:function(t){if(this._count===this._maxOptions)return!1;var e=$(t.currentTarget).closest("li",this._container[0]);this._createOption(void 0,void 0,e)},_removeOption:function(t){$(t.currentTarget).closest("li",this._container[0]).remove(),this._count--,this._container.find("span.jsAddOption").addClass("pointer").removeClass("disabled"),0==this._container.children("li").length&&this._createOption()},_submit:function(t){var e=[];if(this._container.children("li").each(function(t,i){var n=$(i),o=$.trim(n.find("input").val());""!=o&&e.push(n.data("optionID")+"_"+o)}),"object"==typeof t.originalEvent&&t.originalEvent instanceof Event){if(e.length)for(var i=this._container.parents("form").find(".formSubmit"),n=0,o=e.length;n<o;n++)$('<input type="hidden" name="pollOptions['+n+']">').val(e[n]).appendTo(i)}else{t.poll={pollOptions:e};this._container.parents(".messageTabMenuContent:eq(0)").find("input").each(function(e,i){i.name&&("checkbox"!==i.type||i.checked)&&(t.poll[i.name]=i.value)})}},_reset:function(){for(var t=this._container[0];t.childElementCount>1;)t.removeChild(t.children[1]);elBySel("input",t.children[0]).value="",this._container.parents(".messageTabMenuContent:eq(0)").find("input").each(function(t,e){e.name&&("checkbox"===e.type?e.checked=!1:"text"===e.type?e.value="":"number"===e.type&&(e.value=e.min))}),require(["WoltLabSuite/Core/Date/Picker"],function(t){t.clear("pollEndTime_"+this._editorId)}.bind(this))},_validate:function(t){if(""!==elById("pollQuestion_"+this._editorId).value.trim()){var e=0;if(elBySelAll('li input[type="text"]',this._container[0],function(t){""!==t.value.trim()&&e++}),0===e)t.api.throwError(this._container[0],WCF.Language.get("wcf.global.form.error.empty")),t.valid=!1;else{var i=elById("pollMaxVotes_"+this._editorId),n=~~i.value;n&&n>e&&(t.api.throwError(i,WCF.Language.get("wcf.poll.maxVotes.error.invalid")),t.valid=!1)}}},_handleError:function(t){switch(t.returnValues.fieldName){case"pollEndTime":case"pollMaxVotes":var e="pollEndTime"===t.returnValues.fieldName?"endTime":"maxVotes",i=elCreate("small");i.className="innerError",i.innerHTML=WCF.Language.get("wcf.poll."+e+".error."+t.returnValues.errorType);var n=elById(t.returnValues.fieldName+"_"+this._editorId),o=n.parentElement;o.classList.contains("inputAddon")&&(n=o,o=o.parentElement),o.insertBefore(i,n.nextSibling),t.cancel=!0}}}),WCF.Poll.Manager=Class.extend({_cache:{},_canViewParticipants:{},_canViewResult:{},_canVote:{},_inputElements:{},_participants:{},_polls:{},_proxy:null,init:function(t){var e=$(t);if(!e.length)return void console.debug("[WCF.Poll.Manager] Given selector '"+t+"' does not match, aborting.");this._cache={},this._canViewParticipants={},this._canViewResult={},this._inputElements={},this._participants={},this._polls={},this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this),url:"index.php?poll/&t="+SECURITY_TOKEN});var i=this;e.each(function(t,e){var n=$(e),o=n.data("pollID");void 0===i._polls[o]&&(i._cache[o]={result:"",vote:""},i._polls[o]=n,i._canViewParticipants[o]=!!n.data("canViewParticipants"),i._canViewResult[o]=!!n.data("canViewResult"),i._canVote[o]=!!n.data("canVote"),i._bindListeners(o),n.data("inVote")&&i._prepareVote(o),i._toggleButtons(o))})},_bindListeners:function(t){this._polls[t].find(".jsButtonPollShowParticipants").data("pollID",t).click($.proxy(this._showParticipants,this)),this._polls[t].find(".jsButtonPollShowResult").data("pollID",t).click($.proxy(this._showResult,this)),this._polls[t].find(".jsButtonPollShowVote").data("pollID",t).click($.proxy(this._showVote,this)),this._polls[t].find(".jsButtonPollVote").data("pollID",t).click($.proxy(this._vote,this))},_showResult:function(t,e){var i=null===t?e:$(t.currentTarget).data("pollID");this._canViewResult[i]&&this._polls[i].data("inVote")&&(this._cache[i].result?(this._polls[i].find(".pollInnerContainer").html(this._cache[i].result),this._polls[i].data("inVote",!1),this._toggleButtons(i)):(this._proxy.setOption("data",{actionName:"getResult",pollID:i}),this._proxy.sendRequest()))},_showParticipants:function(t){var e=$(t.currentTarget).data("pollID");this._participants[e]||(this._participants[e]=new WCF.User.List("wcf\\data\\poll\\PollAction",this._polls[e].data("question"),{pollID:e})),this._participants[e].open()},_showVote:function(t,e){var i=null===t?e:$(t.currentTarget).data("pollID");this._canVote[i]&&(this._polls[i].data("inVote")||(this._cache[i].vote?(this._polls[i].find(".pollInnerContainer").html(this._cache[i].vote),this._polls[i].data("inVote",!0),this._prepareVote(i),this._toggleButtons(i)):(this._proxy.setOption("data",{actionName:"getVote",pollID:i}),this._proxy.sendRequest())))},_success:function(t,e,i){if(t&&t.actionName){var n=t.pollID;switch(t.resultTemplate&&(this._cache[n].result=t.resultTemplate),t.voteTemplate&&(this._cache[n].vote=t.voteTemplate),t.actionName){case"getResult":this._showResult(null,n);break;case"getVote":this._showVote(null,n);break;case"vote":this._canViewResult[n]=!0,this._canVote[n]=!!t.canVote,this._polls[n].data("isPublic")&&(this._canViewParticipants[n]=!0),this._showResult(null,n)}}},_prepareVote:function(t){this._polls[t].find(".jsButtonPollVote").disable();var e=this._polls[t].find(".pollInnerContainer > .jsPollVote"),i=this;this._inputElements[t]=e.find("input").change(function(){i._handleVoteButton(t)}),this._handleVoteButton(t);var n=e.data("maxVotes");this._inputElements[t].filter("[type=checkbox]").length&&(this._inputElements[t].change(function(){i._enforceMaxVotes(t,n)}),this._enforceMaxVotes(t,n))},_enforceMaxVotes:function(t,e){var i=this._inputElements[t];i.filter(":checked").length==e?i.filter(":not(:checked)").disable():i.enable()},_handleVoteButton:function(t){var e=this._inputElements[t],i=this._polls[t].find(".jsButtonPollVote");e.filter(":checked").length?i.enable():i.disable()},_toggleButtons:function(t){var e=this._polls[t].children(".formSubmit");e.find(".jsButtonPollShowParticipants, .jsButtonPollShowResult, .jsButtonPollShowVote, .jsButtonPollVote").hide();var i=!0;this._polls[t].data("inVote")?(i=!1,e.find(".jsButtonPollVote").show(),this._canViewResult[t]&&e.find(".jsButtonPollShowResult").show()):(this._canVote[t]&&(i=!1,e.find(".jsButtonPollShowVote").show()),this._canViewParticipants[t]&&(i=!1,e.find(".jsButtonPollShowParticipants").show())),i&&e.hide()},_vote:function(t){var e=$(t.currentTarget).data("pollID");if(this._canVote[e]){var i=[];this._inputElements[e].each(function(t,e){var n=$(e);n.is(":checked")&&i.push(n.data("optionID"))}),i.length&&(this._proxy.setOption("data",{actionName:"vote",optionIDs:i,pollID:e}),this._proxy.sendRequest())}}}); })(this);
+(function (window, undefined) { "use strict";WCF.Poll={},WCF.Poll.Management=Class.extend({_container:null,_count:0,_editorId:"",_maxOptions:0,init:function(t,e,i,n,o){if(this._count=0,this._maxOptions=i||-1,this._container=$("#"+t).children("ol:eq(0)"),this._fieldName=o||"pollOptions",!this._container.length)return void console.debug("[WCF.Poll.Management] Invalid container id given, aborting.");e=e||[],this._createOptionList(e),n?(this._editorId=n,WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+n,this._reset.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","submit_"+n,this._submit.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","validate_"+n,this._validate.bind(this)),WCF.System.Event.addListener("com.woltlab.wcf.redactor2","handleError_"+n,this._handleError.bind(this))):this._container.closest("form").submit($.proxy(this._submit,this)),require(["WoltLabSuite/Core/Ui/Sortable/List"],function(e){new e({containerId:t,options:{toleranceElement:"> div"}})})},_createOptionList:function(t){for(var e=0,i=t.length;e<i;e++){var n=t[e];this._createOption(n.optionValue,n.optionID)}t.length<this._maxOptions&&this._createOption()},_createOption:function(t,e,i){t=t||"",e=parseInt(e)||0,i=i||null;var n=$('<li class="sortableNode" />').data("optionID",e);null===i?n.appendTo(this._container):n.insertAfter(i);var o=$('<div class="pollOptionInput" />').appendTo(n);$('<span class="icon icon16 fa-arrows sortableNodeHandle" />').appendTo(o),$('<a role="button" href="#" class="icon icon16 fa-plus jsTooltip jsAddOption pointer" title="'+WCF.Language.get("wcf.poll.button.addOption")+'" />').click($.proxy(this._addOption,this)).appendTo(o),$('<a role="button" href="#" class="icon icon16 fa-times jsTooltip jsDeleteOption pointer" title="'+WCF.Language.get("wcf.poll.button.removeOption")+'" />').click($.proxy(this._removeOption,this)).appendTo(o);var s=$('<input type="text" value="'+t+'" maxlength="255" />').keydown($.proxy(this._keyDown,this)).appendTo(o);s.click(function(){document.activeElement!==this&&this.focus()}),null!==i&&s.focus(),WCF.DOMNodeInsertedHandler.execute(),++this._count===this._maxOptions&&this._container.find("span.jsAddOption").removeClass("pointer").addClass("disabled")},_keyDown:function(t){13===t.which&&($(t.currentTarget).parent().children(".jsAddOption").trigger("click"),t.preventDefault())},_addOption:function(t){if(t.preventDefault(),this._count===this._maxOptions)return!1;var e=$(t.currentTarget).closest("li",this._container[0]);this._createOption(void 0,void 0,e)},_removeOption:function(t){t.preventDefault(),$(t.currentTarget).closest("li",this._container[0]).remove(),this._count--,this._container.find("span.jsAddOption").addClass("pointer").removeClass("disabled"),0==this._container.children("li").length&&this._createOption()},_submit:function(t){var e=[];if(this._container.children("li").each(function(t,i){var n=$(i),o=$.trim(n.find("input").val());""!=o&&e.push(n.data("optionID")+"_"+o)}),"object"==typeof t.originalEvent&&t.originalEvent instanceof Event){if(e.length)for(var i=this._container.parents("form").find(".formSubmit"),n=0,o=e.length;n<o;n++)$('<input type="hidden" name="'+this._fieldName+"["+n+']">').val(e[n]).appendTo(i)}else{t.poll={pollOptions:e};this._container.parents(".messageTabMenuContent:eq(0)").find("input").each(function(e,i){i.name&&("checkbox"!==i.type||i.checked)&&(t.poll[i.name]=i.value)})}},_reset:function(){for(var t=this._container[0];t.childElementCount>1;)t.removeChild(t.children[1]);elBySel("input",t.children[0]).value="",this._container.parents(".messageTabMenuContent:eq(0)").find("input").each(function(t,e){e.name&&("checkbox"===e.type?e.checked=!1:"text"===e.type?e.value="":"number"===e.type&&(e.value=e.min))}),require(["WoltLabSuite/Core/Date/Picker"],function(t){t.clear("pollEndTime_"+this._editorId)}.bind(this))},_validate:function(t){if(""!==elById("pollQuestion_"+this._editorId).value.trim()){var e=0;if(elBySelAll('li input[type="text"]',this._container[0],function(t){""!==t.value.trim()&&e++}),0===e)t.api.throwError(this._container[0],WCF.Language.get("wcf.global.form.error.empty")),t.valid=!1;else{var i=elById("pollMaxVotes_"+this._editorId),n=~~i.value;n&&n>e&&(t.api.throwError(i,WCF.Language.get("wcf.poll.maxVotes.error.invalid")),t.valid=!1)}}},_handleError:function(t){switch(t.returnValues.fieldName){case"pollEndTime":case"pollMaxVotes":var e="pollEndTime"===t.returnValues.fieldName?"endTime":"maxVotes",i=elCreate("small");i.className="innerError",i.innerHTML=WCF.Language.get("wcf.poll."+e+".error."+t.returnValues.errorType);var n=elById(t.returnValues.fieldName+"_"+this._editorId),o=n.parentElement;o.classList.contains("inputAddon")&&(n=o,o=o.parentElement),o.insertBefore(i,n.nextSibling),t.cancel=!0}}}),WCF.Poll.Manager=Class.extend({_cache:{},_canViewParticipants:{},_canViewResult:{},_canVote:{},_inputElements:{},_participants:{},_polls:{},_proxy:null,init:function(t){var e=$(t);if(!e.length)return void console.debug("[WCF.Poll.Manager] Given selector '"+t+"' does not match, aborting.");this._cache={},this._canViewParticipants={},this._canViewResult={},this._inputElements={},this._participants={},this._polls={},this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this),url:"index.php?poll/&t="+SECURITY_TOKEN});var i=this;e.each(function(t,e){var n=$(e),o=n.data("pollID");void 0===i._polls[o]&&(i._cache[o]={result:"",vote:""},i._polls[o]=n,i._canViewParticipants[o]=!!n.data("canViewParticipants"),i._canViewResult[o]=!!n.data("canViewResult"),i._canVote[o]=!!n.data("canVote"),i._bindListeners(o),n.data("inVote")&&i._prepareVote(o),i._toggleButtons(o))})},_bindListeners:function(t){this._polls[t].find(".jsButtonPollShowParticipants").data("pollID",t).click($.proxy(this._showParticipants,this)),this._polls[t].find(".jsButtonPollShowResult").data("pollID",t).click($.proxy(this._showResult,this)),this._polls[t].find(".jsButtonPollShowVote").data("pollID",t).click($.proxy(this._showVote,this)),this._polls[t].find(".jsButtonPollVote").data("pollID",t).click($.proxy(this._vote,this))},_showResult:function(t,e){var i=null===t?e:$(t.currentTarget).data("pollID");this._canViewResult[i]&&this._polls[i].data("inVote")&&(this._cache[i].result?(this._polls[i].find(".pollInnerContainer").html(this._cache[i].result),this._polls[i].data("inVote",!1),this._toggleButtons(i)):(this._proxy.setOption("data",{actionName:"getResult",pollID:i}),this._proxy.sendRequest()))},_showParticipants:function(t){var e=$(t.currentTarget).data("pollID");this._participants[e]||(this._participants[e]=new WCF.User.List("wcf\\data\\poll\\PollAction",this._polls[e].data("question"),{pollID:e})),this._participants[e].open()},_showVote:function(t,e){var i=null===t?e:$(t.currentTarget).data("pollID");this._canVote[i]&&(this._polls[i].data("inVote")||(this._cache[i].vote?(this._polls[i].find(".pollInnerContainer").html(this._cache[i].vote),this._polls[i].data("inVote",!0),this._prepareVote(i),this._toggleButtons(i)):(this._proxy.setOption("data",{actionName:"getVote",pollID:i}),this._proxy.sendRequest())))},_success:function(t,e,i){if(t&&t.actionName){var n=t.pollID;switch(t.resultTemplate&&(this._cache[n].result=t.resultTemplate),t.voteTemplate&&(this._cache[n].vote=t.voteTemplate),t.actionName){case"getResult":this._showResult(null,n);break;case"getVote":this._showVote(null,n);break;case"vote":this._canViewResult[n]=!0,this._canVote[n]=!!t.canVote,this._polls[n].data("isPublic")&&(this._canViewParticipants[n]=!0),this._showResult(null,n)}}},_prepareVote:function(t){this._polls[t].find(".jsButtonPollVote").disable();var e=this._polls[t].find(".pollInnerContainer > .jsPollVote"),i=this;this._inputElements[t]=e.find("input").change(function(){i._handleVoteButton(t)}),this._handleVoteButton(t);var n=e.data("maxVotes");this._inputElements[t].filter("[type=checkbox]").length&&(this._inputElements[t].change(function(){i._enforceMaxVotes(t,n)}),this._enforceMaxVotes(t,n))},_enforceMaxVotes:function(t,e){var i=this._inputElements[t];i.filter(":checked").length==e?i.filter(":not(:checked)").disable():i.enable()},_handleVoteButton:function(t){var e=this._inputElements[t],i=this._polls[t].find(".jsButtonPollVote");e.filter(":checked").length?i.enable():i.disable()},_toggleButtons:function(t){var e=this._polls[t].children(".formSubmit");e.find(".jsButtonPollShowParticipants, .jsButtonPollShowResult, .jsButtonPollShowVote, .jsButtonPollVote").hide();var i=!0;this._polls[t].data("inVote")?(i=!1,e.find(".jsButtonPollVote").show(),this._canViewResult[t]&&e.find(".jsButtonPollShowResult").show()):(this._canVote[t]&&(i=!1,e.find(".jsButtonPollShowVote").show()),this._canViewParticipants[t]&&(i=!1,e.find(".jsButtonPollShowParticipants").show())),i&&e.hide()},_vote:function(t){var e=$(t.currentTarget).data("pollID");if(this._canVote[e]){var i=[];this._inputElements[e].each(function(t,e){var n=$(e);n.is(":checked")&&i.push(n.data("optionID"))}),i.length&&(this._proxy.setOption("data",{actionName:"vote",optionIDs:i,pollID:e}),this._proxy.sendRequest())}}}); })(this);
// WCF.Search.Message.js
(function (window, undefined) { "use strict";WCF.Search.Message={},WCF.Search.Message.KeywordList=WCF.Search.Base.extend({_className:"wcf\\data\\search\\keyword\\SearchKeywordAction",_divider:null,_forceSubmit:!1,init:function(e,i,s){if(!$.isFunction(i))return void console.debug("[WCF.Search.Message.KeywordList] The given callback is invalid, aborting.");this._callback=i,this._excludedSearchValues=[],s&&(this._excludedSearchValues=s),this._searchInput=$(e).keyup($.proxy(this._keyUp,this)).keydown($.proxy(function(e){13===e.which&&this._itemCount&&-1!==this._itemIndex&&e.preventDefault()},this));var t=WCF.Dropdown.getDropdownMenu(this._searchInput.parents(".dropdown").wcfIdentify()),r=t.find("li.dropdownDivider").last();this._divider=$('<li class="dropdownDivider" />').hide().insertBefore(r),this._list=$('<li class="dropdownList"><ul /></li>').hide().insertBefore(r).children("ul"),t.find("input, label").on("click",function(e){e.stopPropagation()}),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)})},_createListItem:function(e){this._divider.show(),this._list.parent().show(),this._super(e)},_clearList:function(e){e&&this._searchInput.val(""),this._divider.hide(),this._list.empty().parent().hide(),WCF.CloseOverlayHandler.removeCallback("WCF.Search.Base"),this._itemCount=0,this._itemIndex=-1}}); })(this);
// WCF.User.js
-(function (window, undefined) { "use strict";WCF.User.Login=Class.extend({_loginSubmitButton:null,_password:null,_passwordContainer:null,_useCookies:null,_useCookiesContainer:null,init:function(t){this._loginSubmitButton=$("#loginSubmitButton"),this._password=$("#password"),this._passwordContainer=this._password.parents("dl"),this._useCookies=$("#useCookies"),this._useCookiesContainer=this._useCookies.parents("dl"),$("#loginForm").find("input[name=action]").change($.proxy(this._change,this)),t&&WCF.User.QuickLogin.init()},_change:function(t){"register"===$(t.currentTarget).val()?this._setState(!1,WCF.Language.get("wcf.user.button.register")):this._setState(!0,WCF.Language.get("wcf.user.button.login"))},_setState:function(t,e){t?(this._password.enable(),this._passwordContainer.removeClass("disabled"),this._useCookies.enable(),this._useCookiesContainer.removeClass("disabled")):(this._password.disable(),this._passwordContainer.addClass("disabled"),this._useCookies.disable(),this._useCookiesContainer.addClass("disabled")),this._loginSubmitButton.val(e)}}),WCF.User.Panel={},WCF.User.Panel.Abstract=Class.extend({_badge:null,_dropdown:null,_identifier:"",_loadData:!0,_markAllAsReadLink:null,_options:{},_proxy:null,_triggerElement:null,init:function(t,e,i){if(this._dropdown=null,this._loadData=!0,this._identifier=e,this._triggerElement=t,this._options=i,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._triggerElement.click($.proxy(this.toggle,this)),this._options.showAllLink&&this._triggerElement.dblclick($.proxy(this._dblClick,this)),!0===this._options.staticDropdown)this._loadData=!1;else{var s=this._triggerElement.find("span.badge");s.length&&(this._badge=s)}},toggle:function(t){if(t instanceof Event&&t.preventDefault(),null===this._dropdown&&(this._dropdown=this._initDropdown()),this._dropdown.toggle()){if(!this._loadData&&null!==this._badge){(parseInt(this._badge.text())||0)&&!this._dropdown.getItemList().children(".interactiveDropdownItemOutstanding").length&&(this._loadData=!0)}this._loadData&&(this._loadData=!1,this._load())}return!1},_dblClick:function(t){return t.preventDefault(),window.location=this._options.showAllLink,!1},_initDropdown:function(){var t=WCF.Dropdown.Interactive.Handler.create(this._triggerElement,this._identifier,this._options);return $('<li class="loading"><span class="icon icon24 fa-spinner" /> <span>'+WCF.Language.get("wcf.global.loading")+"</span></li>").appendTo(t.getItemList()),t},_load:function(){},_success:function(t){if(void 0!==t.returnValues.template){var e=this._dropdown.getItemList().empty();if($(t.returnValues.template).appendTo(e),e.children().length||$('<li class="noItems">'+this._options.noItems+"</li>").appendTo(e),this._options.enableMarkAsRead){var i=this._dropdown.getItemList().children(".interactiveDropdownItemOutstanding");if(null===this._markAllAsReadLink&&i.length&&this._options.markAllAsReadConfirmMessage){(this._markAllAsReadLink=$('<li class="interactiveDropdownItemMarkAllAsRead"><a href="#" title="'+WCF.Language.get("wcf.user.panel.markAllAsRead")+'" class="jsTooltip"><span class="icon icon24 fa-check" /></a></li>').appendTo(this._dropdown.getLinkList())).click(function(t){return this._dropdown.close(),WCF.System.Confirmation.show(this._options.markAllAsReadConfirmMessage,function(t){"confirm"===t&&this._markAllAsRead()}.bind(this)),!1}.bind(this))}i.each(function(t,e){var i=$(e).addClass("interactiveDropdownItemOutstandingIcon"),s=i.data("objectID");$('<div class="interactiveDropdownItemMarkAsRead"><a href="#" title="'+WCF.Language.get("wcf.user.panel.markAsRead")+'" class="jsTooltip"><span class="icon icon16 fa-check" /></a></div>').appendTo(i).click(function(t){return this._markAsRead(t,s),!1}.bind(this))}.bind(this))}this._dropdown.getItemList().children().each(function(t,e){var i=$(e),s=i.data("link");s&&($.browser.msie?i.click(function(t){if("A"!==t.target.tagName)return window.location=s,!1}):(i.addClass("interactiveDropdownItemShadow"),$('<a href="'+s+'" class="interactiveDropdownItemShadowLink" />').appendTo(i)),i.data("linkReplaceAll")&&i.find("> .box48 a:not(.userLink)").prop("href",s))}),this._dropdown.rebuildScrollbar()}if(void 0!==t.returnValues.totalCount&&this.updateBadge(t.returnValues.totalCount),this._options.enableMarkAsRead)if(t.returnValues.markAsRead){var s=this._dropdown.getItemList().children("li[data-object-id="+t.returnValues.markAsRead+"]");s.length&&(s.removeClass("interactiveDropdownItemOutstanding").data("isRead",!0),s.children(".interactiveDropdownItemMarkAsRead").remove())}else t.returnValues.markAllAsRead&&(this.resetItems(),this.updateBadge(0))},_markAsRead:function(t,e){},_markAllAsRead:function(){},updateBadge:function(t){t=parseInt(t)||0,t?(null===this._badge&&(this._badge=$('<span class="badge badgeUpdate" />').appendTo(this._triggerElement.children("a")),this._badge.before(" ")),this._badge.text(t)):null!==this._badge&&(this._badge.remove(),this._badge=null),this._options.enableMarkAsRead&&(t||null===this._markAllAsReadLink||(this._markAllAsReadLink.remove(),this._markAllAsReadLink=null)),WCF.System.Event.fireEvent("com.woltlab.wcf.userMenu","updateBadge",{count:t,identifier:this._identifier})},resetItems:function(){null!==this._dropdown&&(this._dropdown.resetItems(),this._loadData=!0)}}),WCF.User.Panel.Notification=WCF.User.Panel.Abstract.extend({_favico:null,init:function(t){t.enableMarkAsRead=!0,this._super($("#userNotifications"),"userNotifications",t);try{if(this._favico=new Favico({animation:"none",type:"circle"}),null!==this._badge){var e=parseInt(this._badge.text())||0;this._favico.badge(e)}}catch(t){console.debug("[WCF.User.Panel.Notification] Failed to initialized Favico: "+t.message)}WCF.System.PushNotification.addCallback("userNotificationCount",$.proxy(this.updateUserNotificationCount,this)),require(["EventHandler"],function(t){t.add("com.woltlab.wcf.UserMenuMobile","more",function(t){"com.woltlab.wcf.notifications"===t.identifier&&this.toggle()}.bind(this))}.bind(this))},_initDropdown:function(){var t=this._super();return $('<li><a href="'+this._options.settingsLink+'" title="'+WCF.Language.get("wcf.user.panel.settings")+'" class="jsTooltip"><span class="icon icon24 fa-cog" /></a></li>').appendTo(t.getLinkList()),t},_load:function(){this._proxy.setOption("data",{actionName:"getOutstandingNotifications",className:"wcf\\data\\user\\notification\\UserNotificationAction"}),this._proxy.sendRequest()},_markAsRead:function(t,e){this._proxy.setOption("data",{actionName:"markAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction",objectIDs:[e]}),this._proxy.sendRequest()},_markAllAsRead:function(t){this._proxy.setOption("data",{actionName:"markAllAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction"}),this._proxy.sendRequest()},resetItems:function(){this._super(),this._markAllAsReadLink&&(this._markAllAsReadLink.remove(),this._markAllAsReadLink=null)},updateBadge:function(t){t=parseInt(t)||0,$("#userNotifications").attr("data-count",t),null!==this._favico&&this._favico.badge(t),this._super(t)},updateUserNotificationCount:function(t){null!==this._dropdown&&this._dropdown.resetItems(),this.updateBadge(t)}}),WCF.User.Panel.UserMenu=WCF.User.Panel.Abstract.extend({init:function(){this._super($("#userMenu"),"userMenu",{pointerOffset:"13px",staticDropdown:!0})}}),WCF.User.QuickLogin={init:function(){require(["EventHandler","Ui/Dialog"],function(t,e){var i=elById("loginForm"),s=elBySel(".loginFormLogin",i);s&&!s.nextElementSibling&&i.classList.add("loginFormLoginOnly");for(var a=elBySel(".loginFormRegister",i),n=function(t){if(t instanceof Event&&(t.preventDefault(),t.stopPropagation()),i.style.removeProperty("display"),e.openStatic("loginForm",null,{title:WCF.Language.get("wcf.user.login")}),null!==s&&null!==a){var n=s.offsetTop,o=0;if(i.clientWidth>2*s.clientWidth)for(;n<a.offsetTop-50;)o+=100,s.style.setProperty("margin-bottom",o+"px","")}},o=document.getElementsByClassName("loginLink"),r=0,l=o.length;r<l;r++)o[r].addEventListener(WCF_CLICK_EVENT,n);var c=i.querySelector("#loginForm input[name=url]");null===c||c.value.match(/^https?:\/\//)||c.setAttribute("value",window.location.protocol+"//"+window.location.host+c.getAttribute("value")),t.add("com.woltlab.wcf.UserMenuMobile","more",function(t){"com.woltlab.wcf.login"===t.identifier&&(t.handler.close(!0),n())})})}},WCF.User.Profile={},WCF.User.Profile.ActivityPointList={_cache:{},_dialog:null,_didInit:!1,_proxy:null,init:function(){this._didInit||(this._cache={},this._dialog=null,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._init(),WCF.DOMNodeInsertedHandler.addCallback("WCF.User.Profile.ActivityPointList",$.proxy(this._init,this)),this._didInit=!0)},_init:function(){$(".activityPointsDisplay").removeClass("activityPointsDisplay").click($.proxy(this._click,this))},_click:function(t){t.preventDefault();var e=$(t.currentTarget).data("userID");void 0===this._cache[e]?(this._proxy.setOption("data",{actionName:"getDetailedActivityPointList",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[e]}),this._proxy.sendRequest()):this._show(e)},_show:function(t){null===this._dialog?(this._dialog=$("<div>"+this._cache[t]+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.user.activityPoint")})):(this._dialog.html(this._cache[t]),this._dialog.wcfDialog("open"))},_success:function(t,e,i){this._cache[t.returnValues.userID]=t.returnValues.template,this._show(t.returnValues.userID)}},WCF.User.Profile.TabMenu=Class.extend({_hasContent:{},_profileContent:null,_proxy:null,_userID:0,init:function(t){this._profileContent=$("#profileContent"),this._userID=t;var e=this._profileContent.data("active"),i=!1;this._profileContent.find("div.tabMenuContent").each($.proxy(function(t,s){var a=$(s).wcfIdentify();e===a?this._hasContent[a]=!0:(this._hasContent[a]=!1,i=!0)},this)),i&&(this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._profileContent.on("wcftabsbeforeactivate",$.proxy(this._loadContent,this)),this._profileContent.find("> nav.tabMenu > ul > li").each($.proxy(function(t,e){var i=$(e);if(i.hasClass("ui-state-active"))return t&&this._loadContent(null,{newPanel:$("#"+i.attr("aria-controls"))}),!1},this))),$('.userProfileUser .contentDescription a[href$="#likes"]').click(function(t){t.preventDefault(),require(["Ui/TabMenu"],function(t){t.getTabMenu("profileContent").select("likes")})}.bind(this))},_loadContent:function(t,e){var i=$(e.newPanel),s=i.attr("id");this._hasContent[s]||(this._proxy.setOption("data",{actionName:"getContent",className:"wcf\\data\\user\\profile\\menu\\item\\UserProfileMenuItemAction",parameters:{data:{containerID:s,menuItem:i.data("menuItem"),userID:this._userID}}}),this._proxy.sendRequest())},_success:function(t,e,i){var s=t.returnValues.containerID;this._hasContent[s]=!0,require(["Dom/ChangeListener","Dom/Util"],function(e,i){i.insertHtml(t.returnValues.template,elById(s),"append"),e.trigger()})}}),WCF.User.Profile.Editor=Class.extend({_actionName:"",_active:!1,_buttons:{},_cachedTemplate:"",_proxy:null,_tab:null,_userID:0,init:function(t,e){this._actionName="",this._active=!1,this._cachedTemplate="",this._tab=$("#about"),this._userID=t,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initButtons(),e&&this._beginEdit()},_initButtons:function(){this._buttons={beginEdit:$(".jsButtonEditProfile:eq(0)").click(this._beginEdit.bind(this))}},_beginEdit:function(t){t&&t.preventDefault(),this._active||(this._active=!0,this._actionName="beginEdit",this._buttons.beginEdit.parent().addClass("active"),$("#profileContent").wcfTabs("select","about"),this._proxy.setOption("data",{actionName:"beginEdit",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[this._userID]}),this._proxy.sendRequest())},_save:function(){var t=null;if(elBySelAll(".redactor-layer",this._tab[0],function(e){var i={api:{throwError:elInnerError},valid:!0};WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","validate_"+elData(e,"element-id"),i),i.valid||null!==t||(t=e.parentNode)}),t)return void t.scrollIntoView({behavior:"smooth"});this._actionName="save";var e=/values\[([a-zA-Z0-9._-]+)\]/,i={};this._tab.find("input, textarea, select").each(function(t,s){var a=$(s),n=null;switch(a.getTagName()){case"input":var o=a.attr("type");if(("radio"===o||"checkbox"===o)&&!a.prop("checked"))return;break;case"textarea":a.data("redactor")&&(n=a.redactor("code.get"))}var r=a.attr("name");if(e.test(r)){var l=RegExp.$1;null===n&&(n=a.val()),"checkbox"===a.attr("type")&&/\[\]$/.test(r)?(Array.isArray(i[l])||(i[l]=[]),i[l].push(n)):i[l]=n}}),this._proxy.setOption("data",{actionName:"save",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[this._userID],parameters:{values:i}}),this._proxy.sendRequest()},_restore:function(){this._actionName="restore",this._active=!1,this._buttons.beginEdit.parent().removeClass("active"),this._destroyEditor(),this._tab.html(this._cachedTemplate).children().css({height:"auto"})},_success:function(t,e,i){switch(this._actionName){case"beginEdit":this._prepareEdit(t);break;case"save":t.returnValues.success?(this._cachedTemplate=t.returnValues.template,this._restore()):this._prepareEdit(t,!0)}},_prepareEdit:function(t,e){this._destroyEditor();var i=this;this._tab.html(function(s,a){return!0!==e&&(i._cachedTemplate=a),t.returnValues.template}),this._tab.find("input[type=text]").attr("autocomplete","off"),this._tab.find(".formSubmit > button[data-type=save]").click($.proxy(this._save,this)),this._tab.find(".formSubmit > button[data-type=restore]").click($.proxy(this._restore,this)),this._tab.find("input").keyup(function(t){if(t.which===$.ui.keyCode.ENTER)return i._save(),t.preventDefault(),!1})},_destroyEditor:function(){this._tab.find("textarea").each(function(t,e){var i=$(e);i.data("redactor")&&i.redactor("core.destroy")})}}),WCF.User.Registration={},WCF.User.Registration.Validation=Class.extend({_actionName:"",_className:"",_confirmElement:null,_element:null,_errorMessages:{},_options:{},_proxy:null,init:function(t,e,i){this._element=t,this._element.blur($.proxy(this._blur,this)),this._confirmElement=e||null,null!==this._confirmElement&&this._confirmElement.blur($.proxy(this._blurConfirm,this)),i=i||{},this._setOptions(i),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this),showLoadingOverlay:!1}),this._setErrorMessages()},_setOptions:function(t){},_setErrorMessages:function(){this._errorMessages={ajaxError:"",notEqual:""}},_blur:function(t){var e=this._element.val();if(!e)return this._showError(this._element,WCF.Language.get("wcf.global.form.error.empty"));if(null!==this._confirmElement){var i=this._confirmElement.val();if(""!=i&&e!=i)return this._showError(this._confirmElement,this._errorMessages.notEqual)}this._validateOptions()&&(this._proxy.setOption("data",{actionName:this._actionName,className:this._className,parameters:this._getParameters()}),this._proxy.sendRequest())},_getParameters:function(){return{}},_validateOptions:function(){return!0},_blurConfirm:function(t){if(!this._confirmElement.val())return this._showError(this._confirmElement,WCF.Language.get("wcf.global.form.error.empty"));this._blur(t)},_success:function(t,e,i){t.returnValues.isValid?(this._showSuccess(this._element),null!==this._confirmElement&&this._confirmElement.val()&&this._showSuccess(this._confirmElement)):this._showError(this._element,WCF.Language.get(this._errorMessages.ajaxError+t.returnValues.error))},_showError:function(t,e){t.parent().parent().addClass("formError").removeClass("formSuccess");var i=t.parent().find("small.innerError");i.length||(i=$("<small />").addClass("innerError").insertAfter(t)),i.text(e)},_showSuccess:function(t){t.parent().parent().addClass("formSuccess").removeClass("formError"),t.next("small.innerError").remove()}}),WCF.User.Registration.Validation.Username=WCF.User.Registration.Validation.extend({_actionName:"validateUsername",_className:"wcf\\data\\user\\UserRegistrationAction",_setOptions:function(t){this._options=$.extend(!0,{minlength:3,maxlength:25},t)},_setErrorMessages:function(){this._errorMessages={ajaxError:"wcf.user.username.error."}},_validateOptions:function(){var t=this._element.val();return!(t.length<this._options.minlength||t.length>this._options.maxlength)||(this._showError(this._element,WCF.Language.get("wcf.user.username.error.invalid")),!1)},_getParameters:function(){return{username:this._element.val()}}}),WCF.User.Registration.Validation.EmailAddress=WCF.User.Registration.Validation.extend({_actionName:"validateEmailAddress",_className:"wcf\\data\\user\\UserRegistrationAction",_getParameters:function(){return{email:this._element.val()}},_setErrorMessages:function(){this._errorMessages={ajaxError:"wcf.user.email.error.",notEqual:WCF.Language.get("wcf.user.confirmEmail.error.notEqual")}}}),WCF.User.Registration.Validation.Password=WCF.User.Registration.Validation.extend({_actionName:"validatePassword",_className:"wcf\\data\\user\\UserRegistrationAction",_getParameters:function(){return{password:this._element.val()}},_setErrorMessages:function(){this._errorMessages={ajaxError:"wcf.user.password.error.",notEqual:WCF.Language.get("wcf.user.confirmPassword.error.notEqual")}}}),WCF.User.Registration.LostPassword=Class.extend({_email:null,_username:null,init:function(){this._email=$("#emailInput"),this._username=$("#usernameInput"),this._email.keyup($.proxy(this._checkEmail,this)),this._username.keyup($.proxy(this._checkUsername,this)),$.browser.mozilla&&$.browser.touch&&(this._email.on("input",$.proxy(this._checkEmail,this)),this._username.on("input",$.proxy(this._checkUsername,this))),this._checkEmail(),this._checkUsername()},_checkEmail:function(){""==this._email.val()?(this._username.enable(),this._username.parents("dl:eq(0)").removeClass("disabled")):(this._username.disable(),this._username.parents("dl:eq(0)").addClass("disabled"),this._username.val(""))},_checkUsername:function(){""==this._username.val()?(this._email.enable(),this._email.parents("dl:eq(0)").removeClass("disabled")):(this._email.disable(),this._email.parents("dl:eq(0)").addClass("disabled"),this._email.val(""))}}),WCF.Notification={},WCF.Notification.List=Class.extend({_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".contentHeaderNavigation .jsMarkAllAsConfirmed").click(function(){WCF.System.Confirmation.show(WCF.Language.get("wcf.user.notification.markAllAsConfirmed.confirmMessage"),function(t){"confirm"===t&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"markAllAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction"},success:function(){window.location.reload()}})})}),this._convertList()},_convertList:function(){$(".userNotificationItemList > .notificationItem").each(function(t,e){var i=$(e);if(!i.data("isRead")){i.find("a:not(.userLink)").prop("href",i.data("link"));$('<a href="#" class="icon icon24 fa-check notificationItemMarkAsConfirmed jsTooltip" title="'+WCF.Language.get("wcf.user.notification.markAsConfirmed")+'" />').appendTo(i).click($.proxy(this._markAsConfirmed,this))}i.find("a:not(.notificationItemMarkAsConfirmed)").length||i.find(".details > p:eq(0)").html(function(t,e){return'<a href="'+i.data("link")+'">'+e+"</a>"})}.bind(this)),WCF.DOMNodeInsertedHandler.execute()},_markAsConfirmed:function(t){t.preventDefault();var e=$(t.currentTarget).parents(".notificationItem:eq(0)").data("objectID");return this._proxy.setOption("data",{actionName:"markAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction",objectIDs:[e]}),this._proxy.sendRequest(),!1},_success:function(t,e,i){var s=$(".userNotificationItemList > .notificationItem[data-object-id="+t.returnValues.markAsRead+"]");s.data("isRead",!0),s.find(".newContentBadge").remove(),s.find(".notificationItemMarkAsConfirmed").remove(),s.removeClass("notificationUnconfirmed")}}),WCF.User.SignaturePreview=WCF.Message.Preview.extend({_handleResponse:function(t){var e=$("#previewContainer");e.length||(e=$('<section class="section" id="previewContainer"><h2 class="sectionTitle">'+WCF.Language.get("wcf.global.preview")+'</h2><div class="htmlContent"></div></section>').insertBefore($("#signatureContainer")).wcfFadeIn()),e.children("div").first().html(t.returnValues.message)}}),WCF.User.RecentActivityLoader=Class.extend({_container:null,_filteredByFollowedUsers:!1,_loadButton:null,_proxy:null,_userID:0,init:function(t,e){if(this._container=$("#recentActivities"),this._filteredByFollowedUsers=!0===e,this._userID=t,null!==this._userID&&!this._userID)return void console.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._container.children("li").length?(this._loadButton=$('<li class="showMore"><button class="small">'+WCF.Language.get("wcf.user.recentActivity.more")+"</button></li>").appendTo(this._container),this._loadButton=this._loadButton.children("button").click($.proxy(this._click,this))):$('<li class="showMore"><small>'+WCF.Language.get("wcf.user.recentActivity.noMoreEntries")+"</small></li>").appendTo(this._container),WCF.User.userID&&$(".jsRecentActivitySwitchContext .button").click($.proxy(this._switchContext,this))},_click:function(){this._loadButton.enable();var t={lastEventID:this._container.data("lastEventID"),lastEventTime:this._container.data("lastEventTime")};this._userID?t.userID=this._userID:this._filteredByFollowedUsers&&(t.filteredByFollowedUsers=1),this._proxy.setOption("data",{actionName:"load",className:"wcf\\data\\user\\activity\\event\\UserActivityEventAction",parameters:t}),this._proxy.sendRequest()},_switchContext:function(t){t.preventDefault(),$(t.currentTarget).hasClass("active")||new WCF.Action.Proxy({autoSend:!0,data:{actionName:"switchContext",className:"wcf\\data\\user\\activity\\event\\UserActivityEventAction"},success:function(){window.location.hash="#dashboardBoxRecentActivity",window.location.reload()}})},_success:function(t,e,i){t.returnValues.template?($(t.returnValues.template).insertBefore(this._loadButton.parent()),this._container.data("lastEventTime",t.returnValues.lastEventTime),this._container.data("lastEventID",t.returnValues.lastEventID),this._loadButton.enable()):($("<small>"+WCF.Language.get("wcf.user.recentActivity.noMoreEntries")+"</small>").appendTo(this._loadButton.parent()),this._loadButton.remove())}}),WCF.User.LikeLoader=Class.extend({_container:null,_likeType:"received",_likeValue:1,_loadButton:null,_noMoreEntries:null,_proxy:null,_userID:0,init:function(t){if(this._container=$("#likeList"),this._userID=t,!this._userID)return void console.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)});var e=$('<li class="likeListMore showMore"><button class="small">'+WCF.Language.get("wcf.like.likes.more")+"</button><small>"+WCF.Language.get("wcf.like.likes.noMoreEntries")+"</small></li>").appendTo(this._container);this._loadButton=e.children("button").click($.proxy(this._click,this)),this._noMoreEntries=e.children("small").hide(),2==this._container.find("> li").length&&(this._loadButton.hide(),this._noMoreEntries.show()),$("#likeType .button").click($.proxy(this._clickLikeType,this)),$("#likeValue .button").click($.proxy(this._clickLikeValue,this))},_clickLikeType:function(t){var e=$(t.currentTarget);this._likeType!=e.data("likeType")&&(this._likeType=e.data("likeType"),$("#likeType .button").removeClass("active"),e.addClass("active"),this._reload())},_clickLikeValue:function(t){var e=$(t.currentTarget);this._likeValue!=e.data("likeValue")&&(this._likeValue=e.data("likeValue"),$("#likeValue .button").removeClass("active"),e.addClass("active"),$("#likeType > li:first-child > .button").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likesReceived")),$("#likeType > li:last-child > .button").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likesGiven")),this._container.find("> li.likeListMore button").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likes.more")),this._container.find("> li.likeListMore small").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likes.noMoreEntries")),this._reload())},_reload:function(){this._container.find("> li:not(:first-child):not(:last-child)").remove(),this._container.data("lastLikeTime",0),this._click()},_click:function(){this._loadButton.enable();var t={lastLikeTime:this._container.data("lastLikeTime"),userID:this._userID,likeType:this._likeType,likeValue:this._likeValue};this._proxy.setOption("data",{actionName:"load",className:"wcf\\data\\like\\LikeAction",parameters:t}),this._proxy.sendRequest()},_success:function(t,e,i){t.returnValues.template?($(t.returnValues.template).insertBefore(this._loadButton.parent()),this._container.data("lastLikeTime",t.returnValues.lastLikeTime),this._noMoreEntries.hide(),this._loadButton.show().enable()):(this._noMoreEntries.show(),this._loadButton.hide())}}),WCF.User.ProfilePreview=WCF.Popover.extend({_proxy:null,_userProfiles:{},init:function(){this._super(".userLink"),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1}),WCF.System.ObjectStore.add("WCF.User.ProfilePreview",this)},_loadContent:function(){var t=$("#"+this._activeElementID),e=t.data("userID");if(this._userProfiles[e])this._insertContent(this._activeElementID,this._userProfiles[e],!0);else{this._proxy.setOption("data",{actionName:"getUserProfile",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[e]});var i=this._activeElementID,s=this;this._proxy.setOption("success",function(t,a,n){s._userProfiles[e]=t.returnValues.template,s._insertContent(i,t.returnValues.template,!0)}),this._proxy.setOption("failure",function(t,a,n,o){return s._userProfiles[e]=t.message,s._insertContent(i,t.message,!0),!1}),this._proxy.sendRequest()}},purge:function(t){delete this._userProfiles[t],this._data={}}}),WCF.User.Action={},WCF.User.Action.Follow=Class.extend({_containerList:null,_followButtonSelector:".jsFollowButton",_userID:0,init:function(t,e){t.length&&(this._containerList=t,e&&(this._followButtonSelector=e),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._containerList.each($.proxy(function(t,e){$(e).find(this._followButtonSelector).click($.proxy(this._click,this))},this)))},_click:function(t){t.preventDefault();var e=$(t.target);e.is("a")||(e=e.closest("a")),this._userID=e.data("objectID"),this._proxy.setOption("data",{actionName:e.data("following")?"unfollow":"follow",className:"wcf\\data\\user\\follow\\UserFollowAction",parameters:{data:{userID:this._userID}}}),this._proxy.sendRequest()},_success:function(t,e,i){this._containerList.each($.proxy(function(e,i){var s=$(i).find(this._followButtonSelector).get(0);if(s&&$(s).data("objectID")==this._userID)return s=$(s),t.returnValues.following?(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.unfollow")).children(".icon").removeClass("fa-plus").addClass("fa-minus"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.unfollow"))):(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.follow")).children(".icon").removeClass("fa-minus").addClass("fa-plus"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.follow"))),s.data("following",t.returnValues.following),!1},this)),(new WCF.System.Notification).show()}}),WCF.User.Action.Ignore=Class.extend({_containerList:null,_ignoreButtonSelector:".jsIgnoreButton",_userID:0,init:function(t,e){t.length&&(this._containerList=t,e&&(this._ignoreButtonSelector=e),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._containerList.each($.proxy(function(t,e){$(e).find(this._ignoreButtonSelector).click($.proxy(this._click,this))},this)))},_click:function(t){t.preventDefault();var e=$(t.target);e.is("a")||(e=e.closest("a")),this._userID=e.data("objectID"),this._proxy.setOption("data",{actionName:e.data("ignored")?"unignore":"ignore",className:"wcf\\data\\user\\ignore\\UserIgnoreAction",parameters:{data:{userID:this._userID}}}),this._proxy.sendRequest()},_success:function(t,e,i){this._containerList.each($.proxy(function(e,i){var s=$(i).find(this._ignoreButtonSelector).get(0);if(s&&$(s).data("objectID")==this._userID)return s=$(s),t.returnValues.isIgnoredUser?(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.unignore")).children(".icon").removeClass("fa-ban").addClass("fa-circle-o"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.unignore"))):(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.ignore")).children(".icon").removeClass("fa-circle-o").addClass("fa-ban"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.ignore"))),s.data("ignored",t.returnValues.isIgnoredUser),!1},this)),(new WCF.System.Notification).show();var s=this;WCF.System.ObjectStore.invoke("WCF.User.ProfilePreview",function(t){t.purge(s._userID)})}}),WCF.User.Avatar={},WCF.User.Avatar.Upload=WCF.Upload.extend({_userID:0,init:function(t){this._super($("#avatarUpload > dd > div"),void 0,"wcf\\data\\user\\avatar\\UserAvatarAction"),this._userID=t||0,$("#avatarForm input[type=radio]").change(function(){"custom"==$(this).val()?$("#avatarUpload > dd > div").show():$("#avatarUpload > dd > div").hide()}),$("#avatarForm input[type=radio][value=custom]:checked").length||$("#avatarUpload > dd > div").hide()},_initFile:function(t){return $("#avatarUpload > dt > img")},_success:function(t,e){if(e.returnValues.url){this._updateImage(e.returnValues.url),$("#avatarUpload > dd > .innerError").remove();new WCF.System.Notification(WCF.Language.get("wcf.user.avatar.upload.success")).show()}else e.returnValues.errorType&&this._getInnerErrorElement().text(WCF.Language.get("wcf.user.avatar.upload.error."+e.returnValues.errorType))},_updateImage:function(t){$("#avatarUpload > dt > img").remove();var e=$('<img src="'+t+'" class="userAvatarImage" alt="" />').css({height:"auto","max-height":"96px","max-width":"96px",width:"auto"});$("#avatarUpload > dt").prepend(e),WCF.DOMNodeInsertedHandler.execute()},_getInnerErrorElement:function(){var t=$("#avatarUpload > dd > .innerError");return t.length||(t=$('<small class="innerError"></span>'),$("#avatarUpload > dd").append(t)),t},_getParameters:function(){return{userID:this._userID}}}),WCF.User.List=Class.extend({_additionalParameters:{},_cache:{},_className:"",_dialog:null,_dialogTitle:"",_pageCount:0,_pageNo:1,_proxy:null,init:function(t,e,i){this._additionalParameters=i||{},this._cache={},this._className=t,this._dialog=null,this._dialogTitle=e,this._pageCount=0,this._pageNo=1,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)})},open:function(){this._pageNo=1,this._showPage()},_showPage:function(t,e){if(e&&e.activePage&&(this._pageNo=e.activePage),0!=this._pageCount&&(this._pageNo<1||this._pageNo>this._pageCount))return void console.debug("[WCF.User.List] Cannot access page "+this._pageNo+" of "+this._pageCount);if(this._cache[this._pageNo]){var i=!1;null===this._dialog&&(this._dialog=$("#userList"+this._className.hashCode()),0===this._dialog.length&&(this._dialog=$('<div id="userList'+this._className.hashCode()+'" />').hide().appendTo(document.body),i=!0)),this._dialog.empty(),this._dialog.html(this._cache[this._pageNo]),this._pageCount>1?this._dialog.find(".jsPagination").wcfPages({activePage:this._pageNo,maxPage:this._pageCount}).on("wcfpagesswitched",$.proxy(this._showPage,this)):this._dialog.find(".jsPagination").hide(),i?this._dialog.wcfDialog({title:this._dialogTitle}):(this._dialog.wcfDialog("option","title",this._dialogTitle),this._dialog.wcfDialog("open").wcfDialog("render")),WCF.DOMNodeInsertedHandler.execute()}else this._additionalParameters.pageNo=this._pageNo,this._proxy.setOption("data",{actionName:"getGroupedUserList",className:this._className,interfaceName:"wcf\\data\\IGroupedUserListAction",parameters:this._additionalParameters}),
-this._proxy.sendRequest()},_success:function(t,e,i){t.returnValues.pageCount&&(this._pageCount=t.returnValues.pageCount),this._cache[this._pageNo]=t.returnValues.template,this._showPage()}}),WCF.User.ObjectWatch={},WCF.User.ObjectWatch.Subscribe=Class.extend({_buttonSelector:".jsSubscribeButton",_buttons:{},_dialog:null,_notification:null,_reloadOnUnsubscribe:!1,init:function(t){this._buttons={},this._notification=null,this._reloadOnUnsubscribe=!0===t,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(this._buttonSelector).each($.proxy(function(t,e){var i=$(e);i.addClass("pointer");var s=i.data("objectType"),a=i.data("objectID");void 0===this._buttons[s]&&(this._buttons[s]={}),this._buttons[s][a]=i.click($.proxy(this._click,this))},this)),WCF.System.Event.addListener("com.woltlab.wcf.objectWatch","update",$.proxy(this._updateSubscriptionStatus,this))},_click:function(t){t.preventDefault();var e=$(t.currentTarget);this._proxy.setOption("data",{actionName:"manageSubscription",className:"wcf\\data\\user\\object\\watch\\UserObjectWatchAction",parameters:{objectID:e.data("objectID"),objectType:e.data("objectType")}}),this._proxy.sendRequest()},_success:function(t,e,i){if("manageSubscription"===t.actionName){null===this._dialog?(this._dialog=$("<div>"+t.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.user.objectWatch.manageSubscription")})):(this._dialog.html(t.returnValues.template),this._dialog.wcfDialog("open")),this._dialog.find(".formSubmit > .jsButtonSave").data("objectID",t.returnValues.objectID).data("objectType",t.returnValues.objectType).click($.proxy(this._save,this));var s=this._dialog.find("input[name=enableNotification]").disable();this._dialog.find("input[name=subscribe]").change(function(t){1==$(t.currentTarget).val()?s.enable():s.disable()});var a=this._dialog.find("input[name=subscribe]:checked");a.length&&1==a.val()&&s.enable()}else"saveSubscription"===t.actionName&&this._dialog.is(":visible")&&(this._dialog.wcfDialog("close"),this._updateSubscriptionStatus({isSubscribed:t.returnValues.subscribe,objectID:t.returnValues.objectID}),null===this._notification&&(this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success.edit"))),this._notification.show())},_save:function(t){var e=this._buttons[$(t.currentTarget).data("objectType")][$(t.currentTarget).data("objectID")],i=this._dialog.find("input[name=subscribe]:checked").val(),s=this._dialog.find("input[name=enableNotification]").is(":checked")?1:0;this._proxy.setOption("data",{actionName:"saveSubscription",className:"wcf\\data\\user\\object\\watch\\UserObjectWatchAction",parameters:{enableNotification:s,objectID:e.data("objectID"),objectType:e.data("objectType"),subscribe:i}}),this._proxy.sendRequest()},_updateSubscriptionStatus:function(t){var e=$(this._buttonSelector+"[data-object-id="+t.objectID+"]"),i=e.children(".icon");if(t.isSubscribed)i.removeClass("fa-bookmark-o").addClass("fa-bookmark"),e.data("isSubscribed",!0);else if(e.data("removeOnUnsubscribe")?e.parent().remove():(i.removeClass("fa-bookmark").addClass("fa-bookmark-o"),e.data("isSubscribed",!1)),this._reloadOnUnsubscribe)return void window.location.reload();WCF.System.Event.fireEvent("com.woltlab.wcf.objectWatch","updatedSubscription",t)}}); })(this);
+(function (window, undefined) { "use strict";WCF.User.Login=Class.extend({_loginSubmitButton:null,_password:null,_passwordContainer:null,_useCookies:null,_useCookiesContainer:null,init:function(t){this._loginSubmitButton=$("#loginSubmitButton"),this._password=$("#password"),this._passwordContainer=this._password.parents("dl"),this._useCookies=$("#useCookies"),this._useCookiesContainer=this._useCookies.parents("dl"),$("#loginForm").find("input[name=action]").change($.proxy(this._change,this)),t&&WCF.User.QuickLogin.init()},_change:function(t){"register"===$(t.currentTarget).val()?this._setState(!1,WCF.Language.get("wcf.user.button.register")):this._setState(!0,WCF.Language.get("wcf.user.button.login"))},_setState:function(t,e){t?(this._password.enable(),this._passwordContainer.removeClass("disabled"),this._useCookies.enable(),this._useCookiesContainer.removeClass("disabled")):(this._password.disable(),this._passwordContainer.addClass("disabled"),this._useCookies.disable(),this._useCookiesContainer.addClass("disabled")),this._loginSubmitButton.val(e)}}),WCF.User.Panel={},WCF.User.Panel.Abstract=Class.extend({_badge:null,_dropdown:null,_identifier:"",_loadData:!0,_markAllAsReadLink:null,_options:{},_proxy:null,_triggerElement:null,_button:null,_callbackFocus:null,_wasInsideDropdown:!1,init:function(t,e,i){if(this._dropdown=null,this._loadData=!0,this._identifier=e,this._triggerElement=t,this._options=i,this._callbackFocus=null,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._triggerElement.click($.proxy(this.toggle,this)),this._button=elBySel("a",this._triggerElement[0]),this._button&&(elAttr(this._button,"role","button"),elAttr(this._button,"tabindex","0"),elAttr(this._button,"aria-haspopup",!0),elAttr(this._button,"aria-expanded",!1)),this._options.showAllLink&&this._triggerElement.dblclick($.proxy(this._dblClick,this)),!0===this._options.staticDropdown)this._loadData=!1;else{var s=this._triggerElement.find("span.badge");s.length&&(this._badge=s)}},toggle:function(t){if(t instanceof Event&&t.preventDefault(),null===this._dropdown&&(this._dropdown=this._initDropdown()),this._dropdown.toggle()){if(!this._loadData&&null!==this._badge){(parseInt(this._badge.text())||0)&&!this._dropdown.getItemList().children(".interactiveDropdownItemOutstanding").length&&(this._loadData=!0)}this._loadData&&(this._loadData=!1,this._load()),elAttr(this._button,"aria-expanded",!0),null===this._callbackFocus&&(this._callbackFocus=this._maintainFocus.bind(this)),document.body.addEventListener("focus",this._callbackFocus,{capture:!0})}else elAttr(this._button,"aria-expanded",!1),document.body.removeEventListener("focus",this._callbackFocus);return!1},_dblClick:function(t){return t.preventDefault(),window.location=this._options.showAllLink,!1},_initDropdown:function(){var t=WCF.Dropdown.Interactive.Handler.create(this._triggerElement,this._identifier,this._options);return $('<li class="loading"><span class="icon icon24 fa-spinner" /> <span>'+WCF.Language.get("wcf.global.loading")+"</span></li>").appendTo(t.getItemList()),t},_load:function(){},_success:function(t){if(void 0!==t.returnValues.template){var e=this._dropdown.getItemList().empty();if($(t.returnValues.template).appendTo(e),e.children().length||$('<li class="noItems">'+this._options.noItems+"</li>").appendTo(e),this._options.enableMarkAsRead){var i=this._dropdown.getItemList().children(".interactiveDropdownItemOutstanding");if(null===this._markAllAsReadLink&&i.length){(this._markAllAsReadLink=$('<li class="interactiveDropdownItemMarkAllAsRead"><a href="#" title="'+WCF.Language.get("wcf.user.panel.markAllAsRead")+'" class="jsTooltip"><span class="icon icon24 fa-check" /></a></li>').appendTo(this._dropdown.getLinkList())).click(function(t){return this._dropdown.close(),this._markAllAsRead(),!1}.bind(this))}i.each(function(t,e){var i=$(e).addClass("interactiveDropdownItemOutstandingIcon"),s=i.data("objectID");$('<div class="interactiveDropdownItemMarkAsRead"><a href="#" title="'+WCF.Language.get("wcf.user.panel.markAsRead")+'" class="jsTooltip"><span class="icon icon16 fa-check" /></a></div>').appendTo(i).click(function(t){return this._markAsRead(t,s),!1}.bind(this))}.bind(this))}this._dropdown.getItemList().children().each(function(t,e){var i=$(e),s=i.data("link");s&&($.browser.msie?i.click(function(t){if("A"!==t.target.tagName)return window.location=s,!1}):(i.addClass("interactiveDropdownItemShadow"),$('<a href="'+s+'" class="interactiveDropdownItemShadowLink" />').appendTo(i)),i.data("linkReplaceAll")&&i.find("> .box48 a:not(.userLink)").prop("href",s))}),this._dropdown.rebuildScrollbar()}if(void 0!==t.returnValues.totalCount&&this.updateBadge(t.returnValues.totalCount),this._options.enableMarkAsRead)if(t.returnValues.markAsRead){var s=this._dropdown.getItemList().children("li[data-object-id="+t.returnValues.markAsRead+"]");s.length&&(s.removeClass("interactiveDropdownItemOutstanding").data("isRead",!0),s.children(".interactiveDropdownItemMarkAsRead").remove())}else t.returnValues.markAllAsRead&&(this.resetItems(),this.updateBadge(0))},_markAsRead:function(t,e){},_markAllAsRead:function(){},updateBadge:function(t){t=parseInt(t)||0,t?(null===this._badge&&(this._badge=$('<span class="badge badgeUpdate" />').appendTo(this._triggerElement.children("a")),this._badge.before(" ")),this._badge.text(t)):null!==this._badge&&(this._badge.remove(),this._badge=null),this._options.enableMarkAsRead&&(t||null===this._markAllAsReadLink||(this._markAllAsReadLink.remove(),this._markAllAsReadLink=null)),WCF.System.Event.fireEvent("com.woltlab.wcf.userMenu","updateBadge",{count:t,identifier:this._identifier})},resetItems:function(){null!==this._dropdown&&(this._dropdown.resetItems(),this._loadData=!0)},_maintainFocus:function(t){var e=this._dropdown.getContainer()[0];e.contains(t.target)?this._wasInsideDropdown=!0:this._wasInsideDropdown?(this._button.focus(),this._wasInsideDropdown=!1):elBySel("a",e).focus()}}),WCF.User.Panel.Notification=WCF.User.Panel.Abstract.extend({_favico:null,init:function(t){t.enableMarkAsRead=!0,this._super($("#userNotifications"),"userNotifications",t);try{if(this._favico=new Favico({animation:"none",type:"circle"}),null!==this._badge){var e=parseInt(this._badge.text())||0;this._favico.badge(e)}}catch(t){console.debug("[WCF.User.Panel.Notification] Failed to initialized Favico: "+t.message)}WCF.System.PushNotification.addCallback("userNotificationCount",$.proxy(this.updateUserNotificationCount,this)),require(["EventHandler"],function(t){t.add("com.woltlab.wcf.UserMenuMobile","more",function(t){"com.woltlab.wcf.notifications"===t.identifier&&this.toggle()}.bind(this))}.bind(this))},_initDropdown:function(){var t=this._super();return $('<li><a href="'+this._options.settingsLink+'" title="'+WCF.Language.get("wcf.user.panel.settings")+'" class="jsTooltip"><span class="icon icon24 fa-cog" /></a></li>').appendTo(t.getLinkList()),t},_load:function(){this._proxy.setOption("data",{actionName:"getOutstandingNotifications",className:"wcf\\data\\user\\notification\\UserNotificationAction"}),this._proxy.sendRequest()},_markAsRead:function(t,e){this._proxy.setOption("data",{actionName:"markAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction",objectIDs:[e]}),this._proxy.sendRequest()},_markAllAsRead:function(t){this._proxy.setOption("data",{actionName:"markAllAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction"}),this._proxy.sendRequest()},resetItems:function(){this._super(),this._markAllAsReadLink&&(this._markAllAsReadLink.remove(),this._markAllAsReadLink=null)},updateBadge:function(t){t=parseInt(t)||0,$("#userNotifications").attr("data-count",t),null!==this._favico&&this._favico.badge(t),this._super(t)},updateUserNotificationCount:function(t){null!==this._dropdown&&this._dropdown.resetItems(),this.updateBadge(t)}}),WCF.User.Panel.UserMenu=WCF.User.Panel.Abstract.extend({init:function(){this._super($("#userMenu"),"userMenu",{pointerOffset:"13px",staticDropdown:!0})}}),WCF.User.QuickLogin={init:function(){require(["EventHandler","Ui/Dialog"],function(t,e){var i=elById("loginForm"),s=elBySel(".loginFormLogin",i);s&&!s.nextElementSibling&&i.classList.add("loginFormLoginOnly");for(var a=elBySel(".loginFormRegister",i),n=function(t){if(t instanceof Event&&(t.preventDefault(),t.stopPropagation()),i.style.removeProperty("display"),e.openStatic("loginForm",null,{title:WCF.Language.get("wcf.user.login")}),null!==s&&null!==a){var n=s.offsetTop,o=0;if(i.clientWidth>2*s.clientWidth)for(;n<a.offsetTop-50;)o+=100,s.style.setProperty("margin-bottom",o+"px","")}},o=document.getElementsByClassName("loginLink"),r=0,l=o.length;r<l;r++)o[r].addEventListener(WCF_CLICK_EVENT,n);var c=i.querySelector("#loginForm input[name=url]");null===c||c.value.match(/^https?:\/\//)||c.setAttribute("value",window.location.protocol+"//"+window.location.host+c.getAttribute("value")),t.add("com.woltlab.wcf.UserMenuMobile","more",function(t){"com.woltlab.wcf.login"===t.identifier&&(t.handler.close(!0),n())})})}},WCF.User.Profile={},WCF.User.Profile.ActivityPointList={_cache:{},_dialog:null,_didInit:!1,_proxy:null,init:function(){this._didInit||(this._cache={},this._dialog=null,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._init(),WCF.DOMNodeInsertedHandler.addCallback("WCF.User.Profile.ActivityPointList",$.proxy(this._init,this)),this._didInit=!0)},_init:function(){$(".activityPointsDisplay").removeClass("activityPointsDisplay").click($.proxy(this._click,this))},_click:function(t){t.preventDefault();var e=$(t.currentTarget).data("userID");void 0===this._cache[e]?(this._proxy.setOption("data",{actionName:"getDetailedActivityPointList",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[e]}),this._proxy.sendRequest()):this._show(e)},_show:function(t){null===this._dialog?(this._dialog=$("<div>"+this._cache[t]+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.user.activityPoint")})):(this._dialog.html(this._cache[t]),this._dialog.wcfDialog("open"))},_success:function(t,e,i){this._cache[t.returnValues.userID]=t.returnValues.template,this._show(t.returnValues.userID)}},WCF.User.Profile.TabMenu=Class.extend({_hasContent:{},_profileContent:null,_proxy:null,_userID:0,init:function(t){this._profileContent=$("#profileContent"),this._userID=t;var e=this._profileContent.data("active"),i=!1;this._profileContent.find("div.tabMenuContent").each($.proxy(function(t,s){var a=$(s).wcfIdentify();e===a?this._hasContent[a]=!0:(this._hasContent[a]=!1,i=!0)},this)),i&&(this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._profileContent.on("wcftabsbeforeactivate",$.proxy(this._loadContent,this)),this._profileContent.find("> nav.tabMenu > ul > li").each($.proxy(function(t,e){var i=$(e);if(i.hasClass("ui-state-active"))return t&&this._loadContent(null,{newPanel:$("#"+i.attr("aria-controls"))}),!1},this))),$('.userProfileUser .contentDescription a[href$="#likes"]').click(function(t){t.preventDefault(),require(["Ui/TabMenu"],function(t){t.getTabMenu("profileContent").select("likes")})}.bind(this))},_loadContent:function(t,e){var i=$(e.newPanel),s=i.attr("id");this._hasContent[s]||(this._proxy.setOption("data",{actionName:"getContent",className:"wcf\\data\\user\\profile\\menu\\item\\UserProfileMenuItemAction",parameters:{data:{containerID:s,menuItem:i.data("menuItem"),userID:this._userID}}}),this._proxy.sendRequest())},_success:function(t,e,i){var s=t.returnValues.containerID;this._hasContent[s]=!0,require(["Dom/ChangeListener","Dom/Util"],function(e,i){i.insertHtml(t.returnValues.template,elById(s),"append"),e.trigger()})}}),WCF.User.Profile.Editor=Class.extend({_actionName:"",_active:!1,_buttons:{},_cachedTemplate:"",_proxy:null,_tab:null,_userID:0,init:function(t,e){this._actionName="",this._active=!1,this._cachedTemplate="",this._tab=$("#about"),this._userID=t,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initButtons(),e&&this._beginEdit()},_initButtons:function(){this._buttons={beginEdit:$(".jsButtonEditProfile:eq(0)").click(this._beginEdit.bind(this))}},_beginEdit:function(t){t&&t.preventDefault(),this._active||(this._active=!0,this._actionName="beginEdit",this._buttons.beginEdit.parent().addClass("active"),$("#profileContent").wcfTabs("select","about"),this._proxy.setOption("data",{actionName:"beginEdit",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[this._userID]}),this._proxy.sendRequest())},_save:function(){var t=null;if(elBySelAll(".redactor-layer",this._tab[0],function(e){var i={api:{throwError:elInnerError},valid:!0};WCF.System.Event.fireEvent("com.woltlab.wcf.redactor2","validate_"+elData(e,"element-id"),i),i.valid||null!==t||(t=e.parentNode)}),t)return void t.scrollIntoView({behavior:"smooth"});this._actionName="save";var e=/values\[([a-zA-Z0-9._-]+)\]/,i={};this._tab.find("input, textarea, select").each(function(t,s){var a=$(s),n=null;switch(a.getTagName()){case"input":var o=a.attr("type");if(("radio"===o||"checkbox"===o)&&!a.prop("checked"))return;break;case"textarea":a.data("redactor")&&(n=a.redactor("code.get"))}var r=a.attr("name");if(e.test(r)){var l=RegExp.$1;null===n&&(n=a.val()),"checkbox"===a.attr("type")&&/\[\]$/.test(r)?(Array.isArray(i[l])||(i[l]=[]),i[l].push(n)):i[l]=n}}),this._proxy.setOption("data",{actionName:"save",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[this._userID],parameters:{values:i}}),this._proxy.sendRequest()},_restore:function(){this._actionName="restore",this._active=!1,this._buttons.beginEdit.parent().removeClass("active"),this._destroyEditor(),this._tab.html(this._cachedTemplate).children().css({height:"auto"})},_success:function(t,e,i){switch(this._actionName){case"beginEdit":this._prepareEdit(t);break;case"save":t.returnValues.success?(this._cachedTemplate=t.returnValues.template,this._restore()):this._prepareEdit(t,!0)}},_prepareEdit:function(t,e){this._destroyEditor();var i=this;this._tab.html(function(s,a){return!0!==e&&(i._cachedTemplate=a),t.returnValues.template}),this._tab.find("input[type=text]").attr("autocomplete","off"),this._tab.find(".formSubmit > button[data-type=save]").click($.proxy(this._save,this)),this._tab.find(".formSubmit > button[data-type=restore]").click($.proxy(this._restore,this)),this._tab.find("input").keyup(function(t){if(t.which===$.ui.keyCode.ENTER)return i._save(),t.preventDefault(),!1})},_destroyEditor:function(){this._tab.find("textarea").each(function(t,e){var i=$(e);i.data("redactor")&&i.redactor("core.destroy")})}}),WCF.User.Registration={},WCF.User.Registration.Validation=Class.extend({_actionName:"",_className:"",_confirmElement:null,_element:null,_errorMessages:{},_options:{},_proxy:null,init:function(t,e,i){this._element=t,this._element.blur($.proxy(this._blur,this)),this._confirmElement=e||null,null!==this._confirmElement&&this._confirmElement.blur($.proxy(this._blurConfirm,this)),i=i||{},this._setOptions(i),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this),showLoadingOverlay:!1}),this._setErrorMessages()},_setOptions:function(t){},_setErrorMessages:function(){this._errorMessages={ajaxError:"",notEqual:""}},_blur:function(t){var e=this._element.val();if(!e)return this._showError(this._element,WCF.Language.get("wcf.global.form.error.empty"));if(null!==this._confirmElement){var i=this._confirmElement.val();if(""!=i&&e!=i)return this._showError(this._confirmElement,this._errorMessages.notEqual)}this._validateOptions()&&(this._proxy.setOption("data",{actionName:this._actionName,className:this._className,parameters:this._getParameters()}),this._proxy.sendRequest())},_getParameters:function(){return{}},_validateOptions:function(){return!0},_blurConfirm:function(t){if(!this._confirmElement.val())return this._showError(this._confirmElement,WCF.Language.get("wcf.global.form.error.empty"));this._blur(t)},_success:function(t,e,i){t.returnValues.isValid?(this._showSuccess(this._element),null!==this._confirmElement&&this._confirmElement.val()&&this._showSuccess(this._confirmElement)):this._showError(this._element,WCF.Language.get(this._errorMessages.ajaxError+t.returnValues.error))},_showError:function(t,e){t.parent().parent().addClass("formError").removeClass("formSuccess");var i=t.parent().find("small.innerError");i.length||(i=$("<small />").addClass("innerError").insertAfter(t)),i.text(e)},_showSuccess:function(t){t.parent().parent().addClass("formSuccess").removeClass("formError"),t.next("small.innerError").remove()}}),WCF.User.Registration.Validation.Username=WCF.User.Registration.Validation.extend({_actionName:"validateUsername",_className:"wcf\\data\\user\\UserRegistrationAction",_setOptions:function(t){this._options=$.extend(!0,{minlength:3,maxlength:25},t)},_setErrorMessages:function(){this._errorMessages={ajaxError:"wcf.user.username.error."}},_validateOptions:function(){var t=this._element.val();return!(t.length<this._options.minlength||t.length>this._options.maxlength)||(this._showError(this._element,WCF.Language.get("wcf.user.username.error.invalid")),!1)},_getParameters:function(){return{username:this._element.val()}}}),WCF.User.Registration.Validation.EmailAddress=WCF.User.Registration.Validation.extend({_actionName:"validateEmailAddress",_className:"wcf\\data\\user\\UserRegistrationAction",_getParameters:function(){return{email:this._element.val()}},_setErrorMessages:function(){this._errorMessages={ajaxError:"wcf.user.email.error.",notEqual:WCF.Language.get("wcf.user.confirmEmail.error.notEqual")}}}),WCF.User.Registration.Validation.Password=WCF.User.Registration.Validation.extend({_actionName:"validatePassword",_className:"wcf\\data\\user\\UserRegistrationAction",_getParameters:function(){return{password:this._element.val()}},_setErrorMessages:function(){this._errorMessages={ajaxError:"wcf.user.password.error.",notEqual:WCF.Language.get("wcf.user.confirmPassword.error.notEqual")}}}),WCF.User.Registration.LostPassword=Class.extend({_email:null,_username:null,init:function(){this._email=$("#emailInput"),this._username=$("#usernameInput"),this._email.keyup($.proxy(this._checkEmail,this)),this._username.keyup($.proxy(this._checkUsername,this)),$.browser.mozilla&&$.browser.touch&&(this._email.on("input",$.proxy(this._checkEmail,this)),this._username.on("input",$.proxy(this._checkUsername,this))),this._checkEmail(),this._checkUsername()},_checkEmail:function(){""==this._email.val()?(this._username.enable(),this._username.parents("dl:eq(0)").removeClass("disabled")):(this._username.disable(),this._username.parents("dl:eq(0)").addClass("disabled"),this._username.val(""))},_checkUsername:function(){""==this._username.val()?(this._email.enable(),this._email.parents("dl:eq(0)").removeClass("disabled")):(this._email.disable(),this._email.parents("dl:eq(0)").addClass("disabled"),this._email.val(""))}}),WCF.Notification={},WCF.Notification.List=Class.extend({_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".contentHeaderNavigation .jsMarkAllAsConfirmed").click(function(){WCF.System.Confirmation.show(WCF.Language.get("wcf.user.notification.markAllAsConfirmed.confirmMessage"),function(t){"confirm"===t&&new WCF.Action.Proxy({autoSend:!0,data:{actionName:"markAllAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction"},success:function(){window.location.reload()}})})}),this._convertList()},_convertList:function(){$(".userNotificationItemList > .notificationItem").each(function(t,e){var i=$(e);if(!i.data("isRead")){i.find("a:not(.userLink)").prop("href",i.data("link"));$('<a href="#" class="icon icon24 fa-check notificationItemMarkAsConfirmed jsTooltip" title="'+WCF.Language.get("wcf.user.notification.markAsConfirmed")+'" />').appendTo(i).click($.proxy(this._markAsConfirmed,this))}i.find("a:not(.notificationItemMarkAsConfirmed)").length||i.find(".details > p:eq(0)").html(function(t,e){return'<a href="'+i.data("link")+'">'+e+"</a>"})}.bind(this)),WCF.DOMNodeInsertedHandler.execute()},_markAsConfirmed:function(t){t.preventDefault();var e=$(t.currentTarget).parents(".notificationItem:eq(0)").data("objectID");return this._proxy.setOption("data",{actionName:"markAsConfirmed",className:"wcf\\data\\user\\notification\\UserNotificationAction",objectIDs:[e]}),this._proxy.sendRequest(),!1},_success:function(t,e,i){var s=$(".userNotificationItemList > .notificationItem[data-object-id="+t.returnValues.markAsRead+"]");s.data("isRead",!0),s.find(".newContentBadge").remove(),s.find(".notificationItemMarkAsConfirmed").remove(),s.removeClass("notificationUnconfirmed")}}),WCF.User.SignaturePreview=WCF.Message.Preview.extend({_handleResponse:function(t){var e=$("#previewContainer");e.length||(e=$('<section class="section" id="previewContainer"><h2 class="sectionTitle">'+WCF.Language.get("wcf.global.preview")+'</h2><div class="htmlContent"></div></section>').insertBefore($("#signatureContainer")).wcfFadeIn()),e.children("div").first().html(t.returnValues.message)}}),WCF.User.RecentActivityLoader=Class.extend({_container:null,_filteredByFollowedUsers:!1,_loadButton:null,_proxy:null,_userID:0,init:function(t,e){if(this._container=$("#recentActivities"),this._filteredByFollowedUsers=!0===e,this._userID=t,null!==this._userID&&!this._userID)return void console.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._container.children("li").length?(this._loadButton=$('<li class="showMore"><button class="small">'+WCF.Language.get("wcf.user.recentActivity.more")+"</button></li>").appendTo(this._container),this._loadButton=this._loadButton.children("button").click($.proxy(this._click,this))):$('<li class="showMore"><small>'+WCF.Language.get("wcf.user.recentActivity.noMoreEntries")+"</small></li>").appendTo(this._container),WCF.User.userID&&$(".jsRecentActivitySwitchContext .button").click($.proxy(this._switchContext,this))},_click:function(){this._loadButton.enable();var t={lastEventID:this._container.data("lastEventID"),lastEventTime:this._container.data("lastEventTime")};this._userID?t.userID=this._userID:this._filteredByFollowedUsers&&(t.filteredByFollowedUsers=1),this._proxy.setOption("data",{actionName:"load",className:"wcf\\data\\user\\activity\\event\\UserActivityEventAction",parameters:t}),this._proxy.sendRequest()},_switchContext:function(t){t.preventDefault(),$(t.currentTarget).hasClass("active")||new WCF.Action.Proxy({autoSend:!0,data:{actionName:"switchContext",className:"wcf\\data\\user\\activity\\event\\UserActivityEventAction"},success:function(){window.location.hash="#dashboardBoxRecentActivity",window.location.reload()}})},_success:function(t,e,i){t.returnValues.template?($(t.returnValues.template).insertBefore(this._loadButton.parent()),this._container.data("lastEventTime",t.returnValues.lastEventTime),this._container.data("lastEventID",t.returnValues.lastEventID),this._loadButton.enable()):($("<small>"+WCF.Language.get("wcf.user.recentActivity.noMoreEntries")+"</small>").appendTo(this._loadButton.parent()),this._loadButton.remove())}}),WCF.User.LikeLoader=Class.extend({_container:null,_likeType:"received",_likeValue:1,_loadButton:null,_noMoreEntries:null,_proxy:null,_userID:0,init:function(t){if(this._container=$("#likeList"),this._userID=t,!this._userID)return void console.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)});var e=$('<li class="likeListMore showMore"><button class="small">'+WCF.Language.get("wcf.like.likes.more")+"</button><small>"+WCF.Language.get("wcf.like.likes.noMoreEntries")+"</small></li>").appendTo(this._container);this._loadButton=e.children("button").click($.proxy(this._click,this)),this._noMoreEntries=e.children("small").hide(),2==this._container.find("> li").length&&(this._loadButton.hide(),this._noMoreEntries.show()),$("#likeType .button").click($.proxy(this._clickLikeType,this)),$("#likeValue .button").click($.proxy(this._clickLikeValue,this))},_clickLikeType:function(t){var e=$(t.currentTarget);this._likeType!=e.data("likeType")&&(this._likeType=e.data("likeType"),$("#likeType .button").removeClass("active"),e.addClass("active"),this._reload())},_clickLikeValue:function(t){var e=$(t.currentTarget);this._likeValue!=e.data("likeValue")&&(this._likeValue=e.data("likeValue"),$("#likeValue .button").removeClass("active"),e.addClass("active"),$("#likeType > li:first-child > .button").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likesReceived")),$("#likeType > li:last-child > .button").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likesGiven")),this._container.find("> li.likeListMore button").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likes.more")),this._container.find("> li.likeListMore small").text(WCF.Language.get("wcf.like."+(-1==this._likeValue?"dis":"")+"likes.noMoreEntries")),this._reload())},_reload:function(){this._container.find("> li:not(:first-child):not(:last-child)").remove(),this._container.data("lastLikeTime",0),this._click()},_click:function(){this._loadButton.enable();var t={lastLikeTime:this._container.data("lastLikeTime"),userID:this._userID,likeType:this._likeType,likeValue:this._likeValue};this._proxy.setOption("data",{actionName:"load",className:"wcf\\data\\like\\LikeAction",parameters:t}),this._proxy.sendRequest()},_success:function(t,e,i){t.returnValues.template?($(t.returnValues.template).insertBefore(this._loadButton.parent()),this._container.data("lastLikeTime",t.returnValues.lastLikeTime),this._noMoreEntries.hide(),this._loadButton.show().enable()):(this._noMoreEntries.show(),this._loadButton.hide())}}),WCF.User.ProfilePreview=WCF.Popover.extend({_proxy:null,_userProfiles:{},init:function(){this._super(".userLink"),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1}),WCF.System.ObjectStore.add("WCF.User.ProfilePreview",this)},_loadContent:function(){var t=$("#"+this._activeElementID),e=t.data("userID");if(this._userProfiles[e])this._insertContent(this._activeElementID,this._userProfiles[e],!0);else{this._proxy.setOption("data",{actionName:"getUserProfile",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[e]});var i=this._activeElementID,s=this;this._proxy.setOption("success",function(t,a,n){s._userProfiles[e]=t.returnValues.template,s._insertContent(i,t.returnValues.template,!0)}),this._proxy.setOption("failure",function(t,a,n,o){return s._userProfiles[e]=t.message,s._insertContent(i,t.message,!0),!1}),this._proxy.sendRequest()}},purge:function(t){delete this._userProfiles[t],this._data={}}}),WCF.User.Action={},WCF.User.Action.Follow=Class.extend({_containerList:null,_followButtonSelector:".jsFollowButton",_userID:0,init:function(t,e){t.length&&(this._containerList=t,e&&(this._followButtonSelector=e),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._containerList.each($.proxy(function(t,e){$(e).find(this._followButtonSelector).click($.proxy(this._click,this))},this)))},_click:function(t){t.preventDefault();var e=$(t.target);e.is("a")||(e=e.closest("a")),this._userID=e.data("objectID"),this._proxy.setOption("data",{actionName:e.data("following")?"unfollow":"follow",className:"wcf\\data\\user\\follow\\UserFollowAction",parameters:{data:{userID:this._userID}}}),this._proxy.sendRequest()},_success:function(t,e,i){this._containerList.each($.proxy(function(e,i){var s=$(i).find(this._followButtonSelector).get(0);if(s&&$(s).data("objectID")==this._userID)return s=$(s),t.returnValues.following?(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.unfollow")).children(".icon").removeClass("fa-plus").addClass("fa-minus"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.unfollow"))):(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.follow")).children(".icon").removeClass("fa-minus").addClass("fa-plus"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.follow"))),s.data("following",t.returnValues.following),!1},this)),(new WCF.System.Notification).show()}}),WCF.User.Action.Ignore=Class.extend({_containerList:null,_ignoreButtonSelector:".jsIgnoreButton",_userID:0,init:function(t,e){t.length&&(this._containerList=t,e&&(this._ignoreButtonSelector=e),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._containerList.each($.proxy(function(t,e){$(e).find(this._ignoreButtonSelector).click($.proxy(this._click,this))},this)))},_click:function(t){t.preventDefault();var e=$(t.target);e.is("a")||(e=e.closest("a")),this._userID=e.data("objectID"),this._proxy.setOption("data",{actionName:e.data("ignored")?"unignore":"ignore",className:"wcf\\data\\user\\ignore\\UserIgnoreAction",parameters:{data:{userID:this._userID}}}),this._proxy.sendRequest()},_success:function(t,e,i){this._containerList.each($.proxy(function(e,i){var s=$(i).find(this._ignoreButtonSelector).get(0);if(s&&$(s).data("objectID")==this._userID)return s=$(s),t.returnValues.isIgnoredUser?(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.unignore")).children(".icon").removeClass("fa-ban").addClass("fa-circle-o"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.unignore"))):(s.attr("data-tooltip",WCF.Language.get("wcf.user.button.ignore")).children(".icon").removeClass("fa-circle-o").addClass("fa-ban"),s.children(".invisible").text(WCF.Language.get("wcf.user.button.ignore"))),s.data("ignored",t.returnValues.isIgnoredUser),!1},this)),(new WCF.System.Notification).show();var s=this;WCF.System.ObjectStore.invoke("WCF.User.ProfilePreview",function(t){t.purge(s._userID)})}}),WCF.User.Avatar={},WCF.User.Avatar.Upload=WCF.Upload.extend({_userID:0,init:function(t){this._super($("#avatarUpload > dd > div"),void 0,"wcf\\data\\user\\avatar\\UserAvatarAction"),this._userID=t||0,$("#avatarForm input[type=radio]").change(function(){"custom"==$(this).val()?$("#avatarUpload > dd > div").show():$("#avatarUpload > dd > div").hide()}),$("#avatarForm input[type=radio][value=custom]:checked").length||$("#avatarUpload > dd > div").hide()},_initFile:function(t){return $("#avatarUpload > dt > img")},_success:function(t,e){if(e.returnValues.url){this._updateImage(e.returnValues.url),$("#avatarUpload > dd > .innerError").remove();new WCF.System.Notification(WCF.Language.get("wcf.user.avatar.upload.success")).show()}else e.returnValues.errorType&&this._getInnerErrorElement().text(WCF.Language.get("wcf.user.avatar.upload.error."+e.returnValues.errorType))},_updateImage:function(t){$("#avatarUpload > dt > img").remove();var e=$('<img src="'+t+'" class="userAvatarImage" alt="" />').css({height:"auto","max-height":"96px","max-width":"96px",width:"auto"});$("#avatarUpload > dt").prepend(e),WCF.DOMNodeInsertedHandler.execute()},_getInnerErrorElement:function(){var t=$("#avatarUpload > dd > .innerError");return t.length||(t=$('<small class="innerError"></span>'),$("#avatarUpload > dd").append(t)),t},_getParameters:function(){return{userID:this._userID}}}),WCF.User.List=Class.extend({_additionalParameters:{},_cache:{},_className:"",_dialog:null,_dialogTitle:"",_pageCount:0,_pageNo:1,_proxy:null,init:function(t,e,i){this._additionalParameters=i||{},this._cache={},this._className=t,this._dialog=null,this._dialogTitle=e,this._pageCount=0,this._pageNo=1,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)})},open:function(){this._pageNo=1,this._showPage()},_showPage:function(t,e){if(e&&e.activePage&&(this._pageNo=e.activePage),0!=this._pageCount&&(this._pageNo<1||this._pageNo>this._pageCount))return void console.debug("[WCF.User.List] Cannot access page "+this._pageNo+" of "+this._pageCount);if(this._cache[this._pageNo]){var i=!1;null===this._dialog&&(this._dialog=$("#userList"+this._className.hashCode()),0===this._dialog.length&&(this._dialog=$('<div id="userList'+this._className.hashCode()+'" />').hide().appendTo(document.body),i=!0)),this._dialog.empty(),
+this._dialog.html(this._cache[this._pageNo]),this._pageCount>1?this._dialog.find(".jsPagination").wcfPages({activePage:this._pageNo,maxPage:this._pageCount}).on("wcfpagesswitched",$.proxy(this._showPage,this)):this._dialog.find(".jsPagination").hide(),i?this._dialog.wcfDialog({title:this._dialogTitle}):(this._dialog.wcfDialog("option","title",this._dialogTitle),this._dialog.wcfDialog("open").wcfDialog("render")),WCF.DOMNodeInsertedHandler.execute()}else this._additionalParameters.pageNo=this._pageNo,this._proxy.setOption("data",{actionName:"getGroupedUserList",className:this._className,interfaceName:"wcf\\data\\IGroupedUserListAction",parameters:this._additionalParameters}),this._proxy.sendRequest()},_success:function(t,e,i){t.returnValues.pageCount&&(this._pageCount=t.returnValues.pageCount),this._cache[this._pageNo]=t.returnValues.template,this._showPage()}}),WCF.User.ObjectWatch={},WCF.User.ObjectWatch.Subscribe=Class.extend({_buttonSelector:".jsSubscribeButton",_buttons:{},_dialog:null,_notification:null,_reloadOnUnsubscribe:!1,init:function(t){this._buttons={},this._notification=null,this._reloadOnUnsubscribe=!0===t,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(this._buttonSelector).each($.proxy(function(t,e){var i=$(e);i.addClass("pointer");var s=i.data("objectType"),a=i.data("objectID");void 0===this._buttons[s]&&(this._buttons[s]={}),this._buttons[s][a]=i.click($.proxy(this._click,this))},this)),WCF.System.Event.addListener("com.woltlab.wcf.objectWatch","update",$.proxy(this._updateSubscriptionStatus,this))},_click:function(t){t.preventDefault();var e=$(t.currentTarget);this._proxy.setOption("data",{actionName:"manageSubscription",className:"wcf\\data\\user\\object\\watch\\UserObjectWatchAction",parameters:{objectID:e.data("objectID"),objectType:e.data("objectType")}}),this._proxy.sendRequest()},_success:function(t,e,i){if("manageSubscription"===t.actionName){null===this._dialog?(this._dialog=$("<div>"+t.returnValues.template+"</div>").hide().appendTo(document.body),this._dialog.wcfDialog({title:WCF.Language.get("wcf.user.objectWatch.manageSubscription")})):(this._dialog.html(t.returnValues.template),this._dialog.wcfDialog("open")),this._dialog.find(".formSubmit > .jsButtonSave").data("objectID",t.returnValues.objectID).data("objectType",t.returnValues.objectType).click($.proxy(this._save,this));var s=this._dialog.find("input[name=enableNotification]").disable();this._dialog.find("input[name=subscribe]").change(function(t){1==$(t.currentTarget).val()?s.enable():s.disable()});var a=this._dialog.find("input[name=subscribe]:checked");a.length&&1==a.val()&&s.enable()}else"saveSubscription"===t.actionName&&this._dialog.is(":visible")&&(this._dialog.wcfDialog("close"),this._updateSubscriptionStatus({isSubscribed:t.returnValues.subscribe,objectID:t.returnValues.objectID}),null===this._notification&&(this._notification=new WCF.System.Notification(WCF.Language.get("wcf.global.success.edit"))),this._notification.show())},_save:function(t){var e=this._buttons[$(t.currentTarget).data("objectType")][$(t.currentTarget).data("objectID")],i=this._dialog.find("input[name=subscribe]:checked").val(),s=this._dialog.find("input[name=enableNotification]").is(":checked")?1:0;this._proxy.setOption("data",{actionName:"saveSubscription",className:"wcf\\data\\user\\object\\watch\\UserObjectWatchAction",parameters:{enableNotification:s,objectID:e.data("objectID"),objectType:e.data("objectType"),subscribe:i}}),this._proxy.sendRequest()},_updateSubscriptionStatus:function(t){var e=$(this._buttonSelector+"[data-object-id="+t.objectID+"]"),i=e.children(".icon");if(t.isSubscribed)i.removeClass("fa-bookmark-o").addClass("fa-bookmark"),e.data("isSubscribed",!0);else if(e.data("removeOnUnsubscribe")?e.parent().remove():(i.removeClass("fa-bookmark").addClass("fa-bookmark-o"),e.data("isSubscribed",!1)),this._reloadOnUnsubscribe)return void window.location.reload();WCF.System.Event.fireEvent("com.woltlab.wcf.objectWatch","updatedSubscription",t)}}); })(this);
// WCF.Moderation.js
(function (window, undefined) { "use strict";WCF.Moderation={},WCF.Moderation.Management=Class.extend({_buttonSelector:"",_className:"",_confirmationTemplate:{},_dialog:null,_languageItem:"",_proxy:null,_queueID:0,_redirectURL:"",init:function(e,t,i){return this._buttonSelector?this._className?(this._dialog=null,this._queueID=e,this._redirectURL=t,this._languageItem=i,this._proxy=new WCF.Action.Proxy({failure:$.proxy(this._failure,this),success:$.proxy(this._success,this)}),$(this._buttonSelector).click($.proxy(this._click,this)),void $("#moderationAssignUser").click($.proxy(this._clickAssignedUser,this))):void console.debug("[WCF.Moderation.Management] Missing class name, aborting."):void console.debug("[WCF.Moderation.Management] Missing button selector, aborting.")},_click:function(e){var t=$(e.currentTarget).wcfIdentify(),i="";this._confirmationTemplate[t]&&(i=this._confirmationTemplate[t]),WCF.System.Confirmation.show(WCF.Language.get(this._languageItem.replace(/{actionName}/,t)),$.proxy(function(e,i,a){if("confirm"===e){var o={actionName:t,className:this._className,objectIDs:[this._queueID]};this._confirmationTemplate[t]&&(o.parameters={},$(a).find("input, textarea").each(function(e,t){var i=$(t),a=i.val();"input"===i.getTagName()&&"checkbox"===i.attr("type")&&(i.is(":checked")||(a=null)),null!==a&&(o.parameters[i.attr("name")]=a)})),this._proxy.setOption("data",o),this._proxy.sendRequest(),$(this._buttonSelector).disable()}},this),{},i)},_clickAssignedUser:function(){this._proxy.setOption("data",{actionName:"getAssignUserForm",className:"wcf\\data\\moderation\\queue\\ModerationQueueAction",objectIDs:[this._queueID]}),this._proxy.sendRequest()},_success:function(e,t,i){switch(e.actionName){case"getAssignUserForm":null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),this._dialog.html(e.returnValues.template).wcfDialog({title:WCF.Language.get("wcf.moderation.assignedUser")})):this._dialog.html(e.returnValues.template).wcfDialog("open"),this._dialog.find("button[data-type=submit]").click($.proxy(this._assignUser,this));break;case"assignUser":var a=$("#moderationAssignedUserContainer > dd > span").empty();e.returnValues.userID?$('<a href="'+e.returnValues.link+'" data-user-id="'+e.returnValues.userID+'" class="userLink">'+WCF.String.escapeHTML(e.returnValues.username)+"</a>").appendTo(a):a.append(e.returnValues.username),a.append(" "),e.returnValues.newStatus&&$("#moderationStatusContainer > dd").text(WCF.Language.get("wcf.moderation.status."+e.returnValues.newStatus)),this._dialog.wcfDialog("close"),(new WCF.System.Notification).show();break;default:var o=this;new WCF.System.Notification(WCF.Language.get("wcf.global.success")).show(function(){window.location=o._redirectURL})}},_failure:function(e,t,i,a){if(e.returnValues&&e.returnValues.fieldName&&"assignedUsername"==e.returnValues.fieldName){this._dialog.find("small.innerError").remove();var o="";switch(e.returnValues.errorType){case"empty":o=WCF.Language.get("wcf.global.form.error.empty");break;case"notAffected":o=WCF.Language.get("wcf.moderation.assignedUser.error.notAffected");break;default:o=WCF.Language.get("wcf.user.username.error."+e.returnValues.errorType,{username:this._dialog.find("#assignedUsername").val()})}return $('<small class="innerError">'+o+"</small>").insertAfter(this._dialog.find("#assignedUsername")),!1}return!0},_assignUser:function(){var e=this._dialog.find("input[name=assignedUserID]:checked").val(),t="";if(-1==e&&(t=$.trim(this._dialog.find("#assignedUsername").val())),-1==e&&0==t.length)return this._dialog.find("small.innerError").remove(),void $('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>").insertAfter(this._dialog.find("#assignedUsername"));this._proxy.setOption("data",{actionName:"assignUser",className:"wcf\\data\\moderation\\queue\\ModerationQueueAction",objectIDs:[this._queueID],parameters:{assignedUserID:e,assignedUsername:t}}),this._proxy.sendRequest()}}),WCF.Moderation.Queue={},WCF.Moderation.Queue.MarkAsRead=Class.extend({_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(document).on("dblclick",".moderationList .new .columnAvatar",$.proxy(this._dblclick,this))},_dblclick:function(e){this._proxy.setOption("data",{actionName:"markAsRead",className:"wcf\\data\\moderation\\queue\\ModerationQueueAction",objectIDs:[$(e.currentTarget).parents(".moderationQueueEntry:eq(0)").data("queueID")]}),this._proxy.sendRequest()},_success:function(e,t,i){$(".moderationList .new").each(function(t,i){var a=$(i);WCF.inArray(a.data("queueID"),e.objectIDs)&&(a.removeClass("new"),a.find(".columnAvatar").off("dblclick"))})}}),WCF.Moderation.Queue.MarkAllAsRead=Class.extend({_proxy:null,init:function(){this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),$(".markAllAsReadButton").click($.proxy(this._click,this))},_click:function(e){e.preventDefault(),this._proxy.setOption("data",{actionName:"markAllAsRead",className:"wcf\\data\\moderation\\queue\\ModerationQueueAction"}),this._proxy.sendRequest()},_success:function(e,t,i){var a=WCF.Dropdown.Interactive.Handler.getDropdown("outstandingModeration");a&&(a.getLinkList().find(".interactiveDropdownItemMarkAllAsRead").remove(),a.getItemList().find(".interactiveDropdownItemMarkAsRead").remove()),$("#outstandingModeration .badgeUpdate").remove();var o=$(".moderationList");o.find(".new").removeClass("new"),o.find(".columnAvatar").off("dblclick")}}),WCF.Moderation.Activation={},WCF.Moderation.Activation.Management=WCF.Moderation.Management.extend({init:function(e,t){this._buttonSelector="#enableContent, #removeContent",this._className="wcf\\data\\moderation\\queue\\ModerationQueueActivationAction",this._super(e,t,"wcf.moderation.activation.{actionName}.confirmMessage")}}),WCF.Moderation.Report={},WCF.Moderation.Report.Content=Class.extend({_buttons:{},_buttonSelector:"",_dialog:null,_notification:null,_objectID:0,_objectType:"",_proxy:null,init:function(e,t){this._objectType=e,this._buttonSelector=t,this._buttons={},this._notification=null,this._objectID=0,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initButtons(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Moderation.Report"+this._objectType.hashCode(),$.proxy(this._initButtons,this))},_initButtons:function(){var e=this;$(this._buttonSelector).each(function(t,i){var a=$(i),o=a.wcfIdentify();e._buttons[o]||(e._buttons[o]=a,a.click($.proxy(e._click,e)))})},_click:function(e){e.preventDefault(),this._objectID=$(e.currentTarget).data("objectID"),this._proxy.setOption("data",{actionName:"prepareReport",className:"wcf\\data\\moderation\\queue\\ModerationQueueReportAction",parameters:{objectID:this._objectID,objectType:this._objectType}}),this._proxy.sendRequest()},_success:function(e,t,i){e.returnValues.reported?(null===this._notification&&(this._notification=new WCF.System.Notification(WCF.Language.get("wcf.moderation.report.success"))),this._dialog.wcfDialog("close"),this._notification.show()):e.returnValues.template&&(this._showDialog(e.returnValues.template),e.returnValues.alreadyReported||this._dialog.find(".jsSubmitReport").click($.proxy(this._submit,this)))},_showDialog:function(e){null===this._dialog&&(this._dialog=$("#moderationReport"),this._dialog.length||(this._dialog=$('<div id="moderationReport" />').hide().appendTo(document.body))),this._dialog.html(e).wcfDialog({title:WCF.Language.get("wcf.moderation.report.reportContent")}).wcfDialog("render")},_submit:function(){var e=this._dialog.find(".jsReportMessage").val();if(""==$.trim(e))return this._dialog.find(".section > dl").addClass("formError"),void(this._dialog.find(".innerError").length||this._dialog.find(".jsReportMessage").after($('<small class="innerError">'+WCF.Language.get("wcf.global.form.error.empty")+"</small>")));this._proxy.setOption("data",{actionName:"report",className:"wcf\\data\\moderation\\queue\\ModerationQueueReportAction",parameters:{message:e,objectID:this._objectID,objectType:this._objectType}}),this._proxy.sendRequest()}}),WCF.Moderation.Report.Management=WCF.Moderation.Management.extend({init:function(e,t){this._buttonSelector="#removeContent, #removeReport",this._className="wcf\\data\\moderation\\queue\\ModerationQueueReportAction",this._super(e,t,"wcf.moderation.report.{actionName}.confirmMessage"),this._confirmationTemplate.removeContent=$('<div class="section"><dl><dt><label for="message">'+WCF.Language.get("wcf.moderation.report.removeContent.reason")+'</label></dt><dd><textarea name="message" id="message" cols="40" rows="3" /></dd></dl></div>'),this._confirmationTemplate.removeReport=$('<div class="section"><dl><dt></dt><dd><label><input type="checkbox" name="markAsJustified" id="markAsJustified" value="1"> '+WCF.Language.get("wcf.moderation.report.removeReport.markAsJustified")+"</label></dd></dl></div>")}}),WCF.User.Panel.Moderation=WCF.User.Panel.Abstract.extend({init:function(e){e.enableMarkAsRead=!0,this._super($("#outstandingModeration"),"outstandingModeration",e),require(["EventHandler"],function(e){e.add("com.woltlab.wcf.UserMenuMobile","more",function(e){"com.woltlab.wcf.moderation"===e.identifier&&this.toggle()}.bind(this))}.bind(this))},_initDropdown:function(){var e=this._super();return $('<li><a href="'+this._options.deletedContentLink+'" title="'+this._options.deletedContent+'" class="jsTooltip"><span class="icon icon24 fa-trash-o" /></a></li>').appendTo(e.getLinkList()),e},_load:function(){this._proxy.setOption("data",{actionName:"getOutstandingQueues",className:"wcf\\data\\moderation\\queue\\ModerationQueueAction"}),this._proxy.sendRequest()},_markAsRead:function(e,t){this._proxy.setOption("data",{actionName:"markAsRead",className:"wcf\\data\\moderation\\queue\\ModerationQueueAction",objectIDs:[t]}),this._proxy.sendRequest()},_markAllAsRead:function(e){this._proxy.setOption("data",{actionName:"markAllAsRead",className:"wcf\\data\\moderation\\queue\\ModerationQueueAction"}),this._proxy.sendRequest()},resetItems:function(){this._super(),this._loadData=!0}}); })(this);
// WCF.Combined.tiny.min.js -- DO NOT EDIT
// 3rdParty/jquery.js
-(function (window, undefined) { !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(e,t){t=t||ne;var n=t.createElement("script");n.text=e,t.head.appendChild(n).parentNode.removeChild(n)}function r(e){var t=!!e&&"length"in e&&e.length,n=he.type(e);return"function"!==n&&!he.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function i(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}function o(e,t,n){return he.isFunction(t)?he.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?he.grep(e,function(e){return e===t!==n}):"string"!=typeof t?he.grep(e,function(e){return se.call(t,e)>-1!==n}):Ee.test(t)?he.filter(t,e,n):(t=he.filter(t,e),he.grep(e,function(e){return se.call(t,e)>-1!==n&&1===e.nodeType}))}function a(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function s(e){var t={};return he.each(e.match(je)||[],function(e,n){t[n]=!0}),t}function u(e){return e}function l(e){throw e}function c(e,t,n,r){var i;try{e&&he.isFunction(i=e.promise)?i.call(e).done(t).fail(n):e&&he.isFunction(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}function f(){ne.removeEventListener("DOMContentLoaded",f),e.removeEventListener("load",f),he.ready()}function p(){this.expando=he.expando+p.uid++}function d(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Pe.test(e)?JSON.parse(e):e)}function h(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(Re,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n=d(n)}catch(e){}Oe.set(e,t,n)}else n=void 0;return n}function g(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return he.css(e,t,"")},u=s(),l=n&&n[3]||(he.cssNumber[t]?"":"px"),c=(he.cssNumber[t]||"px"!==l&&+u)&&Ie.exec(he.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do{o=o||".5",c/=o,he.style(e,t,c+l)}while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function v(e){var t,n=e.ownerDocument,r=e.nodeName,i=_e[r];return i||(t=n.body.appendChild(n.createElement(r)),i=he.css(t,"display"),t.parentNode.removeChild(t),"none"===i&&(i="block"),_e[r]=i,i)}function m(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)r=e[o],r.style&&(n=r.style.display,t?("none"===n&&(i[o]=Fe.get(r,"display")||null,i[o]||(r.style.display="")),""===r.style.display&&$e(r)&&(i[o]=v(r))):"none"!==n&&(i[o]="none",Fe.set(r,"display",n)));for(o=0;o<a;o++)null!=i[o]&&(e[o].style.display=i[o]);return e}function y(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&i(e,t)?he.merge([e],n):n}function x(e,t){for(var n=0,r=e.length;n<r;n++)Fe.set(e[n],"globalEval",!t||Fe.get(t[n],"globalEval"))}function b(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===he.type(o))he.merge(p,o.nodeType?[o]:o);else if(Ge.test(o)){for(a=a||f.appendChild(t.createElement("div")),s=(Xe.exec(o)||["",""])[1].toLowerCase(),u=Ve[s]||Ve._default,a.innerHTML=u[1]+he.htmlPrefilter(o)+u[2],c=u[0];c--;)a=a.lastChild;he.merge(p,a.childNodes),a=f.firstChild,a.textContent=""}else p.push(t.createTextNode(o));for(f.textContent="",d=0;o=p[d++];)if(r&&he.inArray(o,r)>-1)i&&i.push(o);else if(l=he.contains(o.ownerDocument,o),a=y(f.appendChild(o),"script"),l&&x(a),n)for(c=0;o=a[c++];)Ue.test(o.type||"")&&n.push(o);return f}function w(){return!0}function T(){return!1}function C(){try{return ne.activeElement}catch(e){}}function E(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)E(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=T;else if(!i)return e;return 1===o&&(a=i,i=function(e){return he().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=he.guid++)),e.each(function(){he.event.add(this,t,i,r,n)})}function k(e,t){return i(e,"table")&&i(11!==t.nodeType?t:t.firstChild,"tr")?he(">tbody",e)[0]||e:e}function S(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function N(e){var t=nt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function D(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Fe.hasData(e)&&(o=Fe.access(e),a=Fe.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n<r;n++)he.event.add(t,i,l[i][n])}Oe.hasData(e)&&(s=Oe.access(e),u=he.extend({},s),Oe.set(t,u))}}function j(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ze.test(e.type)?t.checked=e.checked:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}function A(e,t,r,i){t=oe.apply([],t);var o,a,s,u,l,c,f=0,p=e.length,d=p-1,h=t[0],g=he.isFunction(h);if(g||p>1&&"string"==typeof h&&!de.checkClone&&tt.test(h))return e.each(function(n){var o=e.eq(n);g&&(t[0]=h.call(this,n,o.html())),A(o,t,r,i)});if(p&&(o=b(t,e[0].ownerDocument,!1,e,i),a=o.firstChild,1===o.childNodes.length&&(o=a),a||i)){for(s=he.map(y(o,"script"),S),u=s.length;f<p;f++)l=o,f!==d&&(l=he.clone(l,!0,!0),u&&he.merge(s,y(l,"script"))),r.call(e[f],l,f);if(u)for(c=s[s.length-1].ownerDocument,he.map(s,N),f=0;f<u;f++)l=s[f],Ue.test(l.type||"")&&!Fe.access(l,"globalEval")&&he.contains(c,l)&&(l.src?he._evalUrl&&he._evalUrl(l.src):n(l.textContent.replace(rt,""),c))}return e}function q(e,t,n){for(var r,i=t?he.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||he.cleanData(y(r)),r.parentNode&&(n&&he.contains(r.ownerDocument,r)&&x(y(r,"script")),r.parentNode.removeChild(r));return e}function L(e,t,n){var r,i,o,a,s=e.style;return n=n||at(e),n&&(a=n.getPropertyValue(t)||n[t],""!==a||he.contains(e.ownerDocument,e)||(a=he.style(e,t)),!de.pixelMarginRight()&&ot.test(a)&&it.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function H(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function F(e){if(e in pt)return e;for(var t=e[0].toUpperCase()+e.slice(1),n=ft.length;n--;)if((e=ft[n]+t)in pt)return e}function O(e){var t=he.cssProps[e];return t||(t=he.cssProps[e]=F(e)||e),t}function P(e,t,n){var r=Ie.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function R(e,t,n,r,i){var o,a=0;for(o=n===(r?"border":"content")?4:"width"===t?1:0;o<4;o+=2)"margin"===n&&(a+=he.css(e,n+We[o],!0,i)),r?("content"===n&&(a-=he.css(e,"padding"+We[o],!0,i)),"margin"!==n&&(a-=he.css(e,"border"+We[o]+"Width",!0,i))):(a+=he.css(e,"padding"+We[o],!0,i),"padding"!==n&&(a+=he.css(e,"border"+We[o]+"Width",!0,i)));return a}function M(e,t,n){var r,i=at(e),o=L(e,t,i),a="border-box"===he.css(e,"boxSizing",!1,i);return ot.test(o)?o:(r=a&&(de.boxSizingReliable()||o===e.style[t]),"auto"===o&&(o=e["offset"+t[0].toUpperCase()+t.slice(1)]),(o=parseFloat(o)||0)+R(e,t,n||(a?"border":"content"),r,i)+"px")}function I(e,t,n,r,i){return new I.prototype.init(e,t,n,r,i)}function W(){ht&&(!1===ne.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(W):e.setTimeout(W,he.fx.interval),he.fx.tick())}function $(){return e.setTimeout(function(){dt=void 0}),dt=he.now()}function B(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)n=We[r],i["margin"+n]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function _(e,t,n){for(var r,i=(U.tweeners[t]||[]).concat(U.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function z(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&$e(e),v=Fe.get(e,"fxshow");n.queue||(a=he._queueHooks(e,"fx"),null==a.unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,he.queue(e,"fx").length||a.empty.fire()})}));for(r in t)if(i=t[r],gt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||he.style(e,r)}if((u=!he.isEmptyObject(t))||!he.isEmptyObject(d)){f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],l=v&&v.display,null==l&&(l=Fe.get(e,"display")),c=he.css(e,"display"),"none"===c&&(l?c=l:(m([e],!0),l=e.style.display||l,c=he.css(e,"display"),m([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===he.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1;for(r in d)u||(v?"hidden"in v&&(g=v.hidden):v=Fe.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&m([e],!0),p.done(function(){g||m([e]),Fe.remove(e,"fxshow");for(r in d)he.style(e,r,d[r])})),u=_(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}}function X(e,t){var n,r,i,o,a;for(n in e)if(r=he.camelCase(n),i=t[r],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=he.cssHooks[r])&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function U(e,t,n){var r,i,o=0,a=U.prefilters.length,s=he.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=dt||$(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;a<u;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),o<1&&u?n:(u||s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:he.extend({},t),opts:he.extend(!0,{specialEasing:{},easing:he.easing._default},n),originalProperties:t,originalOptions:n,startTime:dt||$(),duration:n.duration,tweens:[],createTween:function(t,n){var r=he.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;n<r;n++)l.tweens[n].run(1);return t?(s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l,t])):s.rejectWith(e,[l,t]),this}}),c=l.props;for(X(c,l.opts.specialEasing);o<a;o++)if(r=U.prefilters[o].call(l,e,c,l.opts))return he.isFunction(r.stop)&&(he._queueHooks(l.elem,l.opts.queue).stop=he.proxy(r.stop,r)),r;return he.map(c,_,l),he.isFunction(l.opts.start)&&l.opts.start.call(e,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),he.fx.timer(he.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l}function V(e){return(e.match(je)||[]).join(" ")}function G(e){return e.getAttribute&&e.getAttribute("class")||""}function Y(e,t,n,r){var i;if(Array.isArray(t))he.each(t,function(t,i){n||St.test(e)?r(e,i):Y(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==he.type(t))r(e,t);else for(i in t)Y(e+"["+i+"]",t[i],n,r)}function Q(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(je)||[];if(he.isFunction(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function J(e,t,n,r){function i(s){var u;return o[s]=!0,he.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||a||o[l]?a?!(u=l):void 0:(t.dataTypes.unshift(l),i(l),!1)}),u}var o={},a=e===Mt;return i(t.dataTypes[0])||!o["*"]&&i("*")}function K(e,t){var n,r,i=he.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&he.extend(!0,e,r),e}function Z(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function ee(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if(s=i.split(" "),s[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}var te=[],ne=e.document,re=Object.getPrototypeOf,ie=te.slice,oe=te.concat,ae=te.push,se=te.indexOf,ue={},le=ue.toString,ce=ue.hasOwnProperty,fe=ce.toString,pe=fe.call(Object),de={},he=function(e,t){return new he.fn.init(e,t)},ge=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,ve=/^-ms-/,me=/-([a-z])/g,ye=function(e,t){return t.toUpperCase()};he.fn=he.prototype={jquery:"3.2.1",constructor:he,length:0,toArray:function(){return ie.call(this)},get:function(e){return null==e?ie.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=he.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return he.each(this,e)},map:function(e){return this.pushStack(he.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(ie.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:ae,sort:te.sort,splice:te.splice},he.extend=he.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||he.isFunction(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],r=e[t],a!==r&&(l&&r&&(he.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&he.isPlainObject(n)?n:{},a[t]=he.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},he.extend({expando:"jQuery"+("3.2.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isFunction:function(e){return"function"===he.type(e)},isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){var t=he.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==le.call(e))&&(!(t=re(e))||"function"==typeof(n=ce.call(t,"constructor")&&t.constructor)&&fe.call(n)===pe)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(e){n(e)},camelCase:function(e){return e.replace(ve,"ms-").replace(me,ye)},each:function(e,t){var n,i=0;if(r(e))for(n=e.length;i<n&&!1!==t.call(e[i],i,e[i]);i++);else for(i in e)if(!1===t.call(e[i],i,e[i]))break;return e},trim:function(e){return null==e?"":(e+"").replace(ge,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(r(Object(e))?he.merge(n,"string"==typeof e?[e]:e):ae.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:se.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var i,o,a=0,s=[];if(r(e))for(i=e.length;a<i;a++)null!=(o=t(e[a],a,n))&&s.push(o);else for(a in e)null!=(o=t(e[a],a,n))&&s.push(o);return oe.apply([],s)},guid:1,proxy:function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),he.isFunction(e))return r=ie.call(arguments,2),i=function(){return e.apply(t||this,r.concat(ie.call(arguments)))},i.guid=e.guid=e.guid||he.guid++,i},now:Date.now,support:de}),"function"==typeof Symbol&&(he.fn[Symbol.iterator]=te[Symbol.iterator]),he.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){ue["[object "+t+"]"]=t.toLowerCase()});var xe=function(e){function t(e,t,n,r){var i,o,a,s,u,c,p,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!r&&((t?t.ownerDocument||t:I)!==q&&A(t),t=t||q,H)){if(11!==h&&(u=ge.exec(e)))if(i=u[1]){if(9===h){if(!(a=t.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(d&&(a=d.getElementById(i))&&R(t,a)&&a.id===i)return n.push(a),n}else{if(u[2])return Q.apply(n,t.getElementsByTagName(e)),n;if((i=u[3])&&b.getElementsByClassName&&t.getElementsByClassName)return Q.apply(n,t.getElementsByClassName(i)),n}if(b.qsa&&!z[e+" "]&&(!F||!F.test(e))){if(1!==h)d=t,p=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(xe,be):t.setAttribute("id",s=M),c=E(e),o=c.length;o--;)c[o]="#"+s+" "+f(c[o]);p=c.join(","),d=ve.test(e)&&l(t.parentNode)||t}if(p)try{return Q.apply(n,d.querySelectorAll(p)),n}catch(e){}finally{s===M&&t.removeAttribute("id")}}}return S(e.replace(oe,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>w.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[M]=!0,e}function i(e){var t=q.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)w.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&Te(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function l(e){return e&&void 0!==e.getElementsByTagName&&e}function c(){}function f(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=$++;return t.first?function(t,n,i){for(;t=t[r];)if(1===t.nodeType||a)return e(t,n,i);return!1}:function(t,n,u){var l,c,f,p=[W,s];if(u){for(;t=t[r];)if((1===t.nodeType||a)&&e(t,n,u))return!0}else for(;t=t[r];)if(1===t.nodeType||a)if(f=t[M]||(t[M]={}),c=f[t.uniqueID]||(f[t.uniqueID]={}),i&&i===t.nodeName.toLowerCase())t=t[r]||t;else{if((l=c[o])&&l[0]===W&&l[1]===s)return p[2]=l[2];if(c[o]=p,p[2]=e(t,n,u))return!0}return!1}}function d(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function h(e,n,r){for(var i=0,o=n.length;i<o;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function v(e,t,n,i,o,a){return i&&!i[M]&&(i=v(i)),o&&!o[M]&&(o=v(o,a)),r(function(r,a,s,u){var l,c,f,p=[],d=[],v=a.length,m=r||h(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?m:g(m,p,e,s,u),x=n?o||(r?e:v||i)?[]:a:y;if(n&&n(y,x,s,u),i)for(l=g(x,d),i(l,[],s,u),c=l.length;c--;)(f=l[c])&&(x[d[c]]=!(y[d[c]]=f));if(r){if(o||e){if(o){for(l=[],c=x.length;c--;)(f=x[c])&&l.push(y[c]=f);o(null,x=[],l,u)}for(c=x.length;c--;)(f=x[c])&&(l=o?K(r,f):p[c])>-1&&(r[l]=!(a[l]=f))}}else x=g(x===a?x.splice(v,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function m(e){for(var t,n,r,i=e.length,o=w.relative[e[0].type],a=o||w.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return K(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==N)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s<i;s++)if(n=w.relative[e[s].type])c=[p(d(c),n)];else{if(n=w.filter[e[s].type].apply(null,e[s].matches),n[M]){for(r=++s;r<i&&!w.relative[e[r].type];r++);return v(s>1&&d(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(oe,"$1"),n,s<r&&m(e.slice(s,r)),r<i&&m(e=e.slice(r)),r<i&&f(e))}c.push(n)}return d(c)}function y(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,u,l){var c,f,p,d=0,h="0",v=r&&[],m=[],y=N,x=r||o&&w.find.TAG("*",l),b=W+=null==y?1:Math.random()||.1,T=x.length;for(l&&(N=a===q||a||l);h!==T&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===q||(A(c),s=!H);p=e[f++];)if(p(c,a||q,s)){u.push(c);break}l&&(W=b)}i&&((c=!p&&c)&&d--,r&&v.push(c))}if(d+=h,i&&h!==d){for(f=0;p=n[f++];)p(v,m,a,s);if(r){if(d>0)for(;h--;)v[h]||m[h]||(m[h]=G.call(u));m=g(m)}Q.apply(u,m),l&&!r&&m.length>0&&d+n.length>1&&t.uniqueSort(u)}return l&&(W=b,N=y),v};return i?r(a):a}var x,b,w,T,C,E,k,S,N,D,j,A,q,L,H,F,O,P,R,M="sizzle"+1*new Date,I=e.document,W=0,$=0,B=n(),_=n(),z=n(),X=function(e,t){return e===t&&(j=!0),0},U={}.hasOwnProperty,V=[],G=V.pop,Y=V.push,Q=V.push,J=V.slice,K=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},Z="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ee="[\\x20\\t\\r\\n\\f]",te="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",ne="\\["+ee+"*("+te+")(?:"+ee+"*([*^$|!~]?=)"+ee+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+te+"))|)"+ee+"*\\]",re=":("+te+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ne+")*)|.*)\\)|)",ie=new RegExp(ee+"+","g"),oe=new RegExp("^"+ee+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ee+"+$","g"),ae=new RegExp("^"+ee+"*,"+ee+"*"),se=new RegExp("^"+ee+"*([>+~]|"+ee+")"+ee+"*"),ue=new RegExp("="+ee+"*([^\\]'\"]*?)"+ee+"*\\]","g"),le=new RegExp(re),ce=new RegExp("^"+te+"$"),fe={ID:new RegExp("^#("+te+")"),CLASS:new RegExp("^\\.("+te+")"),TAG:new RegExp("^("+te+"|[*])"),ATTR:new RegExp("^"+ne),PSEUDO:new RegExp("^"+re),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ee+"*(even|odd|(([+-]|)(\\d*)n|)"+ee+"*(?:([+-]|)"+ee+"*(\\d+)|))"+ee+"*\\)|)","i"),bool:new RegExp("^(?:"+Z+")$","i"),needsContext:new RegExp("^"+ee+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ee+"*((?:-\\d)?\\d*)"+ee+"*\\)|)(?=[^-]|$)","i")},pe=/^(?:input|select|textarea|button)$/i,de=/^h\d$/i,he=/^[^{]+\{\s*\[native \w/,ge=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,me=new RegExp("\\\\([\\da-f]{1,6}"+ee+"?|("+ee+")|.)","ig"),ye=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},xe=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,be=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},we=function(){A()},Te=p(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{Q.apply(V=J.call(I.childNodes),I.childNodes),V[I.childNodes.length].nodeType}catch(e){Q={apply:V.length?function(e,t){Y.apply(e,J.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}b=t.support={},C=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},A=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:I;return r!==q&&9===r.nodeType&&r.documentElement?(q=r,L=q.documentElement,H=!C(q),I!==q&&(n=q.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",we,!1):n.attachEvent&&n.attachEvent("onunload",we)),b.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),b.getElementsByTagName=i(function(e){return e.appendChild(q.createComment("")),!e.getElementsByTagName("*").length}),b.getElementsByClassName=he.test(q.getElementsByClassName),b.getById=i(function(e){return L.appendChild(e).id=M,!q.getElementsByName||!q.getElementsByName(M).length}),b.getById?(w.filter.ID=function(e){var t=e.replace(me,ye);return function(e){return e.getAttribute("id")===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n=t.getElementById(e);return n?[n]:[]}}):(w.filter.ID=function(e){var t=e.replace(me,ye);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),w.find.TAG=b.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):b.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},w.find.CLASS=b.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&H)return t.getElementsByClassName(e)},O=[],F=[],(b.qsa=he.test(q.querySelectorAll))&&(i(function(e){L.appendChild(e).innerHTML="<a id='"+M+"'></a><select id='"+M+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ee+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ee+"*(?:value|"+Z+")"),e.querySelectorAll("[id~="+M+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+M+"+*").length||F.push(".#.+[+~]")}),i(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=q.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ee+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&F.push(":enabled",":disabled"),L.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(b.matchesSelector=he.test(P=L.matches||L.webkitMatchesSelector||L.mozMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&i(function(e){b.disconnectedMatch=P.call(e,"*"),P.call(e,"[s!='']:x"),O.push("!=",re)}),F=F.length&&new RegExp(F.join("|")),O=O.length&&new RegExp(O.join("|")),t=he.test(L.compareDocumentPosition),R=t||he.test(L.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},X=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!b.sortDetached&&t.compareDocumentPosition(e)===n?e===q||e.ownerDocument===I&&R(I,e)?-1:t===q||t.ownerDocument===I&&R(I,t)?1:D?K(D,e)-K(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===q?-1:t===q?1:i?-1:o?1:D?K(D,e)-K(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===I?-1:u[r]===I?1:0},q):q},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==q&&A(e),n=n.replace(ue,"='$1']"),b.matchesSelector&&H&&!z[n+" "]&&(!O||!O.test(n))&&(!F||!F.test(n)))try{var r=P.call(e,n);if(r||b.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return t(n,q,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==q&&A(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==q&&A(e);var n=w.attrHandle[t.toLowerCase()],r=n&&U.call(w.attrHandle,t.toLowerCase())?n(e,t,!H):void 0;return void 0!==r?r:b.attributes||!H?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.escape=function(e){return(e+"").replace(xe,be)},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!b.detectDuplicates,D=!b.sortStable&&e.slice(0),e.sort(X),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},T=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=T(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=T(t);return n},w=t.selectors={cacheLength:50,createPseudo:r,match:fe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(me,ye),e[3]=(e[3]||e[4]||e[5]||"").replace(me,ye),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return fe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&le.test(n)&&(t=E(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(me,ye).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=B[e+" "];return t||(t=new RegExp("(^|"+ee+")"+e+"("+ee+"|$)"))&&B(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ie," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,m=s&&t.nodeName.toLowerCase(),y=!u&&!s,x=!1;if(v){if(o){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===m:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&y){for(p=v,f=p[M]||(p[M]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===W&&l[1],x=d&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[W,d,x];break}}else if(y&&(p=t,f=p[M]||(p[M]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===W&&l[1],x=d),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==m:1!==p.nodeType)||!++x||(y&&(f=p[M]||(p[M]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),c[e]=[W,x]),p!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=w.pseudos[e]||w.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[M]?o(n):o.length>1?(i=[e,e,"",n],w.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=K(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(oe,"$1"));return i[M]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(me,ye),function(t){return(t.textContent||t.innerText||T(t)).indexOf(e)>-1}}),lang:r(function(e){return ce.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(me,ye).toLowerCase(),function(t){var n;do{if(n=H?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===L},focus:function(e){return e===q.activeElement&&(!q.hasFocus||q.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:s(!1),disabled:s(!0),checked:function(e){var t=e.nodeName.toLowerCase()
-;return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!w.pseudos.empty(e)},header:function(e){return de.test(e.nodeName)},input:function(e){return pe.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[n<0?n+t:n]}),even:u(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},w.pseudos.nth=w.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})w.pseudos[x]=function(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}(x);for(x in{submit:!0,reset:!0})w.pseudos[x]=function(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}(x);return c.prototype=w.filters=w.pseudos,w.setFilters=new c,E=t.tokenize=function(e,n){var r,i,o,a,s,u,l,c=_[e+" "];if(c)return n?0:c.slice(0);for(s=e,u=[],l=w.preFilter;s;){r&&!(i=ae.exec(s))||(i&&(s=s.slice(i[0].length)||s),u.push(o=[])),r=!1,(i=se.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(oe," ")}),s=s.slice(r.length));for(a in w.filter)!(i=fe[a].exec(s))||l[a]&&!(i=l[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):_(e,u).slice(0)},k=t.compile=function(e,t){var n,r=[],i=[],o=z[e+" "];if(!o){for(t||(t=E(e)),n=t.length;n--;)o=m(t[n]),o[M]?r.push(o):i.push(o);o=z(e,y(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,u,c="function"==typeof e&&e,p=!r&&E(e=c.selector||e);if(n=n||[],1===p.length){if(o=p[0]=p[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&H&&w.relative[o[1].type]){if(!(t=(w.find.ID(a.matches[0].replace(me,ye),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=fe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!w.relative[s=a.type]);)if((u=w.find[s])&&(r=u(a.matches[0].replace(me,ye),ve.test(o[0].type)&&l(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&f(o)))return Q.apply(n,r),n;break}}return(c||k(e,p))(r,t,!H,n,!t||ve.test(e)&&l(t.parentNode)||t),n},b.sortStable=M.split("").sort(X).join("")===M,b.detectDuplicates=!!j,A(),b.sortDetached=i(function(e){return 1&e.compareDocumentPosition(q.createElement("fieldset"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),b.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(Z,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);he.find=xe,he.expr=xe.selectors,he.expr[":"]=he.expr.pseudos,he.uniqueSort=he.unique=xe.uniqueSort,he.text=xe.getText,he.isXMLDoc=xe.isXML,he.contains=xe.contains,he.escapeSelector=xe.escape;var be=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&he(e).is(n))break;r.push(e)}return r},we=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},Te=he.expr.match.needsContext,Ce=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,Ee=/^.[^:#\[\.,]*$/;he.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?he.find.matchesSelector(r,e)?[r]:[]:he.find.matches(e,he.grep(t,function(e){return 1===e.nodeType}))},he.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(he(e).filter(function(){for(t=0;t<r;t++)if(he.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)he.find(e,i[t],n);return r>1?he.uniqueSort(n):n},filter:function(e){return this.pushStack(o(this,e||[],!1))},not:function(e){return this.pushStack(o(this,e||[],!0))},is:function(e){return!!o(this,"string"==typeof e&&Te.test(e)?he(e):e||[],!1).length}});var ke,Se=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(he.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||ke,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:Se.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof he?t[0]:t,he.merge(this,he.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:ne,!0)),Ce.test(r[1])&&he.isPlainObject(t))for(r in t)he.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=ne.getElementById(r[2]),i&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):he.isFunction(e)?void 0!==n.ready?n.ready(e):e(he):he.makeArray(e,this)}).prototype=he.fn,ke=he(ne);var Ne=/^(?:parents|prev(?:Until|All))/,De={children:!0,contents:!0,next:!0,prev:!0};he.fn.extend({has:function(e){var t=he(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(he.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&he(e);if(!Te.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&he.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?he.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?se.call(he(e),this[0]):se.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(he.uniqueSort(he.merge(this.get(),he(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),he.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return be(e,"parentNode")},parentsUntil:function(e,t,n){return be(e,"parentNode",n)},next:function(e){return a(e,"nextSibling")},prev:function(e){return a(e,"previousSibling")},nextAll:function(e){return be(e,"nextSibling")},prevAll:function(e){return be(e,"previousSibling")},nextUntil:function(e,t,n){return be(e,"nextSibling",n)},prevUntil:function(e,t,n){return be(e,"previousSibling",n)},siblings:function(e){return we((e.parentNode||{}).firstChild,e)},children:function(e){return we(e.firstChild)},contents:function(e){return i(e,"iframe")?e.contentDocument:(i(e,"template")&&(e=e.content||e),he.merge([],e.childNodes))}},function(e,t){he.fn[e]=function(n,r){var i=he.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=he.filter(r,i)),this.length>1&&(De[e]||he.uniqueSort(i),Ne.test(e)&&i.reverse()),this.pushStack(i)}});var je=/[^\x20\t\r\n\f]+/g;he.Callbacks=function(e){e="string"==typeof e?s(e):he.extend({},e);var t,n,r,i,o=[],a=[],u=-1,l=function(){for(i=i||e.once,r=t=!0;a.length;u=-1)for(n=a.shift();++u<o.length;)!1===o[u].apply(n[0],n[1])&&e.stopOnFalse&&(u=o.length,n=!1);e.memory||(n=!1),t=!1,i&&(o=n?[]:"")},c={add:function(){return o&&(n&&!t&&(u=o.length-1,a.push(n)),function t(n){he.each(n,function(n,r){he.isFunction(r)?e.unique&&c.has(r)||o.push(r):r&&r.length&&"string"!==he.type(r)&&t(r)})}(arguments),n&&!t&&l()),this},remove:function(){return he.each(arguments,function(e,t){for(var n;(n=he.inArray(t,o,n))>-1;)o.splice(n,1),n<=u&&u--}),this},has:function(e){return e?he.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],a.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},he.extend({Deferred:function(t){var n=[["notify","progress",he.Callbacks("memory"),he.Callbacks("memory"),2],["resolve","done",he.Callbacks("once memory"),he.Callbacks("once memory"),0,"resolved"],["reject","fail",he.Callbacks("once memory"),he.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return he.Deferred(function(t){he.each(n,function(n,r){var i=he.isFunction(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&he.isFunction(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){function o(t,n,r,i){return function(){var s=this,c=arguments,f=function(){var e,f;if(!(t<a)){if((e=r.apply(s,c))===n.promise())throw new TypeError("Thenable self-resolution");f=e&&("object"==typeof e||"function"==typeof e)&&e.then,he.isFunction(f)?i?f.call(e,o(a,n,u,i),o(a,n,l,i)):(a++,f.call(e,o(a,n,u,i),o(a,n,l,i),o(a,n,u,n.notifyWith))):(r!==u&&(s=void 0,c=[e]),(i||n.resolveWith)(s,c))}},p=i?f:function(){try{f()}catch(e){he.Deferred.exceptionHook&&he.Deferred.exceptionHook(e,p.stackTrace),t+1>=a&&(r!==l&&(s=void 0,c=[e]),n.rejectWith(s,c))}};t?p():(he.Deferred.getStackHook&&(p.stackTrace=he.Deferred.getStackHook()),e.setTimeout(p))}}var a=0;return he.Deferred(function(e){n[0][3].add(o(0,e,he.isFunction(i)?i:u,e.notifyWith)),n[1][3].add(o(0,e,he.isFunction(t)?t:u)),n[2][3].add(o(0,e,he.isFunction(r)?r:l))}).promise()},promise:function(e){return null!=e?he.extend(e,i):i}},o={};return he.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[0][2].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=ie.call(arguments),o=he.Deferred(),a=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?ie.call(arguments):n,--t||o.resolveWith(r,i)}};if(t<=1&&(c(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||he.isFunction(i[n]&&i[n].then)))return o.then();for(;n--;)c(i[n],a(n),o.reject);return o.promise()}});var Ae=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;he.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&Ae.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},he.readyException=function(t){e.setTimeout(function(){throw t})};var qe=he.Deferred();he.fn.ready=function(e){return qe.then(e).catch(function(e){he.readyException(e)}),this},he.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--he.readyWait:he.isReady)||(he.isReady=!0,!0!==e&&--he.readyWait>0||qe.resolveWith(ne,[he]))}}),he.ready.then=qe.then,"complete"===ne.readyState||"loading"!==ne.readyState&&!ne.documentElement.doScroll?e.setTimeout(he.ready):(ne.addEventListener("DOMContentLoaded",f),e.addEventListener("load",f));var Le=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===he.type(n)){i=!0;for(s in n)Le(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,he.isFunction(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(he(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},He=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};p.uid=1,p.prototype={cache:function(e){var t=e[this.expando];return t||(t={},He(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[he.camelCase(t)]=n;else for(r in t)i[he.camelCase(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][he.camelCase(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){Array.isArray(t)?t=t.map(he.camelCase):(t=he.camelCase(t),t=t in r?[t]:t.match(je)||[]),n=t.length;for(;n--;)delete r[t[n]]}(void 0===t||he.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!he.isEmptyObject(t)}};var Fe=new p,Oe=new p,Pe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Re=/[A-Z]/g;he.extend({hasData:function(e){return Oe.hasData(e)||Fe.hasData(e)},data:function(e,t,n){return Oe.access(e,t,n)},removeData:function(e,t){Oe.remove(e,t)},_data:function(e,t,n){return Fe.access(e,t,n)},_removeData:function(e,t){Fe.remove(e,t)}}),he.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=Oe.get(o),1===o.nodeType&&!Fe.get(o,"hasDataAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=he.camelCase(r.slice(5)),h(o,r,i[r])));Fe.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){Oe.set(this,e)}):Le(this,function(t){var n;if(o&&void 0===t){if(void 0!==(n=Oe.get(o,e)))return n;if(void 0!==(n=h(o,e)))return n}else this.each(function(){Oe.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){Oe.remove(this,e)})}}),he.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Fe.get(e,t),n&&(!r||Array.isArray(n)?r=Fe.access(e,t,he.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=he.queue(e,t),r=n.length,i=n.shift(),o=he._queueHooks(e,t),a=function(){he.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Fe.get(e,n)||Fe.access(e,n,{empty:he.Callbacks("once memory").add(function(){Fe.remove(e,[t+"queue",n])})})}}),he.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?he.queue(this[0],e):void 0===t?this:this.each(function(){var n=he.queue(this,e,t);he._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&he.dequeue(this,e)})},dequeue:function(e){return this.each(function(){he.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=he.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)(n=Fe.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var Me=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,Ie=new RegExp("^(?:([+-])=|)("+Me+")([a-z%]*)$","i"),We=["Top","Right","Bottom","Left"],$e=function(e,t){return e=t||e,"none"===e.style.display||""===e.style.display&&he.contains(e.ownerDocument,e)&&"none"===he.css(e,"display")},Be=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i},_e={};he.fn.extend({show:function(){return m(this,!0)},hide:function(){return m(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){$e(this)?he(this).show():he(this).hide()})}});var ze=/^(?:checkbox|radio)$/i,Xe=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,Ue=/^$|\/(?:java|ecma)script/i,Ve={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};Ve.optgroup=Ve.option,Ve.tbody=Ve.tfoot=Ve.colgroup=Ve.caption=Ve.thead,Ve.th=Ve.td;var Ge=/<|&#?\w+;/;!function(){var e=ne.createDocumentFragment(),t=e.appendChild(ne.createElement("div")),n=ne.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),de.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="<textarea>x</textarea>",de.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var Ye=ne.documentElement,Qe=/^key/,Je=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ke=/^([^.]*)(?:\.(.+)|)/;he.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Fe.get(e);if(v)for(n.handler&&(o=n,n=o.handler,i=o.selector),i&&he.find.matchesSelector(Ye,i),n.guid||(n.guid=he.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==he&&he.event.triggered!==t.type?he.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(je)||[""],l=t.length;l--;)s=Ke.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d&&(f=he.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=he.event.special[d]||{},c=he.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&he.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||(p=u[d]=[],p.delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),he.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Fe.hasData(e)&&Fe.get(e);if(v&&(u=v.events)){for(t=(t||"").match(je)||[""],l=t.length;l--;)if(s=Ke.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){for(f=he.event.special[d]||{},d=(r?f.delegateType:f.bindType)||d,p=u[d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||he.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)he.event.remove(e,d+t[l],n,r,!0);he.isEmptyObject(u)&&Fe.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=he.event.fix(e),u=new Array(arguments.length),l=(Fe.get(this,"events")||{})[s.type]||[],c=he.event.special[s.type]||{};for(u[0]=s,t=1;t<arguments.length;t++)u[t]=arguments[t];if(s.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,s)){for(a=he.event.handlers.call(this,s,l),t=0;(i=a[t++])&&!s.isPropagationStopped();)for(s.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!s.isImmediatePropagationStopped();)s.rnamespace&&!s.rnamespace.test(o.namespace)||(s.handleObj=o,s.data=o.data,void 0!==(r=((he.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,u))&&!1===(s.result=r)&&(s.preventDefault(),s.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,s),s.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&e.button>=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)r=t[n],i=r.selector+" ",void 0===a[i]&&(a[i]=r.needsContext?he(i,this).index(l)>-1:he.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(e,t){Object.defineProperty(he.Event.prototype,e,{enumerable:!0,configurable:!0,get:he.isFunction(t)?function(){if(this.originalEvent)return t(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[e]},set:function(t){Object.defineProperty(this,e,{enumerable:!0,configurable:!0,writable:!0,value:t})}})},fix:function(e){return e[he.expando]?e:new he.Event(e)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==C()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===C()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&i(this,"input"))return this.click(),!1},_default:function(e){return i(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},he.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},he.Event=function(e,t){if(!(this instanceof he.Event))return new he.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?w:T,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&he.extend(this,t),this.timeStamp=e&&e.timeStamp||he.now(),this[he.expando]=!0},he.Event.prototype={constructor:he.Event,isDefaultPrevented:T,isPropagationStopped:T,isImmediatePropagationStopped:T,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=w,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=w,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=w,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},he.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,char:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&Qe.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&Je.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},he.event.addProp),he.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){he.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return i&&(i===r||he.contains(r,i))||(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),he.fn.extend({on:function(e,t,n,r){return E(this,e,t,n,r)},one:function(e,t,n,r){return E(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,he(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=T),this.each(function(){he.event.remove(this,e,n,t)})}});var Ze=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,et=/<script|<style|<link/i,tt=/checked\s*(?:[^=]|=\s*.checked.)/i,nt=/^true\/(.*)/,rt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;he.extend({htmlPrefilter:function(e){return e.replace(Ze,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=he.contains(e.ownerDocument,e);if(!(de.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||he.isXMLDoc(e)))for(a=y(s),o=y(e),r=0,i=o.length;r<i;r++)j(o[r],a[r]);if(t)if(n)for(o=o||y(e),a=a||y(s),r=0,i=o.length;r<i;r++)D(o[r],a[r]);else D(e,s);return a=y(s,"script"),a.length>0&&x(a,!u&&y(e,"script")),s},cleanData:function(e){for(var t,n,r,i=he.event.special,o=0;void 0!==(n=e[o]);o++)if(He(n)){if(t=n[Fe.expando]){if(t.events)for(r in t.events)i[r]?he.event.remove(n,r):he.removeEvent(n,r,t.handle);n[Fe.expando]=void 0}n[Oe.expando]&&(n[Oe.expando]=void 0)}}}),he.fn.extend({detach:function(e){return q(this,e,!0)},remove:function(e){return q(this,e)},text:function(e){return Le(this,function(e){return void 0===e?he.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){k(this,e).appendChild(e)}})},prepend:function(){return A(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=k(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return A(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(he.cleanData(y(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return he.clone(this,e,t)})},html:function(e){return Le(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!et.test(e)&&!Ve[(Xe.exec(e)||["",""])[1].toLowerCase()]){e=he.htmlPrefilter(e);try{for(;n<r;n++)t=this[n]||{},1===t.nodeType&&(he.cleanData(y(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return A(this,arguments,function(t){var n=this.parentNode;he.inArray(this,e)<0&&(he.cleanData(y(this)),n&&n.replaceChild(t,this))},e)}}),he.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){he.fn[e]=function(e){for(var n,r=[],i=he(e),o=i.length-1,a=0;a<=o;a++)n=a===o?this:this.clone(!0),he(i[a])[t](n),ae.apply(r,n.get());return this.pushStack(r)}});var it=/^margin/,ot=new RegExp("^("+Me+")(?!px)[a-z%]+$","i"),at=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)};!function(){function t(){if(s){s.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",s.innerHTML="",Ye.appendChild(a);var t=e.getComputedStyle(s);n="1%"!==t.top,o="2px"===t.marginLeft,r="4px"===t.width,s.style.marginRight="50%",i="4px"===t.marginRight,Ye.removeChild(a),s=null}}var n,r,i,o,a=ne.createElement("div"),s=ne.createElement("div");s.style&&(s.style.backgroundClip="content-box",s.cloneNode(!0).style.backgroundClip="",de.clearCloneStyle="content-box"===s.style.backgroundClip,a.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",a.appendChild(s),he.extend(de,{pixelPosition:function(){return t(),n},boxSizingReliable:function(){return t(),r},pixelMarginRight:function(){return t(),i},reliableMarginLeft:function(){return t(),o}}))}();var st=/^(none|table(?!-c[ea]).+)/,ut=/^--/,lt={position:"absolute",visibility:"hidden",display:"block"},ct={letterSpacing:"0",fontWeight:"400"},ft=["Webkit","Moz","ms"],pt=ne.createElement("div").style;he.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=L(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{float:"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=he.camelCase(t),u=ut.test(t),l=e.style;if(u||(t=O(s)),a=he.cssHooks[t]||he.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];o=typeof n,"string"===o&&(i=Ie.exec(n))&&i[1]&&(n=g(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(he.cssNumber[s]?"":"px")),de.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=he.camelCase(t);return ut.test(t)||(t=O(s)),a=he.cssHooks[t]||he.cssHooks[s],a&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=L(e,t,r)),"normal"===i&&t in ct&&(i=ct[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),he.each(["height","width"],function(e,t){he.cssHooks[t]={get:function(e,n,r){if(n)return!st.test(he.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?M(e,t,r):Be(e,lt,function(){return M(e,t,r)})},set:function(e,n,r){var i,o=r&&at(e),a=r&&R(e,t,r,"border-box"===he.css(e,"boxSizing",!1,o),o);return a&&(i=Ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=he.css(e,t)),P(e,n,a)}}}),he.cssHooks.marginLeft=H(de.reliableMarginLeft,function(e,t){if(t)return(parseFloat(L(e,"marginLeft"))||e.getBoundingClientRect().left-Be(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),he.each({margin:"",padding:"",border:"Width"},function(e,t){he.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+We[r]+t]=o[r]||o[r-2]||o[0];return i}},it.test(e)||(he.cssHooks[e+t].set=P)}),he.fn.extend({css:function(e,t){return Le(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=at(e),i=t.length;a<i;a++)o[t[a]]=he.css(e,t[a],!1,r);return o}return void 0!==n?he.style(e,t,n):he.css(e,t)},e,t,arguments.length>1)}}),he.Tween=I,I.prototype={constructor:I,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||he.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(he.cssNumber[n]?"":"px")},cur:function(){var e=I.propHooks[this.prop];return e&&e.get?e.get(this):I.propHooks._default.get(this)},run:function(e){var t,n=I.propHooks[this.prop];return this.options.duration?this.pos=t=he.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):I.propHooks._default.set(this),this}},I.prototype.init.prototype=I.prototype,I.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=he.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){he.fx.step[e.prop]?he.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[he.cssProps[e.prop]]&&!he.cssHooks[e.prop]?e.elem[e.prop]=e.now:he.style(e.elem,e.prop,e.now+e.unit)}}},I.propHooks.scrollTop=I.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},he.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},he.fx=I.prototype.init,he.fx.step={};var dt,ht,gt=/^(?:toggle|show|hide)$/,vt=/queueHooks$/;he.Animation=he.extend(U,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return g(n.elem,e,Ie.exec(t),n),n}]},tweener:function(e,t){he.isFunction(e)?(t=e,e=["*"]):e=e.match(je);for(var n,r=0,i=e.length;r<i;r++)n=e[r],U.tweeners[n]=U.tweeners[n]||[],U.tweeners[n].unshift(t)},prefilters:[z],prefilter:function(e,t){t?U.prefilters.unshift(e):U.prefilters.push(e)}}),he.speed=function(e,t,n){var r=e&&"object"==typeof e?he.extend({},e):{complete:n||!n&&t||he.isFunction(e)&&e,duration:e,easing:n&&t||t&&!he.isFunction(t)&&t};return he.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in he.fx.speeds?r.duration=he.fx.speeds[r.duration]:r.duration=he.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){he.isFunction(r.old)&&r.old.call(this),r.queue&&he.dequeue(this,r.queue)},r},he.fn.extend({fadeTo:function(e,t,n,r){return this.filter($e).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=he.isEmptyObject(e),o=he.speed(t,n,r),a=function(){var t=U(this,he.extend({},e),o);(i||Fe.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&!1!==e&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=he.timers,a=Fe.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&vt.test(i)&&r(a[i])
-;for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));!t&&n||he.dequeue(this,e)})},finish:function(e){return!1!==e&&(e=e||"fx"),this.each(function(){var t,n=Fe.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=he.timers,a=r?r.length:0;for(n.finish=!0,he.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;t<a;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),he.each(["toggle","show","hide"],function(e,t){var n=he.fn[t];he.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(B(t,!0),e,r,i)}}),he.each({slideDown:B("show"),slideUp:B("hide"),slideToggle:B("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){he.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),he.timers=[],he.fx.tick=function(){var e,t=0,n=he.timers;for(dt=he.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||he.fx.stop(),dt=void 0},he.fx.timer=function(e){he.timers.push(e),he.fx.start()},he.fx.interval=13,he.fx.start=function(){ht||(ht=!0,W())},he.fx.stop=function(){ht=null},he.fx.speeds={slow:600,fast:200,_default:400},he.fn.delay=function(t,n){return t=he.fx?he.fx.speeds[t]||t:t,n=n||"fx",this.queue(n,function(n,r){var i=e.setTimeout(n,t);r.stop=function(){e.clearTimeout(i)}})},function(){var e=ne.createElement("input"),t=ne.createElement("select"),n=t.appendChild(ne.createElement("option"));e.type="checkbox",de.checkOn=""!==e.value,de.optSelected=n.selected,e=ne.createElement("input"),e.value="t",e.type="radio",de.radioValue="t"===e.value}();var mt,yt=he.expr.attrHandle;he.fn.extend({attr:function(e,t){return Le(this,he.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){he.removeAttr(this,e)})}}),he.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?he.prop(e,t,n):(1===o&&he.isXMLDoc(e)||(i=he.attrHooks[t.toLowerCase()]||(he.expr.match.bool.test(t)?mt:void 0)),void 0!==n?null===n?void he.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=he.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!de.radioValue&&"radio"===t&&i(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(je);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),mt={set:function(e,t,n){return!1===t?he.removeAttr(e,n):e.setAttribute(n,n),n}},he.each(he.expr.match.bool.source.match(/\w+/g),function(e,t){var n=yt[t]||he.find.attr;yt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=yt[a],yt[a]=i,i=null!=n(e,t,r)?a:null,yt[a]=o),i}});var xt=/^(?:input|select|textarea|button)$/i,bt=/^(?:a|area)$/i;he.fn.extend({prop:function(e,t){return Le(this,he.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[he.propFix[e]||e]})}}),he.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&he.isXMLDoc(e)||(t=he.propFix[t]||t,i=he.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=he.find.attr(e,"tabindex");return t?parseInt(t,10):xt.test(e.nodeName)||bt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),de.optSelected||(he.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),he.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){he.propFix[this.toLowerCase()]=this}),he.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(he.isFunction(e))return this.each(function(t){he(this).addClass(e.call(this,t,G(this)))});if("string"==typeof e&&e)for(t=e.match(je)||[];n=this[u++];)if(i=G(n),r=1===n.nodeType&&" "+V(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=V(r),i!==s&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(he.isFunction(e))return this.each(function(t){he(this).removeClass(e.call(this,t,G(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(je)||[];n=this[u++];)if(i=G(n),r=1===n.nodeType&&" "+V(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=V(r),i!==s&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):he.isFunction(e)?this.each(function(n){he(this).toggleClass(e.call(this,n,G(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=he(this),o=e.match(je)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=G(this),t&&Fe.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":Fe.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+V(G(n))+" ").indexOf(t)>-1)return!0;return!1}});var wt=/\r/g;he.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=he.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,he(this).val()):e,null==i?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=he.map(i,function(e){return null==e?"":e+""})),(t=he.valHooks[this.type]||he.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=he.valHooks[i.type]||he.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(wt,""):null==n?"":n)}}}),he.extend({valHooks:{option:{get:function(e){var t=he.find.attr(e,"value");return null!=t?t:V(he.text(e))}},select:{get:function(e){var t,n,r,o=e.options,a=e.selectedIndex,s="select-one"===e.type,u=s?null:[],l=s?a+1:o.length;for(r=a<0?l:s?a:0;r<l;r++)if(n=o[r],(n.selected||r===a)&&!n.disabled&&(!n.parentNode.disabled||!i(n.parentNode,"optgroup"))){if(t=he(n).val(),s)return t;u.push(t)}return u},set:function(e,t){for(var n,r,i=e.options,o=he.makeArray(t),a=i.length;a--;)r=i[a],(r.selected=he.inArray(he.valHooks.option.get(r),o)>-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),he.each(["radio","checkbox"],function(){he.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=he.inArray(he(e).val(),t)>-1}},de.checkOn||(he.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Tt=/^(?:focusinfocus|focusoutblur)$/;he.extend(he.event,{trigger:function(t,n,r,i){var o,a,s,u,l,c,f,p=[r||ne],d=ce.call(t,"type")?t.type:t,h=ce.call(t,"namespace")?t.namespace.split("."):[];if(a=s=r=r||ne,3!==r.nodeType&&8!==r.nodeType&&!Tt.test(d+he.event.triggered)&&(d.indexOf(".")>-1&&(h=d.split("."),d=h.shift(),h.sort()),l=d.indexOf(":")<0&&"on"+d,t=t[he.expando]?t:new he.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:he.makeArray(n,[t]),f=he.event.special[d]||{},i||!f.trigger||!1!==f.trigger.apply(r,n))){if(!i&&!f.noBubble&&!he.isWindow(r)){for(u=f.delegateType||d,Tt.test(u+d)||(a=a.parentNode);a;a=a.parentNode)p.push(a),s=a;s===(r.ownerDocument||ne)&&p.push(s.defaultView||s.parentWindow||e)}for(o=0;(a=p[o++])&&!t.isPropagationStopped();)t.type=o>1?u:f.bindType||d,c=(Fe.get(a,"events")||{})[t.type]&&Fe.get(a,"handle"),c&&c.apply(a,n),(c=l&&a[l])&&c.apply&&He(a)&&(t.result=c.apply(a,n),!1===t.result&&t.preventDefault());return t.type=d,i||t.isDefaultPrevented()||f._default&&!1!==f._default.apply(p.pop(),n)||!He(r)||l&&he.isFunction(r[d])&&!he.isWindow(r)&&(s=r[l],s&&(r[l]=null),he.event.triggered=d,r[d](),he.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=he.extend(new he.Event,n,{type:e,isSimulated:!0});he.event.trigger(r,null,t)}}),he.fn.extend({trigger:function(e,t){return this.each(function(){he.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return he.event.trigger(e,t,n,!0)}}),he.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){he.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),he.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),de.focusin="onfocusin"in e,de.focusin||he.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){he.event.simulate(t,e.target,he.event.fix(e))};he.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=Fe.access(r,t);i||r.addEventListener(e,n,!0),Fe.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=Fe.access(r,t)-1;i?Fe.access(r,t,i):(r.removeEventListener(e,n,!0),Fe.remove(r,t))}}});var Ct=e.location,Et=he.now(),kt=/\?/;he.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||he.error("Invalid XML: "+t),n};var St=/\[\]$/,Nt=/\r?\n/g,Dt=/^(?:submit|button|image|reset|file)$/i,jt=/^(?:input|select|textarea|keygen)/i;he.param=function(e,t){var n,r=[],i=function(e,t){var n=he.isFunction(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!he.isPlainObject(e))he.each(e,function(){i(this.name,this.value)});else for(n in e)Y(n,e[n],t,i);return r.join("&")},he.fn.extend({serialize:function(){return he.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=he.prop(this,"elements");return e?he.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!he(this).is(":disabled")&&jt.test(this.nodeName)&&!Dt.test(e)&&(this.checked||!ze.test(e))}).map(function(e,t){var n=he(this).val();return null==n?null:Array.isArray(n)?he.map(n,function(e){return{name:t.name,value:e.replace(Nt,"\r\n")}}):{name:t.name,value:n.replace(Nt,"\r\n")}}).get()}});var At=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ft=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=ne.createElement("a");Wt.href=Ct.href,he.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Ft.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":he.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?K(K(e,he.ajaxSettings),t):K(he.ajaxSettings,e)},ajaxPrefilter:Q(Rt),ajaxTransport:Q(Mt),ajax:function(t,n){function r(t,n,r,s){var l,p,d,b,w,T=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",C.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Z(h,C,r)),b=ee(h,b,C,l),l?(h.ifModified&&(w=C.getResponseHeader("Last-Modified"),w&&(he.lastModified[o]=w),(w=C.getResponseHeader("etag"))&&(he.etag[o]=w)),204===t||"HEAD"===h.type?T="nocontent":304===t?T="notmodified":(T=b.state,p=b.data,d=b.error,l=!d)):(d=T,!t&&T||(T="error",t<0&&(t=0))),C.status=t,C.statusText=(n||T)+"",l?m.resolveWith(g,[p,T,C]):m.rejectWith(g,[C,T,d]),C.statusCode(x),x=void 0,f&&v.trigger(l?"ajaxSuccess":"ajaxError",[C,h,l?p:d]),y.fireWith(g,[C,T]),f&&(v.trigger("ajaxComplete",[C,h]),--he.active||he.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=he.ajaxSetup({},n),g=h.context||h,v=h.context&&(g.nodeType||g.jquery)?he(g):he.event,m=he.Deferred(),y=he.Callbacks("once memory"),x=h.statusCode||{},b={},w={},T="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=Ht.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)C.always(e[C.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(m.promise(C),h.url=((t||h.url||Ct.href)+"").replace(Pt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(je)||[""],null==h.crossDomain){l=ne.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Wt.protocol+"//"+Wt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=he.param(h.data,h.traditional)),J(Rt,h,n,C),c)return C;f=he.event&&h.global,f&&0==he.active++&&he.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Ot.test(h.type),o=h.url.replace(qt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(At,"+")):(d=h.url.slice(o.length),h.data&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Lt,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(he.lastModified[o]&&C.setRequestHeader("If-Modified-Since",he.lastModified[o]),he.etag[o]&&C.setRequestHeader("If-None-Match",he.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&C.setRequestHeader("Content-Type",h.contentType),C.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+It+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)C.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,C,h)||c))return C.abort();if(T="abort",y.add(h.complete),C.done(h.success),C.fail(h.error),i=J(Mt,h,n,C)){if(C.readyState=1,f&&v.trigger("ajaxSend",[C,h]),c)return C;h.async&&h.timeout>0&&(u=e.setTimeout(function(){C.abort("timeout")},h.timeout));try{c=!1,i.send(b,r)}catch(e){if(c)throw e;r(-1,e)}}else r(-1,"No Transport");return C},getJSON:function(e,t,n){return he.get(e,t,n,"json")},getScript:function(e,t){return he.get(e,void 0,t,"script")}}),he.each(["get","post"],function(e,t){he[t]=function(e,n,r,i){return he.isFunction(n)&&(i=i||r,r=n,n=void 0),he.ajax(he.extend({url:e,type:t,dataType:i,data:n,success:r},he.isPlainObject(e)&&e))}}),he._evalUrl=function(e){return he.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},he.fn.extend({wrapAll:function(e){var t;return this[0]&&(he.isFunction(e)&&(e=e.call(this[0])),t=he(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return he.isFunction(e)?this.each(function(t){he(this).wrapInner(e.call(this,t))}):this.each(function(){var t=he(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=he.isFunction(e);return this.each(function(n){he(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){he(this).replaceWith(this.childNodes)}),this}}),he.expr.pseudos.hidden=function(e){return!he.expr.pseudos.visible(e)},he.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},he.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var $t={0:200,1223:204},Bt=he.ajaxSettings.xhr();de.cors=!!Bt&&"withCredentials"in Bt,de.ajax=Bt=!!Bt,he.ajaxTransport(function(t){var n,r;if(de.cors||Bt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o($t[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),he.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),he.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return he.globalEval(e),e}}}),he.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),he.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=he("<script>").prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),ne.head.appendChild(t[0])},abort:function(){n&&n()}}}});var _t=[],zt=/(=)\?(?=&|$)|\?\?/;he.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=_t.pop()||he.expando+"_"+Et++;return this[e]=!0,e}}),he.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=!1!==t.jsonp&&(zt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&zt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=he.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(zt,"$1"+i):!1!==t.jsonp&&(t.url+=(kt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||he.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?he(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,_t.push(i)),a&&he.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),de.createHTMLDocument=function(){var e=ne.implementation.createHTMLDocument("").body;return e.innerHTML="<form></form><form></form>",2===e.childNodes.length}(),he.parseHTML=function(e,t,n){if("string"!=typeof e)return[];"boolean"==typeof t&&(n=t,t=!1);var r,i,o;return t||(de.createHTMLDocument?(t=ne.implementation.createHTMLDocument(""),r=t.createElement("base"),r.href=ne.location.href,t.head.appendChild(r)):t=ne),i=Ce.exec(e),o=!n&&[],i?[t.createElement(i[1])]:(i=b([e],t,o),o&&o.length&&he(o).remove(),he.merge([],i.childNodes))},he.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=V(e.slice(s)),e=e.slice(0,s)),he.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&he.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?he("<div>").append(he.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},he.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){he.fn[t]=function(e){return this.on(t,e)}}),he.expr.pseudos.animated=function(e){return he.grep(he.timers,function(t){return e===t.elem}).length},he.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=he.css(e,"position"),f=he(e),p={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=he.css(e,"top"),u=he.css(e,"left"),l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),he.isFunction(t)&&(t=t.call(e,n,he.extend({},s))),null!=t.top&&(p.top=t.top-s.top+a),null!=t.left&&(p.left=t.left-s.left+i),"using"in t?t.using.call(e,p):f.css(p)}},he.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){he.offset.setOffset(this,e,t)});var t,n,r,i,o=this[0];if(o)return o.getClientRects().length?(r=o.getBoundingClientRect(),t=o.ownerDocument,n=t.documentElement,i=t.defaultView,{top:r.top+i.pageYOffset-n.clientTop,left:r.left+i.pageXOffset-n.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n=this[0],r={top:0,left:0};return"fixed"===he.css(n,"position")?t=n.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),i(e[0],"html")||(r=e.offset()),r={top:r.top+he.css(e[0],"borderTopWidth",!0),left:r.left+he.css(e[0],"borderLeftWidth",!0)}),{top:t.top-r.top-he.css(n,"marginTop",!0),left:t.left-r.left-he.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===he.css(e,"position");)e=e.offsetParent;return e||Ye})}}),he.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;he.fn[e]=function(r){return Le(this,function(e,r,i){var o;if(he.isWindow(e)?o=e:9===e.nodeType&&(o=e.defaultView),void 0===i)return o?o[t]:e[r];o?o.scrollTo(n?o.pageXOffset:i,n?i:o.pageYOffset):e[r]=i},e,r,arguments.length)}}),he.each(["top","left"],function(e,t){he.cssHooks[t]=H(de.pixelPosition,function(e,n){if(n)return n=L(e,t),ot.test(n)?he(e).position()[t]+"px":n})}),he.each({Height:"height",Width:"width"},function(e,t){he.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){he.fn[r]=function(i,o){var a=arguments.length&&(n||"boolean"!=typeof i),s=n||(!0===i||!0===o?"margin":"border");return Le(this,function(t,n,i){var o;return he.isWindow(t)?0===r.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(o=t.documentElement,Math.max(t.body["scroll"+e],o["scroll"+e],t.body["offset"+e],o["offset"+e],o["client"+e])):void 0===i?he.css(t,n,s):he.style(t,n,i,s)},t,a?i:void 0,a)}})}),he.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),he.holdReady=function(e){e?he.readyWait++:he.ready(!0)},he.isArray=Array.isArray,he.parseJSON=JSON.parse,he.nodeName=i,"function"==typeof define&&define.amd&&define("jquery",[],function(){return he});var Xt=e.jQuery,Ut=e.$;return he.noConflict=function(t){return e.$===he&&(e.$=Ut),t&&e.jQuery===he&&(e.jQuery=Xt),he},t||(e.jQuery=e.$=he),he}); })(this);
+(function (window, undefined) { !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(e,t,n){t=t||ae;var r,i=t.createElement("script");if(i.text=e,n)for(r in be)n[r]&&(i[r]=n[r]);t.head.appendChild(i).parentNode.removeChild(i)}function r(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?pe[de.call(e)]||"object":typeof e}function i(e){var t=!!e&&"length"in e&&e.length,n=r(e);return!me(e)&&!xe(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function o(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}function a(e,t,n){return me(t)?we.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?we.grep(e,function(e){return e===t!==n}):"string"!=typeof t?we.grep(e,function(e){return fe.call(t,e)>-1!==n}):we.filter(t,e,n)}function s(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function u(e){var t={};return we.each(e.match(Le)||[],function(e,n){t[n]=!0}),t}function l(e){return e}function c(e){throw e}function f(e,t,n,r){var i;try{e&&me(i=e.promise)?i.call(e).done(t).fail(n):e&&me(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}function p(){ae.removeEventListener("DOMContentLoaded",p),e.removeEventListener("load",p),we.ready()}function d(e,t){return t.toUpperCase()}function h(e){return e.replace(Me,"ms-").replace(Re,d)}function g(){this.expando=we.expando+g.uid++}function v(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Be.test(e)?JSON.parse(e):e)}function y(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(Fe,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n=v(n)}catch(e){}$e.set(e,t,n)}else n=void 0;return n}function m(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return we.css(e,t,"")},u=s(),l=n&&n[3]||(we.cssNumber[t]?"":"px"),c=(we.cssNumber[t]||"px"!==l&&+u)&&ze.exec(we.css(e,t));if(c&&c[3]!==l){for(u/=2,l=l||c[3],c=+u||1;a--;)we.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,we.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function x(e){var t,n=e.ownerDocument,r=e.nodeName,i=Ge[r];return i||(t=n.body.appendChild(n.createElement(r)),i=we.css(t,"display"),t.parentNode.removeChild(t),"none"===i&&(i="block"),Ge[r]=i,i)}function b(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)r=e[o],r.style&&(n=r.style.display,t?("none"===n&&(i[o]=We.get(r,"display")||null,i[o]||(r.style.display="")),""===r.style.display&&Ue(r)&&(i[o]=x(r))):"none"!==n&&(i[o]="none",We.set(r,"display",n)));for(o=0;o<a;o++)null!=i[o]&&(e[o].style.display=i[o]);return e}function w(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&o(e,t)?we.merge([e],n):n}function T(e,t){for(var n=0,r=e.length;n<r;n++)We.set(e[n],"globalEval",!t||We.get(t[n],"globalEval"))}function C(e,t,n,i,o){for(var a,s,u,l,c,f,p=t.createDocumentFragment(),d=[],h=0,g=e.length;h<g;h++)if((a=e[h])||0===a)if("object"===r(a))we.merge(d,a.nodeType?[a]:a);else if(Ze.test(a)){for(s=s||p.appendChild(t.createElement("div")),u=(Qe.exec(a)||["",""])[1].toLowerCase(),l=Ke[u]||Ke._default,s.innerHTML=l[1]+we.htmlPrefilter(a)+l[2],f=l[0];f--;)s=s.lastChild;we.merge(d,s.childNodes),s=p.firstChild,s.textContent=""}else d.push(t.createTextNode(a));for(p.textContent="",h=0;a=d[h++];)if(i&&we.inArray(a,i)>-1)o&&o.push(a);else if(c=we.contains(a.ownerDocument,a),s=w(p.appendChild(a),"script"),c&&T(s),n)for(f=0;a=s[f++];)Je.test(a.type||"")&&n.push(a);return p}function E(){return!0}function k(){return!1}function S(){try{return ae.activeElement}catch(e){}}function D(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)D(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=k;else if(!i)return e;return 1===o&&(a=i,i=function(e){return we().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=we.guid++)),e.each(function(){we.event.add(this,t,i,r,n)})}function N(e,t){return o(e,"table")&&o(11!==t.nodeType?t:t.firstChild,"tr")?we(e).children("tbody")[0]||e:e}function A(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function j(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function q(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(We.hasData(e)&&(o=We.access(e),a=We.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n<r;n++)we.event.add(t,i,l[i][n])}$e.hasData(e)&&(s=$e.access(e),u=we.extend({},s),$e.set(t,u))}}function L(e,t){var n=t.nodeName.toLowerCase();"input"===n&&Ye.test(e.type)?t.checked=e.checked:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}function H(e,t,r,i){t=le.apply([],t);var o,a,s,u,l,c,f=0,p=e.length,d=p-1,h=t[0],g=me(h);if(g||p>1&&"string"==typeof h&&!ye.checkClone&&at.test(h))return e.each(function(n){var o=e.eq(n);g&&(t[0]=h.call(this,n,o.html())),H(o,t,r,i)});if(p&&(o=C(t,e[0].ownerDocument,!1,e,i),a=o.firstChild,1===o.childNodes.length&&(o=a),a||i)){for(s=we.map(w(o,"script"),A),u=s.length;f<p;f++)l=o,f!==d&&(l=we.clone(l,!0,!0),u&&we.merge(s,w(l,"script"))),r.call(e[f],l,f);if(u)for(c=s[s.length-1].ownerDocument,we.map(s,j),f=0;f<u;f++)l=s[f],Je.test(l.type||"")&&!We.access(l,"globalEval")&&we.contains(c,l)&&(l.src&&"module"!==(l.type||"").toLowerCase()?we._evalUrl&&we._evalUrl(l.src):n(l.textContent.replace(st,""),c,l))}return e}function O(e,t,n){for(var r,i=t?we.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||we.cleanData(w(r)),r.parentNode&&(n&&we.contains(r.ownerDocument,r)&&T(w(r,"script")),r.parentNode.removeChild(r));return e}function P(e,t,n){var r,i,o,a,s=e.style;return n=n||lt(e),n&&(a=n.getPropertyValue(t)||n[t],""!==a||we.contains(e.ownerDocument,e)||(a=we.style(e,t)),!ye.pixelBoxStyles()&&ut.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function M(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function R(e){if(e in vt)return e;for(var t=e[0].toUpperCase()+e.slice(1),n=gt.length;n--;)if((e=gt[n]+t)in vt)return e}function I(e){var t=we.cssProps[e];return t||(t=we.cssProps[e]=R(e)||e),t}function W(e,t,n){var r=ze.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function $(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=we.css(e,n+Xe[a],!0,i)),r?("content"===n&&(u-=we.css(e,"padding"+Xe[a],!0,i)),"margin"!==n&&(u-=we.css(e,"border"+Xe[a]+"Width",!0,i))):(u+=we.css(e,"padding"+Xe[a],!0,i),"padding"!==n?u+=we.css(e,"border"+Xe[a]+"Width",!0,i):s+=we.css(e,"border"+Xe[a]+"Width",!0,i));return!r&&o>=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function B(e,t,n){var r=lt(e),i=P(e,t,r),o="border-box"===we.css(e,"boxSizing",!1,r),a=o;if(ut.test(i)){if(!n)return i;i="auto"}return a=a&&(ye.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===we.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+$(e,t,n||(o?"border":"content"),a,r,i)+"px"}function F(e,t,n,r,i){return new F.prototype.init(e,t,n,r,i)}function _(){mt&&(!1===ae.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(_):e.setTimeout(_,we.fx.interval),we.fx.tick())}function z(){return e.setTimeout(function(){yt=void 0}),yt=Date.now()}function X(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)n=Xe[r],i["margin"+n]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function U(e,t,n){for(var r,i=(Y.tweeners[t]||[]).concat(Y.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function V(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&Ue(e),v=We.get(e,"fxshow");n.queue||(a=we._queueHooks(e,"fx"),null==a.unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,we.queue(e,"fx").length||a.empty.fire()})}));for(r in t)if(i=t[r],xt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||we.style(e,r)}if((u=!we.isEmptyObject(t))||!we.isEmptyObject(d)){f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],l=v&&v.display,null==l&&(l=We.get(e,"display")),c=we.css(e,"display"),"none"===c&&(l?c=l:(b([e],!0),l=e.style.display||l,c=we.css(e,"display"),b([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===we.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1;for(r in d)u||(v?"hidden"in v&&(g=v.hidden):v=We.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&b([e],!0),p.done(function(){g||b([e]),We.remove(e,"fxshow");for(r in d)we.style(e,r,d[r])})),u=U(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}}function G(e,t){var n,r,i,o,a;for(n in e)if(r=h(n),i=t[r],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=we.cssHooks[r])&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function Y(e,t,n){var r,i,o=0,a=Y.prefilters.length,s=we.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=yt||z(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;a<u;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),o<1&&u?n:(u||s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:we.extend({},t),opts:we.extend(!0,{specialEasing:{},easing:we.easing._default},n),originalProperties:t,originalOptions:n,startTime:yt||z(),duration:n.duration,tweens:[],createTween:function(t,n){var r=we.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;n<r;n++)l.tweens[n].run(1);return t?(s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l,t])):s.rejectWith(e,[l,t]),this}}),c=l.props;for(G(c,l.opts.specialEasing);o<a;o++)if(r=Y.prefilters[o].call(l,e,c,l.opts))return me(r.stop)&&(we._queueHooks(l.elem,l.opts.queue).stop=r.stop.bind(r)),r;return we.map(c,U,l),me(l.opts.start)&&l.opts.start.call(e,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),we.fx.timer(we.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l}function Q(e){return(e.match(Le)||[]).join(" ")}function J(e){return e.getAttribute&&e.getAttribute("class")||""}function K(e){return Array.isArray(e)?e:"string"==typeof e?e.match(Le)||[]:[]}function Z(e,t,n,i){var o;if(Array.isArray(t))we.each(t,function(t,r){n||qt.test(e)?i(e,r):Z(e+"["+("object"==typeof r&&null!=r?t:"")+"]",r,n,i)});else if(n||"object"!==r(t))i(e,t);else for(o in t)Z(e+"["+o+"]",t[o],n,i)}function ee(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(Le)||[];if(me(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function te(e,t,n,r){function i(s){var u;return o[s]=!0,we.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||a||o[l]?a?!(u=l):void 0:(t.dataTypes.unshift(l),i(l),!1)}),u}var o={},a=e===_t;return i(t.dataTypes[0])||!o["*"]&&i("*")}function ne(e,t){var n,r,i=we.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&we.extend(!0,e,r),e}function re(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function ie(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if(s=i.split(" "),s[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}var oe=[],ae=e.document,se=Object.getPrototypeOf,ue=oe.slice,le=oe.concat,ce=oe.push,fe=oe.indexOf,pe={},de=pe.toString,he=pe.hasOwnProperty,ge=he.toString,ve=ge.call(Object),ye={},me=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},xe=function(e){return null!=e&&e===e.window},be={type:!0,src:!0,noModule:!0},we=function(e,t){return new we.fn.init(e,t)},Te=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;we.fn=we.prototype={jquery:"3.3.1",constructor:we,length:0,toArray:function(){return ue.call(this)},get:function(e){return null==e?ue.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=we.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return we.each(this,e)},map:function(e){return this.pushStack(we.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(ue.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:ce,sort:oe.sort,splice:oe.splice},we.extend=we.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||me(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],r=e[t],a!==r&&(l&&r&&(we.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&we.isPlainObject(n)?n:{},a[t]=we.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},we.extend({expando:"jQuery"+("3.3.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==de.call(e))&&(!(t=se(e))||"function"==typeof(n=he.call(t,"constructor")&&t.constructor)&&ge.call(n)===ve)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e){n(e)},each:function(e,t){var n,r=0;if(i(e))for(n=e.length;r<n&&!1!==t.call(e[r],r,e[r]);r++);else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(Te,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(i(Object(e))?we.merge(n,"string"==typeof e?[e]:e):ce.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:fe.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,o,a=0,s=[];if(i(e))for(r=e.length;a<r;a++)null!=(o=t(e[a],a,n))&&s.push(o);else for(a in e)null!=(o=t(e[a],a,n))&&s.push(o);return le.apply([],s)},guid:1,support:ye}),"function"==typeof Symbol&&(we.fn[Symbol.iterator]=oe[Symbol.iterator]),we.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){pe["[object "+t+"]"]=t.toLowerCase()});var Ce=function(e){function t(e,t,n,r){var i,o,a,s,u,c,p,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!r&&((t?t.ownerDocument||t:W)!==q&&j(t),t=t||q,H)){if(11!==h&&(u=ge.exec(e)))if(i=u[1]){if(9===h){if(!(a=t.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(d&&(a=d.getElementById(i))&&R(t,a)&&a.id===i)return n.push(a),n}else{if(u[2])return Q.apply(n,t.getElementsByTagName(e)),n;if((i=u[3])&&b.getElementsByClassName&&t.getElementsByClassName)return Q.apply(n,t.getElementsByClassName(i)),n}if(b.qsa&&!z[e+" "]&&(!O||!O.test(e))){if(1!==h)d=t,p=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(xe,be):t.setAttribute("id",s=I),c=E(e),o=c.length;o--;)c[o]="#"+s+" "+f(c[o]);p=c.join(","),d=ve.test(e)&&l(t.parentNode)||t}if(p)try{return Q.apply(n,d.querySelectorAll(p)),n}catch(e){}finally{s===I&&t.removeAttribute("id")}}}return S(e.replace(oe,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>w.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[I]=!0,e}function i(e){var t=q.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)w.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&Te(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function l(e){return e&&void 0!==e.getElementsByTagName&&e}function c(){}function f(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=B++;return t.first?function(t,n,i){for(;t=t[r];)if(1===t.nodeType||a)return e(t,n,i);return!1}:function(t,n,u){var l,c,f,p=[$,s];if(u){for(;t=t[r];)if((1===t.nodeType||a)&&e(t,n,u))return!0}else for(;t=t[r];)if(1===t.nodeType||a)if(f=t[I]||(t[I]={}),c=f[t.uniqueID]||(f[t.uniqueID]={}),i&&i===t.nodeName.toLowerCase())t=t[r]||t;else{if((l=c[o])&&l[0]===$&&l[1]===s)return p[2]=l[2];if(c[o]=p,p[2]=e(t,n,u))return!0}return!1}}function d(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function h(e,n,r){for(var i=0,o=n.length;i<o;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function v(e,t,n,i,o,a){return i&&!i[I]&&(i=v(i)),o&&!o[I]&&(o=v(o,a)),r(function(r,a,s,u){var l,c,f,p=[],d=[],v=a.length,y=r||h(t||"*",s.nodeType?[s]:s,[]),m=!e||!r&&t?y:g(y,p,e,s,u),x=n?o||(r?e:v||i)?[]:a:m;if(n&&n(m,x,s,u),i)for(l=g(x,d),i(l,[],s,u),c=l.length;c--;)(f=l[c])&&(x[d[c]]=!(m[d[c]]=f));if(r){if(o||e){if(o){for(l=[],c=x.length;c--;)(f=x[c])&&l.push(m[c]=f);o(null,x=[],l,u)}for(c=x.length;c--;)(f=x[c])&&(l=o?K(r,f):p[c])>-1&&(r[l]=!(a[l]=f))}}else x=g(x===a?x.splice(v,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function y(e){for(var t,n,r,i=e.length,o=w.relative[e[0].type],a=o||w.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return K(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==D)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s<i;s++)if(n=w.relative[e[s].type])c=[p(d(c),n)];else{if(n=w.filter[e[s].type].apply(null,e[s].matches),n[I]){for(r=++s;r<i&&!w.relative[e[r].type];r++);return v(s>1&&d(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(oe,"$1"),n,s<r&&y(e.slice(s,r)),r<i&&y(e=e.slice(r)),r<i&&f(e))}c.push(n)}return d(c)}function m(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,u,l){var c,f,p,d=0,h="0",v=r&&[],y=[],m=D,x=r||o&&w.find.TAG("*",l),b=$+=null==m?1:Math.random()||.1,T=x.length;for(l&&(D=a===q||a||l);h!==T&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===q||(j(c),s=!H);p=e[f++];)if(p(c,a||q,s)){u.push(c);break}l&&($=b)}i&&((c=!p&&c)&&d--,r&&v.push(c))}if(d+=h,i&&h!==d){for(f=0;p=n[f++];)p(v,y,a,s);if(r){if(d>0)for(;h--;)v[h]||y[h]||(y[h]=G.call(u));y=g(y)}Q.apply(u,y),l&&!r&&y.length>0&&d+n.length>1&&t.uniqueSort(u)}return l&&($=b,D=m),v};return i?r(a):a}var x,b,w,T,C,E,k,S,D,N,A,j,q,L,H,O,P,M,R,I="sizzle"+1*new Date,W=e.document,$=0,B=0,F=n(),_=n(),z=n(),X=function(e,t){return e===t&&(A=!0),0},U={}.hasOwnProperty,V=[],G=V.pop,Y=V.push,Q=V.push,J=V.slice,K=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},Z="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ee="[\\x20\\t\\r\\n\\f]",te="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",ne="\\["+ee+"*("+te+")(?:"+ee+"*([*^$|!~]?=)"+ee+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+te+"))|)"+ee+"*\\]",re=":("+te+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ne+")*)|.*)\\)|)",ie=new RegExp(ee+"+","g"),oe=new RegExp("^"+ee+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ee+"+$","g"),ae=new RegExp("^"+ee+"*,"+ee+"*"),se=new RegExp("^"+ee+"*([>+~]|"+ee+")"+ee+"*"),ue=new RegExp("="+ee+"*([^\\]'\"]*?)"+ee+"*\\]","g"),le=new RegExp(re),ce=new RegExp("^"+te+"$"),fe={ID:new RegExp("^#("+te+")"),CLASS:new RegExp("^\\.("+te+")"),TAG:new RegExp("^("+te+"|[*])"),ATTR:new RegExp("^"+ne),PSEUDO:new RegExp("^"+re),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ee+"*(even|odd|(([+-]|)(\\d*)n|)"+ee+"*(?:([+-]|)"+ee+"*(\\d+)|))"+ee+"*\\)|)","i"),bool:new RegExp("^(?:"+Z+")$","i"),needsContext:new RegExp("^"+ee+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ee+"*((?:-\\d)?\\d*)"+ee+"*\\)|)(?=[^-]|$)","i")},pe=/^(?:input|select|textarea|button)$/i,de=/^h\d$/i,he=/^[^{]+\{\s*\[native \w/,ge=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,ye=new RegExp("\\\\([\\da-f]{1,6}"+ee+"?|("+ee+")|.)","ig"),me=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},xe=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,be=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},we=function(){j()},Te=p(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{Q.apply(V=J.call(W.childNodes),W.childNodes),V[W.childNodes.length].nodeType}catch(e){Q={apply:V.length?function(e,t){Y.apply(e,J.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}b=t.support={},C=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},j=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:W;return r!==q&&9===r.nodeType&&r.documentElement?(q=r,L=q.documentElement,H=!C(q),W!==q&&(n=q.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",we,!1):n.attachEvent&&n.attachEvent("onunload",we)),b.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),b.getElementsByTagName=i(function(e){return e.appendChild(q.createComment("")),!e.getElementsByTagName("*").length}),b.getElementsByClassName=he.test(q.getElementsByClassName),b.getById=i(function(e){return L.appendChild(e).id=I,!q.getElementsByName||!q.getElementsByName(I).length}),b.getById?(w.filter.ID=function(e){var t=e.replace(ye,me);return function(e){return e.getAttribute("id")===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n=t.getElementById(e);return n?[n]:[]}}):(w.filter.ID=function(e){var t=e.replace(ye,me);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},w.find.ID=function(e,t){if(void 0!==t.getElementById&&H){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),w.find.TAG=b.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):b.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},w.find.CLASS=b.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&H)return t.getElementsByClassName(e)},P=[],O=[],(b.qsa=he.test(q.querySelectorAll))&&(i(function(e){L.appendChild(e).innerHTML="<a id='"+I+"'></a><select id='"+I+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&O.push("[*^$]="+ee+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||O.push("\\["+ee+"*(?:value|"+Z+")"),e.querySelectorAll("[id~="+I+"-]").length||O.push("~="),e.querySelectorAll(":checked").length||O.push(":checked"),e.querySelectorAll("a#"+I+"+*").length||O.push(".#.+[+~]")}),i(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=q.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&O.push("name"+ee+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&O.push(":enabled",":disabled"),L.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&O.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),O.push(",.*:")})),(b.matchesSelector=he.test(M=L.matches||L.webkitMatchesSelector||L.mozMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&i(function(e){b.disconnectedMatch=M.call(e,"*"),M.call(e,"[s!='']:x"),P.push("!=",re)}),O=O.length&&new RegExp(O.join("|")),P=P.length&&new RegExp(P.join("|")),t=he.test(L.compareDocumentPosition),R=t||he.test(L.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},X=t?function(e,t){if(e===t)return A=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!b.sortDetached&&t.compareDocumentPosition(e)===n?e===q||e.ownerDocument===W&&R(W,e)?-1:t===q||t.ownerDocument===W&&R(W,t)?1:N?K(N,e)-K(N,t):0:4&n?-1:1)}:function(e,t){if(e===t)return A=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===q?-1:t===q?1:i?-1:o?1:N?K(N,e)-K(N,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===W?-1:u[r]===W?1:0},q):q},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==q&&j(e),n=n.replace(ue,"='$1']"),b.matchesSelector&&H&&!z[n+" "]&&(!P||!P.test(n))&&(!O||!O.test(n)))try{var r=M.call(e,n);if(r||b.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return t(n,q,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==q&&j(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==q&&j(e);var n=w.attrHandle[t.toLowerCase()],r=n&&U.call(w.attrHandle,t.toLowerCase())?n(e,t,!H):void 0;return void 0!==r?r:b.attributes||!H?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.escape=function(e){return(e+"").replace(xe,be)},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(A=!b.detectDuplicates,N=!b.sortStable&&e.slice(0),e.sort(X),A){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return N=null,e},T=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=T(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=T(t);return n},w=t.selectors={cacheLength:50,createPseudo:r,match:fe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ye,me),e[3]=(e[3]||e[4]||e[5]||"").replace(ye,me),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return fe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&le.test(n)&&(t=E(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(ye,me).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=F[e+" "];return t||(t=new RegExp("(^|"+ee+")"+e+"("+ee+"|$)"))&&F(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ie," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,y=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(v){if(o){for(;g;){for(p=t;p=p[g];)if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){for(p=v,f=p[I]||(p[I]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===$&&l[1],x=d&&l[2],p=d&&v.childNodes[d];p=++d&&p&&p[g]||(x=d=0)||h.pop();)if(1===p.nodeType&&++x&&p===t){c[e]=[$,d,x];break}}else if(m&&(p=t,f=p[I]||(p[I]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===$&&l[1],x=d),!1===x)for(;(p=++d&&p&&p[g]||(x=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==y:1!==p.nodeType)||!++x||(m&&(f=p[I]||(p[I]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),c[e]=[$,x]),p!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=w.pseudos[e]||w.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[I]?o(n):o.length>1?(i=[e,e,"",n],w.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=K(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(oe,"$1"));return i[I]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(ye,me),function(t){return(t.textContent||t.innerText||T(t)).indexOf(e)>-1}}),lang:r(function(e){return ce.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(ye,me).toLowerCase(),function(t){var n;do{if(n=H?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===L},focus:function(e){return e===q.activeElement&&(!q.hasFocus||q.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:s(!1),disabled:s(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},
+empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!w.pseudos.empty(e)},header:function(e){return de.test(e.nodeName)},input:function(e){return pe.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[n<0?n+t:n]}),even:u(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},w.pseudos.nth=w.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})w.pseudos[x]=function(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}(x);for(x in{submit:!0,reset:!0})w.pseudos[x]=function(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}(x);return c.prototype=w.filters=w.pseudos,w.setFilters=new c,E=t.tokenize=function(e,n){var r,i,o,a,s,u,l,c=_[e+" "];if(c)return n?0:c.slice(0);for(s=e,u=[],l=w.preFilter;s;){r&&!(i=ae.exec(s))||(i&&(s=s.slice(i[0].length)||s),u.push(o=[])),r=!1,(i=se.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(oe," ")}),s=s.slice(r.length));for(a in w.filter)!(i=fe[a].exec(s))||l[a]&&!(i=l[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):_(e,u).slice(0)},k=t.compile=function(e,t){var n,r=[],i=[],o=z[e+" "];if(!o){for(t||(t=E(e)),n=t.length;n--;)o=y(t[n]),o[I]?r.push(o):i.push(o);o=z(e,m(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,u,c="function"==typeof e&&e,p=!r&&E(e=c.selector||e);if(n=n||[],1===p.length){if(o=p[0]=p[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&H&&w.relative[o[1].type]){if(!(t=(w.find.ID(a.matches[0].replace(ye,me),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=fe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!w.relative[s=a.type]);)if((u=w.find[s])&&(r=u(a.matches[0].replace(ye,me),ve.test(o[0].type)&&l(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&f(o)))return Q.apply(n,r),n;break}}return(c||k(e,p))(r,t,!H,n,!t||ve.test(e)&&l(t.parentNode)||t),n},b.sortStable=I.split("").sort(X).join("")===I,b.detectDuplicates=!!A,j(),b.sortDetached=i(function(e){return 1&e.compareDocumentPosition(q.createElement("fieldset"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),b.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(Z,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);we.find=Ce,we.expr=Ce.selectors,we.expr[":"]=we.expr.pseudos,we.uniqueSort=we.unique=Ce.uniqueSort,we.text=Ce.getText,we.isXMLDoc=Ce.isXML,we.contains=Ce.contains,we.escapeSelector=Ce.escape;var Ee=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&we(e).is(n))break;r.push(e)}return r},ke=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},Se=we.expr.match.needsContext,De=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;we.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?we.find.matchesSelector(r,e)?[r]:[]:we.find.matches(e,we.grep(t,function(e){return 1===e.nodeType}))},we.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(we(e).filter(function(){for(t=0;t<r;t++)if(we.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)we.find(e,i[t],n);return r>1?we.uniqueSort(n):n},filter:function(e){return this.pushStack(a(this,e||[],!1))},not:function(e){return this.pushStack(a(this,e||[],!0))},is:function(e){return!!a(this,"string"==typeof e&&Se.test(e)?we(e):e||[],!1).length}});var Ne,Ae=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(we.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ne,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:Ae.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof we?t[0]:t,we.merge(this,we.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:ae,!0)),De.test(r[1])&&we.isPlainObject(t))for(r in t)me(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=ae.getElementById(r[2]),i&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):me(e)?void 0!==n.ready?n.ready(e):e(we):we.makeArray(e,this)}).prototype=we.fn,Ne=we(ae);var je=/^(?:parents|prev(?:Until|All))/,qe={children:!0,contents:!0,next:!0,prev:!0};we.fn.extend({has:function(e){var t=we(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(we.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&we(e);if(!Se.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&we.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?we.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?fe.call(we(e),this[0]):fe.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(we.uniqueSort(we.merge(this.get(),we(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),we.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return Ee(e,"parentNode")},parentsUntil:function(e,t,n){return Ee(e,"parentNode",n)},next:function(e){return s(e,"nextSibling")},prev:function(e){return s(e,"previousSibling")},nextAll:function(e){return Ee(e,"nextSibling")},prevAll:function(e){return Ee(e,"previousSibling")},nextUntil:function(e,t,n){return Ee(e,"nextSibling",n)},prevUntil:function(e,t,n){return Ee(e,"previousSibling",n)},siblings:function(e){return ke((e.parentNode||{}).firstChild,e)},children:function(e){return ke(e.firstChild)},contents:function(e){return o(e,"iframe")?e.contentDocument:(o(e,"template")&&(e=e.content||e),we.merge([],e.childNodes))}},function(e,t){we.fn[e]=function(n,r){var i=we.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=we.filter(r,i)),this.length>1&&(qe[e]||we.uniqueSort(i),je.test(e)&&i.reverse()),this.pushStack(i)}});var Le=/[^\x20\t\r\n\f]+/g;we.Callbacks=function(e){e="string"==typeof e?u(e):we.extend({},e);var t,n,i,o,a=[],s=[],l=-1,c=function(){for(o=o||e.once,i=t=!0;s.length;l=-1)for(n=s.shift();++l<a.length;)!1===a[l].apply(n[0],n[1])&&e.stopOnFalse&&(l=a.length,n=!1);e.memory||(n=!1),t=!1,o&&(a=n?[]:"")},f={add:function(){return a&&(n&&!t&&(l=a.length-1,s.push(n)),function t(n){we.each(n,function(n,i){me(i)?e.unique&&f.has(i)||a.push(i):i&&i.length&&"string"!==r(i)&&t(i)})}(arguments),n&&!t&&c()),this},remove:function(){return we.each(arguments,function(e,t){for(var n;(n=we.inArray(t,a,n))>-1;)a.splice(n,1),n<=l&&l--}),this},has:function(e){return e?we.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return o=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return o=s=[],n||t||(a=n=""),this},locked:function(){return!!o},fireWith:function(e,n){return o||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!i}};return f},we.extend({Deferred:function(t){var n=[["notify","progress",we.Callbacks("memory"),we.Callbacks("memory"),2],["resolve","done",we.Callbacks("once memory"),we.Callbacks("once memory"),0,"resolved"],["reject","fail",we.Callbacks("once memory"),we.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return we.Deferred(function(t){we.each(n,function(n,r){var i=me(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&me(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){function o(t,n,r,i){return function(){var s=this,u=arguments,f=function(){var e,f;if(!(t<a)){if((e=r.apply(s,u))===n.promise())throw new TypeError("Thenable self-resolution");f=e&&("object"==typeof e||"function"==typeof e)&&e.then,me(f)?i?f.call(e,o(a,n,l,i),o(a,n,c,i)):(a++,f.call(e,o(a,n,l,i),o(a,n,c,i),o(a,n,l,n.notifyWith))):(r!==l&&(s=void 0,u=[e]),(i||n.resolveWith)(s,u))}},p=i?f:function(){try{f()}catch(e){we.Deferred.exceptionHook&&we.Deferred.exceptionHook(e,p.stackTrace),t+1>=a&&(r!==c&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?p():(we.Deferred.getStackHook&&(p.stackTrace=we.Deferred.getStackHook()),e.setTimeout(p))}}var a=0;return we.Deferred(function(e){n[0][3].add(o(0,e,me(i)?i:l,e.notifyWith)),n[1][3].add(o(0,e,me(t)?t:l)),n[2][3].add(o(0,e,me(r)?r:c))}).promise()},promise:function(e){return null!=e?we.extend(e,i):i}},o={};return we.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=ue.call(arguments),o=we.Deferred(),a=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?ue.call(arguments):n,--t||o.resolveWith(r,i)}};if(t<=1&&(f(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||me(i[n]&&i[n].then)))return o.then();for(;n--;)f(i[n],a(n),o.reject);return o.promise()}});var He=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;we.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&He.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},we.readyException=function(t){e.setTimeout(function(){throw t})};var Oe=we.Deferred();we.fn.ready=function(e){return Oe.then(e).catch(function(e){we.readyException(e)}),this},we.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--we.readyWait:we.isReady)||(we.isReady=!0,!0!==e&&--we.readyWait>0||Oe.resolveWith(ae,[we]))}}),we.ready.then=Oe.then,"complete"===ae.readyState||"loading"!==ae.readyState&&!ae.documentElement.doScroll?e.setTimeout(we.ready):(ae.addEventListener("DOMContentLoaded",p),e.addEventListener("load",p));var Pe=function(e,t,n,i,o,a,s){var u=0,l=e.length,c=null==n;if("object"===r(n)){o=!0;for(u in n)Pe(e,t,u,n[u],!0,a,s)}else if(void 0!==i&&(o=!0,me(i)||(s=!0),c&&(s?(t.call(e,i),t=null):(c=t,t=function(e,t,n){return c.call(we(e),n)})),t))for(;u<l;u++)t(e[u],n,s?i:i.call(e[u],u,t(e[u],n)));return o?e:c?t.call(e):l?t(e[0],n):a},Me=/^-ms-/,Re=/-([a-z])/g,Ie=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};g.uid=1,g.prototype={cache:function(e){var t=e[this.expando];return t||(t={},Ie(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[h(t)]=n;else for(r in t)i[h(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][h(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){Array.isArray(t)?t=t.map(h):(t=h(t),t=t in r?[t]:t.match(Le)||[]),n=t.length;for(;n--;)delete r[t[n]]}(void 0===t||we.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!we.isEmptyObject(t)}};var We=new g,$e=new g,Be=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Fe=/[A-Z]/g;we.extend({hasData:function(e){return $e.hasData(e)||We.hasData(e)},data:function(e,t,n){return $e.access(e,t,n)},removeData:function(e,t){$e.remove(e,t)},_data:function(e,t,n){return We.access(e,t,n)},_removeData:function(e,t){We.remove(e,t)}}),we.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=$e.get(o),1===o.nodeType&&!We.get(o,"hasDataAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=h(r.slice(5)),y(o,r,i[r])));We.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){$e.set(this,e)}):Pe(this,function(t){var n;if(o&&void 0===t){if(void 0!==(n=$e.get(o,e)))return n;if(void 0!==(n=y(o,e)))return n}else this.each(function(){$e.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){$e.remove(this,e)})}}),we.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=We.get(e,t),n&&(!r||Array.isArray(n)?r=We.access(e,t,we.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=we.queue(e,t),r=n.length,i=n.shift(),o=we._queueHooks(e,t),a=function(){we.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return We.get(e,n)||We.access(e,n,{empty:we.Callbacks("once memory").add(function(){We.remove(e,[t+"queue",n])})})}}),we.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?we.queue(this[0],e):void 0===t?this:this.each(function(){var n=we.queue(this,e,t);we._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&we.dequeue(this,e)})},dequeue:function(e){return this.each(function(){we.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=we.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)(n=We.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var _e=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ze=new RegExp("^(?:([+-])=|)("+_e+")([a-z%]*)$","i"),Xe=["Top","Right","Bottom","Left"],Ue=function(e,t){return e=t||e,"none"===e.style.display||""===e.style.display&&we.contains(e.ownerDocument,e)&&"none"===we.css(e,"display")},Ve=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i},Ge={};we.fn.extend({show:function(){return b(this,!0)},hide:function(){return b(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Ue(this)?we(this).show():we(this).hide()})}});var Ye=/^(?:checkbox|radio)$/i,Qe=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,Je=/^$|^module$|\/(?:java|ecma)script/i,Ke={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};Ke.optgroup=Ke.option,Ke.tbody=Ke.tfoot=Ke.colgroup=Ke.caption=Ke.thead,Ke.th=Ke.td;var Ze=/<|&#?\w+;/;!function(){var e=ae.createDocumentFragment(),t=e.appendChild(ae.createElement("div")),n=ae.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),ye.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="<textarea>x</textarea>",ye.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var et=ae.documentElement,tt=/^key/,nt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,rt=/^([^.]*)(?:\.(.+)|)/;we.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=We.get(e);if(v)for(n.handler&&(o=n,n=o.handler,i=o.selector),i&&we.find.matchesSelector(et,i),n.guid||(n.guid=we.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(t){return void 0!==we&&we.event.triggered!==t.type?we.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(Le)||[""],l=t.length;l--;)s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d&&(f=we.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=we.event.special[d]||{},c=we.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&we.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||(p=u[d]=[],p.delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),we.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=We.hasData(e)&&We.get(e);if(v&&(u=v.events)){for(t=(t||"").match(Le)||[""],l=t.length;l--;)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){for(f=we.event.special[d]||{},d=(r?f.delegateType:f.bindType)||d,p=u[d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||we.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)we.event.remove(e,d+t[l],n,r,!0);we.isEmptyObject(u)&&We.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=we.event.fix(e),u=new Array(arguments.length),l=(We.get(this,"events")||{})[s.type]||[],c=we.event.special[s.type]||{};for(u[0]=s,t=1;t<arguments.length;t++)u[t]=arguments[t];if(s.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,s)){for(a=we.event.handlers.call(this,s,l),t=0;(i=a[t++])&&!s.isPropagationStopped();)for(s.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!s.isImmediatePropagationStopped();)s.rnamespace&&!s.rnamespace.test(o.namespace)||(s.handleObj=o,s.data=o.data,void 0!==(r=((we.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,u))&&!1===(s.result=r)&&(s.preventDefault(),s.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,s),s.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&e.button>=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)r=t[n],i=r.selector+" ",void 0===a[i]&&(a[i]=r.needsContext?we(i,this).index(l)>-1:we.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(e,t){Object.defineProperty(we.Event.prototype,e,{enumerable:!0,configurable:!0,get:me(t)?function(){if(this.originalEvent)return t(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[e]},set:function(t){Object.defineProperty(this,e,{enumerable:!0,configurable:!0,writable:!0,value:t})}})},fix:function(e){return e[we.expando]?e:new we.Event(e)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==S()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===S()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&o(this,"input"))return this.click(),!1},_default:function(e){return o(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},we.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},we.Event=function(e,t){if(!(this instanceof we.Event))return new we.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?E:k,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&we.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[we.expando]=!0},we.Event.prototype={constructor:we.Event,isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=E,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=E,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=E,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},we.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,char:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&tt.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&nt.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},we.event.addProp),we.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){we.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return i&&(i===r||we.contains(r,i))||(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),we.fn.extend({on:function(e,t,n,r){return D(this,e,t,n,r)},one:function(e,t,n,r){return D(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,we(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=k),this.each(function(){we.event.remove(this,e,n,t)})}});var it=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,ot=/<script|<style|<link/i,at=/checked\s*(?:[^=]|=\s*.checked.)/i,st=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;we.extend({htmlPrefilter:function(e){return e.replace(it,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=we.contains(e.ownerDocument,e);if(!(ye.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||we.isXMLDoc(e)))for(a=w(s),o=w(e),r=0,i=o.length;r<i;r++)L(o[r],a[r]);if(t)if(n)for(o=o||w(e),a=a||w(s),r=0,i=o.length;r<i;r++)q(o[r],a[r]);else q(e,s);return a=w(s,"script"),a.length>0&&T(a,!u&&w(e,"script")),s},cleanData:function(e){for(var t,n,r,i=we.event.special,o=0;void 0!==(n=e[o]);o++)if(Ie(n)){if(t=n[We.expando]){if(t.events)for(r in t.events)i[r]?we.event.remove(n,r):we.removeEvent(n,r,t.handle);n[We.expando]=void 0}n[$e.expando]&&(n[$e.expando]=void 0)}}}),we.fn.extend({detach:function(e){return O(this,e,!0)},remove:function(e){return O(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?we.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return H(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){N(this,e).appendChild(e)}})},prepend:function(){return H(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=N(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return H(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return H(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(we.cleanData(w(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return we.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ot.test(e)&&!Ke[(Qe.exec(e)||["",""])[1].toLowerCase()]){e=we.htmlPrefilter(e);try{for(;n<r;n++)t=this[n]||{},1===t.nodeType&&(we.cleanData(w(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return H(this,arguments,function(t){var n=this.parentNode;we.inArray(this,e)<0&&(we.cleanData(w(this)),n&&n.replaceChild(t,this))},e)}}),we.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){we.fn[e]=function(e){for(var n,r=[],i=we(e),o=i.length-1,a=0;a<=o;a++)n=a===o?this:this.clone(!0),we(i[a])[t](n),ce.apply(r,n.get());return this.pushStack(r)}});var ut=new RegExp("^("+_e+")(?!px)[a-z%]+$","i"),lt=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},ct=new RegExp(Xe.join("|"),"i");!function(){function t(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",et.appendChild(u).appendChild(l);var t=e.getComputedStyle(l);r="1%"!==t.top,s=12===n(t.marginLeft),l.style.right="60%",a=36===n(t.right),i=36===n(t.width),l.style.position="absolute",o=36===l.offsetWidth||"absolute",et.removeChild(u),l=null}}function n(e){return Math.round(parseFloat(e))}var r,i,o,a,s,u=ae.createElement("div"),l=ae.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",ye.clearCloneStyle="content-box"===l.style.backgroundClip,we.extend(ye,{boxSizingReliable:function(){return t(),i},pixelBoxStyles:function(){return t(),a},pixelPosition:function(){return t(),r},reliableMarginLeft:function(){return t(),s},scrollboxSize:function(){return t(),o}}))}();var ft=/^(none|table(?!-c[ea]).+)/,pt=/^--/,dt={position:"absolute",visibility:"hidden",display:"block"},ht={letterSpacing:"0",fontWeight:"400"},gt=["Webkit","Moz","ms"],vt=ae.createElement("div").style;we.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=P(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=h(t),u=pt.test(t),l=e.style;if(u||(t=I(s)),a=we.cssHooks[t]||we.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];o=typeof n,"string"===o&&(i=ze.exec(n))&&i[1]&&(n=m(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(we.cssNumber[s]?"":"px")),ye.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=h(t);return pt.test(t)||(t=I(s)),a=we.cssHooks[t]||we.cssHooks[s],a&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=P(e,t,r)),"normal"===i&&t in ht&&(i=ht[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),we.each(["height","width"],function(e,t){we.cssHooks[t]={get:function(e,n,r){if(n)return!ft.test(we.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?B(e,t,r):Ve(e,dt,function(){return B(e,t,r)})},set:function(e,n,r){var i,o=lt(e),a="border-box"===we.css(e,"boxSizing",!1,o),s=r&&$(e,t,r,a,o);return a&&ye.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-$(e,t,"border",!1,o)-.5)),s&&(i=ze.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=we.css(e,t)),W(e,n,s)}}}),we.cssHooks.marginLeft=M(ye.reliableMarginLeft,function(e,t){if(t)return(parseFloat(P(e,"marginLeft"))||e.getBoundingClientRect().left-Ve(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),we.each({margin:"",padding:"",border:"Width"},function(e,t){we.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Xe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(we.cssHooks[e+t].set=W)}),we.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=lt(e),i=t.length;a<i;a++)o[t[a]]=we.css(e,t[a],!1,r);return o}return void 0!==n?we.style(e,t,n):we.css(e,t)},e,t,arguments.length>1)}}),we.Tween=F,F.prototype={constructor:F,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||we.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(we.cssNumber[n]?"":"px")},cur:function(){var e=F.propHooks[this.prop];return e&&e.get?e.get(this):F.propHooks._default.get(this)},run:function(e){var t,n=F.propHooks[this.prop];return this.options.duration?this.pos=t=we.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):F.propHooks._default.set(this),this}},F.prototype.init.prototype=F.prototype,F.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=we.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){we.fx.step[e.prop]?we.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[we.cssProps[e.prop]]&&!we.cssHooks[e.prop]?e.elem[e.prop]=e.now:we.style(e.elem,e.prop,e.now+e.unit)}}},F.propHooks.scrollTop=F.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},we.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},we.fx=F.prototype.init,we.fx.step={};var yt,mt,xt=/^(?:toggle|show|hide)$/,bt=/queueHooks$/;we.Animation=we.extend(Y,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return m(n.elem,e,ze.exec(t),n),n}]},tweener:function(e,t){me(e)?(t=e,e=["*"]):e=e.match(Le);for(var n,r=0,i=e.length;r<i;r++)n=e[r],Y.tweeners[n]=Y.tweeners[n]||[],Y.tweeners[n].unshift(t)},prefilters:[V],prefilter:function(e,t){t?Y.prefilters.unshift(e):Y.prefilters.push(e)}}),we.speed=function(e,t,n){var r=e&&"object"==typeof e?we.extend({},e):{complete:n||!n&&t||me(e)&&e,duration:e,easing:n&&t||t&&!me(t)&&t};return we.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in we.fx.speeds?r.duration=we.fx.speeds[r.duration]:r.duration=we.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){me(r.old)&&r.old.call(this),r.queue&&we.dequeue(this,r.queue)},r},we.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Ue).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=we.isEmptyObject(e),o=we.speed(t,n,r),a=function(){var t=Y(this,we.extend({},e),o);(i||We.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&!1!==e&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=we.timers,a=We.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&bt.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));!t&&n||we.dequeue(this,e)})},finish:function(e){return!1!==e&&(e=e||"fx"),this.each(function(){
+var t,n=We.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=we.timers,a=r?r.length:0;for(n.finish=!0,we.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;t<a;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),we.each(["toggle","show","hide"],function(e,t){var n=we.fn[t];we.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(X(t,!0),e,r,i)}}),we.each({slideDown:X("show"),slideUp:X("hide"),slideToggle:X("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){we.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),we.timers=[],we.fx.tick=function(){var e,t=0,n=we.timers;for(yt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||we.fx.stop(),yt=void 0},we.fx.timer=function(e){we.timers.push(e),we.fx.start()},we.fx.interval=13,we.fx.start=function(){mt||(mt=!0,_())},we.fx.stop=function(){mt=null},we.fx.speeds={slow:600,fast:200,_default:400},we.fn.delay=function(t,n){return t=we.fx?we.fx.speeds[t]||t:t,n=n||"fx",this.queue(n,function(n,r){var i=e.setTimeout(n,t);r.stop=function(){e.clearTimeout(i)}})},function(){var e=ae.createElement("input"),t=ae.createElement("select"),n=t.appendChild(ae.createElement("option"));e.type="checkbox",ye.checkOn=""!==e.value,ye.optSelected=n.selected,e=ae.createElement("input"),e.value="t",e.type="radio",ye.radioValue="t"===e.value}();var wt,Tt=we.expr.attrHandle;we.fn.extend({attr:function(e,t){return Pe(this,we.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){we.removeAttr(this,e)})}}),we.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?we.prop(e,t,n):(1===o&&we.isXMLDoc(e)||(i=we.attrHooks[t.toLowerCase()]||(we.expr.match.bool.test(t)?wt:void 0)),void 0!==n?null===n?void we.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=we.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!ye.radioValue&&"radio"===t&&o(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(Le);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),wt={set:function(e,t,n){return!1===t?we.removeAttr(e,n):e.setAttribute(n,n),n}},we.each(we.expr.match.bool.source.match(/\w+/g),function(e,t){var n=Tt[t]||we.find.attr;Tt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=Tt[a],Tt[a]=i,i=null!=n(e,t,r)?a:null,Tt[a]=o),i}});var Ct=/^(?:input|select|textarea|button)$/i,Et=/^(?:a|area)$/i;we.fn.extend({prop:function(e,t){return Pe(this,we.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[we.propFix[e]||e]})}}),we.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&we.isXMLDoc(e)||(t=we.propFix[t]||t,i=we.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=we.find.attr(e,"tabindex");return t?parseInt(t,10):Ct.test(e.nodeName)||Et.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),ye.optSelected||(we.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),we.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){we.propFix[this.toLowerCase()]=this}),we.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(me(e))return this.each(function(t){we(this).addClass(e.call(this,t,J(this)))});if(t=K(e),t.length)for(;n=this[u++];)if(i=J(n),r=1===n.nodeType&&" "+Q(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=Q(r),i!==s&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(me(e))return this.each(function(t){we(this).removeClass(e.call(this,t,J(this)))});if(!arguments.length)return this.attr("class","");if(t=K(e),t.length)for(;n=this[u++];)if(i=J(n),r=1===n.nodeType&&" "+Q(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=Q(r),i!==s&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):me(e)?this.each(function(n){we(this).toggleClass(e.call(this,n,J(this),t),t)}):this.each(function(){var t,i,o,a;if(r)for(i=0,o=we(this),a=K(e);t=a[i++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||(t=J(this),t&&We.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":We.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+Q(J(n))+" ").indexOf(t)>-1)return!0;return!1}});var kt=/\r/g;we.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=me(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,we(this).val()):e,null==i?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=we.map(i,function(e){return null==e?"":e+""})),(t=we.valHooks[this.type]||we.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=we.valHooks[i.type]||we.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(kt,""):null==n?"":n)}}}),we.extend({valHooks:{option:{get:function(e){var t=we.find.attr(e,"value");return null!=t?t:Q(we.text(e))}},select:{get:function(e){var t,n,r,i=e.options,a=e.selectedIndex,s="select-one"===e.type,u=s?null:[],l=s?a+1:i.length;for(r=a<0?l:s?a:0;r<l;r++)if(n=i[r],(n.selected||r===a)&&!n.disabled&&(!n.parentNode.disabled||!o(n.parentNode,"optgroup"))){if(t=we(n).val(),s)return t;u.push(t)}return u},set:function(e,t){for(var n,r,i=e.options,o=we.makeArray(t),a=i.length;a--;)r=i[a],(r.selected=we.inArray(we.valHooks.option.get(r),o)>-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),we.each(["radio","checkbox"],function(){we.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=we.inArray(we(e).val(),t)>-1}},ye.checkOn||(we.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),ye.focusin="onfocusin"in e;var St=/^(?:focusinfocus|focusoutblur)$/,Dt=function(e){e.stopPropagation()};we.extend(we.event,{trigger:function(t,n,r,i){var o,a,s,u,l,c,f,p,d=[r||ae],h=he.call(t,"type")?t.type:t,g=he.call(t,"namespace")?t.namespace.split("."):[];if(a=p=s=r=r||ae,3!==r.nodeType&&8!==r.nodeType&&!St.test(h+we.event.triggered)&&(h.indexOf(".")>-1&&(g=h.split("."),h=g.shift(),g.sort()),l=h.indexOf(":")<0&&"on"+h,t=t[we.expando]?t:new we.Event(h,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:we.makeArray(n,[t]),f=we.event.special[h]||{},i||!f.trigger||!1!==f.trigger.apply(r,n))){if(!i&&!f.noBubble&&!xe(r)){for(u=f.delegateType||h,St.test(u+h)||(a=a.parentNode);a;a=a.parentNode)d.push(a),s=a;s===(r.ownerDocument||ae)&&d.push(s.defaultView||s.parentWindow||e)}for(o=0;(a=d[o++])&&!t.isPropagationStopped();)p=a,t.type=o>1?u:f.bindType||h,c=(We.get(a,"events")||{})[t.type]&&We.get(a,"handle"),c&&c.apply(a,n),(c=l&&a[l])&&c.apply&&Ie(a)&&(t.result=c.apply(a,n),!1===t.result&&t.preventDefault());return t.type=h,i||t.isDefaultPrevented()||f._default&&!1!==f._default.apply(d.pop(),n)||!Ie(r)||l&&me(r[h])&&!xe(r)&&(s=r[l],s&&(r[l]=null),we.event.triggered=h,t.isPropagationStopped()&&p.addEventListener(h,Dt),r[h](),t.isPropagationStopped()&&p.removeEventListener(h,Dt),we.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=we.extend(new we.Event,n,{type:e,isSimulated:!0});we.event.trigger(r,null,t)}}),we.fn.extend({trigger:function(e,t){return this.each(function(){we.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return we.event.trigger(e,t,n,!0)}}),ye.focusin||we.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){we.event.simulate(t,e.target,we.event.fix(e))};we.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=We.access(r,t);i||r.addEventListener(e,n,!0),We.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=We.access(r,t)-1;i?We.access(r,t,i):(r.removeEventListener(e,n,!0),We.remove(r,t))}}});var Nt=e.location,At=Date.now(),jt=/\?/;we.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||we.error("Invalid XML: "+t),n};var qt=/\[\]$/,Lt=/\r?\n/g,Ht=/^(?:submit|button|image|reset|file)$/i,Ot=/^(?:input|select|textarea|keygen)/i;we.param=function(e,t){var n,r=[],i=function(e,t){var n=me(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!we.isPlainObject(e))we.each(e,function(){i(this.name,this.value)});else for(n in e)Z(n,e[n],t,i);return r.join("&")},we.fn.extend({serialize:function(){return we.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=we.prop(this,"elements");return e?we.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!we(this).is(":disabled")&&Ot.test(this.nodeName)&&!Ht.test(e)&&(this.checked||!Ye.test(e))}).map(function(e,t){var n=we(this).val();return null==n?null:Array.isArray(n)?we.map(n,function(e){return{name:t.name,value:e.replace(Lt,"\r\n")}}):{name:t.name,value:n.replace(Lt,"\r\n")}}).get()}});var Pt=/%20/g,Mt=/#.*$/,Rt=/([?&])_=[^&]*/,It=/^(.*?):[ \t]*([^\r\n]*)$/gm,Wt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,$t=/^(?:GET|HEAD)$/,Bt=/^\/\//,Ft={},_t={},zt="*/".concat("*"),Xt=ae.createElement("a");Xt.href=Nt.href,we.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Nt.href,type:"GET",isLocal:Wt.test(Nt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":we.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ne(ne(e,we.ajaxSettings),t):ne(we.ajaxSettings,e)},ajaxPrefilter:ee(Ft),ajaxTransport:ee(_t),ajax:function(t,n){function r(t,n,r,s){var l,p,d,b,w,T=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",C.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=re(h,C,r)),b=ie(h,b,C,l),l?(h.ifModified&&(w=C.getResponseHeader("Last-Modified"),w&&(we.lastModified[o]=w),(w=C.getResponseHeader("etag"))&&(we.etag[o]=w)),204===t||"HEAD"===h.type?T="nocontent":304===t?T="notmodified":(T=b.state,p=b.data,d=b.error,l=!d)):(d=T,!t&&T||(T="error",t<0&&(t=0))),C.status=t,C.statusText=(n||T)+"",l?y.resolveWith(g,[p,T,C]):y.rejectWith(g,[C,T,d]),C.statusCode(x),x=void 0,f&&v.trigger(l?"ajaxSuccess":"ajaxError",[C,h,l?p:d]),m.fireWith(g,[C,T]),f&&(v.trigger("ajaxComplete",[C,h]),--we.active||we.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=we.ajaxSetup({},n),g=h.context||h,v=h.context&&(g.nodeType||g.jquery)?we(g):we.event,y=we.Deferred(),m=we.Callbacks("once memory"),x=h.statusCode||{},b={},w={},T="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=It.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)C.always(e[C.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(y.promise(C),h.url=((t||h.url||Nt.href)+"").replace(Bt,Nt.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(Le)||[""],null==h.crossDomain){l=ae.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Xt.protocol+"//"+Xt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=we.param(h.data,h.traditional)),te(Ft,h,n,C),c)return C;f=we.event&&h.global,f&&0==we.active++&&we.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!$t.test(h.type),o=h.url.replace(Mt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(Pt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(jt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Rt,"$1"),d=(jt.test(o)?"&":"?")+"_="+At+++d),h.url=o+d),h.ifModified&&(we.lastModified[o]&&C.setRequestHeader("If-Modified-Since",we.lastModified[o]),we.etag[o]&&C.setRequestHeader("If-None-Match",we.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&C.setRequestHeader("Content-Type",h.contentType),C.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+zt+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)C.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,C,h)||c))return C.abort();if(T="abort",m.add(h.complete),C.done(h.success),C.fail(h.error),i=te(_t,h,n,C)){if(C.readyState=1,f&&v.trigger("ajaxSend",[C,h]),c)return C;h.async&&h.timeout>0&&(u=e.setTimeout(function(){C.abort("timeout")},h.timeout));try{c=!1,i.send(b,r)}catch(e){if(c)throw e;r(-1,e)}}else r(-1,"No Transport");return C},getJSON:function(e,t,n){return we.get(e,t,n,"json")},getScript:function(e,t){return we.get(e,void 0,t,"script")}}),we.each(["get","post"],function(e,t){we[t]=function(e,n,r,i){return me(n)&&(i=i||r,r=n,n=void 0),we.ajax(we.extend({url:e,type:t,dataType:i,data:n,success:r},we.isPlainObject(e)&&e))}}),we._evalUrl=function(e){return we.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},we.fn.extend({wrapAll:function(e){var t;return this[0]&&(me(e)&&(e=e.call(this[0])),t=we(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return me(e)?this.each(function(t){we(this).wrapInner(e.call(this,t))}):this.each(function(){var t=we(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=me(e);return this.each(function(n){we(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){we(this).replaceWith(this.childNodes)}),this}}),we.expr.pseudos.hidden=function(e){return!we.expr.pseudos.visible(e)},we.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},we.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Ut={0:200,1223:204},Vt=we.ajaxSettings.xhr();ye.cors=!!Vt&&"withCredentials"in Vt,ye.ajax=Vt=!!Vt,we.ajaxTransport(function(t){var n,r;if(ye.cors||Vt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Ut[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),we.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),we.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return we.globalEval(e),e}}}),we.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),we.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=we("<script>").prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),ae.head.appendChild(t[0])},abort:function(){n&&n()}}}});var Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;we.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||we.expando+"_"+At++;return this[e]=!0,e}}),we.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=!1!==t.jsonp&&(Yt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=me(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(Yt,"$1"+i):!1!==t.jsonp&&(t.url+=(jt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||we.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?we(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,Gt.push(i)),a&&me(o)&&o(a[0]),a=o=void 0}),"script"}),ye.createHTMLDocument=function(){var e=ae.implementation.createHTMLDocument("").body;return e.innerHTML="<form></form><form></form>",2===e.childNodes.length}(),we.parseHTML=function(e,t,n){if("string"!=typeof e)return[];"boolean"==typeof t&&(n=t,t=!1);var r,i,o;return t||(ye.createHTMLDocument?(t=ae.implementation.createHTMLDocument(""),r=t.createElement("base"),r.href=ae.location.href,t.head.appendChild(r)):t=ae),i=De.exec(e),o=!n&&[],i?[t.createElement(i[1])]:(i=C([e],t,o),o&&o.length&&we(o).remove(),we.merge([],i.childNodes))},we.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=Q(e.slice(s)),e=e.slice(0,s)),me(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&we.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?we("<div>").append(we.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},we.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){we.fn[t]=function(e){return this.on(t,e)}}),we.expr.pseudos.animated=function(e){return we.grep(we.timers,function(t){return e===t.elem}).length},we.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=we.css(e,"position"),f=we(e),p={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=we.css(e,"top"),u=we.css(e,"left"),l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),me(t)&&(t=t.call(e,n,we.extend({},s))),null!=t.top&&(p.top=t.top-s.top+a),null!=t.left&&(p.left=t.left-s.left+i),"using"in t?t.using.call(e,p):f.css(p)}},we.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){we.offset.setOffset(this,e,t)});var t,n,r=this[0];if(r)return r.getClientRects().length?(t=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===we.css(r,"position"))t=r.getBoundingClientRect();else{for(t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===we.css(e,"position");)e=e.parentNode;e&&e!==r&&1===e.nodeType&&(i=we(e).offset(),i.top+=we.css(e,"borderTopWidth",!0),i.left+=we.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-we.css(r,"marginTop",!0),left:t.left-i.left-we.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===we.css(e,"position");)e=e.offsetParent;return e||et})}}),we.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;we.fn[e]=function(r){return Pe(this,function(e,r,i){var o;if(xe(e)?o=e:9===e.nodeType&&(o=e.defaultView),void 0===i)return o?o[t]:e[r];o?o.scrollTo(n?o.pageXOffset:i,n?i:o.pageYOffset):e[r]=i},e,r,arguments.length)}}),we.each(["top","left"],function(e,t){we.cssHooks[t]=M(ye.pixelPosition,function(e,n){if(n)return n=P(e,t),ut.test(n)?we(e).position()[t]+"px":n})}),we.each({Height:"height",Width:"width"},function(e,t){we.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){we.fn[r]=function(i,o){var a=arguments.length&&(n||"boolean"!=typeof i),s=n||(!0===i||!0===o?"margin":"border");return Pe(this,function(t,n,i){var o;return xe(t)?0===r.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(o=t.documentElement,Math.max(t.body["scroll"+e],o["scroll"+e],t.body["offset"+e],o["offset"+e],o["client"+e])):void 0===i?we.css(t,n,s):we.style(t,n,i,s)},t,a?i:void 0,a)}})}),we.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){we.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),we.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),we.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),we.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),me(e))return r=ue.call(arguments,2),i=function(){return e.apply(t||this,r.concat(ue.call(arguments)))},i.guid=e.guid=e.guid||we.guid++,i},we.holdReady=function(e){e?we.readyWait++:we.ready(!0)},we.isArray=Array.isArray,we.parseJSON=JSON.parse,we.nodeName=o,we.isFunction=me,we.isWindow=xe,we.camelCase=h,we.type=r,we.now=Date.now,we.isNumeric=function(e){var t=we.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},"function"==typeof define&&define.amd&&define("jquery",[],function(){return we});var Qt=e.jQuery,Jt=e.$;return we.noConflict=function(t){return e.$===we&&(e.$=Jt),t&&e.jQuery===we&&(e.jQuery=Qt),we},t||(e.jQuery=e.$=we),we}); })(this);
// 3rdParty/jquery-ui.js
(function (window, undefined) { !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(t){t.ui=t.ui||{};var e=(t.ui.version="1.12.1",0),i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,o,n;for(n=0;null!=(o=i[n]);n++)try{s=t._data(o,"events"),s&&s.remove&&t(o).triggerHandler("remove")}catch(t){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var o,n,r,a={},h=e.split(".")[0];e=e.split(".")[1];var l=h+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][l.toLowerCase()]=function(e){return!!t.data(e,l)},t[h]=t[h]||{},o=t[h][e],n=t[h][e]=function(t,e){if(!this._createWidget)return new n(t,e);arguments.length&&this._createWidget(t,e)},t.extend(n,o,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),r=new i,r.options=t.widget.extend({},r.options),t.each(s,function(e,s){if(!t.isFunction(s))return void(a[e]=s);a[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function o(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,n=this._superApply;return this._super=t,this._superApply=o,e=s.apply(this,arguments),this._super=i,this._superApply=n,e}}()}),n.prototype=t.widget.extend(r,{widgetEventPrefix:o?r.widgetEventPrefix||e:e},a,{constructor:n,namespace:h,widgetName:e,widgetFullName:l}),o?(t.each(o._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,n,i._proto)}),delete o._childConstructors):i._childConstructors.push(n),t.widget.bridge(e,n),n},t.widget.extend=function(e){for(var s,o,n=i.call(arguments,1),r=0,a=n.length;r<a;r++)for(s in n[r])o=n[r][s],n[r].hasOwnProperty(s)&&void 0!==o&&(t.isPlainObject(o)?e[s]=t.isPlainObject(e[s])?t.widget.extend({},e[s],o):t.widget.extend({},o):e[s]=o);return e},t.widget.bridge=function(e,s){var o=s.prototype.widgetFullName||e;t.fn[e]=function(n){var r="string"==typeof n,a=i.call(arguments,1),h=this;return r?this.length||"instance"!==n?this.each(function(){var i,s=t.data(this,o);return"instance"===n?(h=s,!1):s?t.isFunction(s[n])&&"_"!==n.charAt(0)?(i=s[n].apply(s,a),i!==s&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+n+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; attempted to call method '"+n+"'")}):h=void 0:(a.length&&(n=t.widget.extend.apply(null,[n].concat(a))),this.each(function(){var e=t.data(this,o);e?(e.option(n||{}),e._init&&e._init()):t.data(this,o,new s(n,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,o,n,r=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(r={},s=e.split("."),e=s.shift(),s.length){for(o=r[e]=t.widget.extend({},this.options[e]),n=0;n<s.length-1;n++)o[s[n]]=o[s[n]]||{},o=o[s[n]];if(e=s.pop(),1===arguments.length)return void 0===o[e]?null:o[e];o[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];r[e]=i}return this._setOptions(r),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,o;for(i in e)o=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&o&&o.length&&(s=t(o.get()),this._removeClass(o,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,n){var r,a;for(a=0;a<i.length;a++)r=o.classesElementLookup[i[a]]||t(),r=t(e.add?t.unique(r.get().concat(e.element.get())):r.not(e.element).get()),o.classesElementLookup[i[a]]=r,s.push(i[a]),n&&e.classes[i[a]]&&s.push(e.classes[i[a]])}var s=[],o=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,o){-1!==t.inArray(e.target,o)&&(i.classesElementLookup[s]=t(o.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var o="string"==typeof t||null===t,n={extra:o?e:i,keys:o?t:e,element:o?this.element:t,add:s};return n.element.toggleClass(this._classes(n),s),this},_on:function(e,i,s){var o,n=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=o=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,o=this.widget()),t.each(s,function(s,r){function a(){if(e||!0!==n.options.disabled&&!t(this).hasClass("ui-state-disabled"))return("string"==typeof r?n[r]:r).apply(n,arguments)}"string"!=typeof r&&(a.guid=r.guid=r.guid||a.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+n.eventNamespace,c=h[2];c?o.on(l,c,a):i.on(l,a)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var o,n,r=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],n=i.originalEvent)for(o in n)o in i||(i[o]=n[o]);return this.element.trigger(i,s),!(t.isFunction(r)&&!1===r.apply(this.element[0],[i].concat(s))||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,o,n){"string"==typeof o&&(o={effect:o});var r,a=o?!0===o||"number"==typeof o?i:o.effect||i:e;o=o||{},"number"==typeof o&&(o={duration:o}),r=!t.isEmptyObject(o),o.complete=n,o.delay&&s.delay(o.delay),r&&t.effects&&t.effects.effect[a]?s[e](o):a!==e&&s[a]?s[a](o.duration,o.easing,n):s.queue(function(i){t(this)[e](),n&&n.call(s[0]),i()})}});t.widget;!function(){function e(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var o,n=Math.max,r=Math.abs,a=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==o)return o;var e,i,s=t("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),n=s.children()[0];return t("body").append(s),e=n.offsetWidth,s.css("overflow","scroll"),i=n.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),o=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),o="scroll"===i||"auto"===i&&e.width<e.element[0].scrollWidth;return{width:"scroll"===s||"auto"===s&&e.height<e.element[0].scrollHeight?t.position.scrollbarWidth():0,height:o?t.position.scrollbarWidth():0}},getWithinInfo:function(e){var i=t(e||window),s=t.isWindow(i[0]),o=!!i[0]&&9===i[0].nodeType;return{element:i,isWindow:s,isDocument:o,offset:s||o?{left:0,top:0}:t(e).offset(),scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:i.outerWidth(),height:i.outerHeight()}}},t.fn.position=function(o){if(!o||!o.of)return f.apply(this,arguments);o=t.extend({},o);var p,u,d,g,m,v,_=t(o.of),b=t.position.getWithinInfo(o.within),w=t.position.getScrollInfo(b),y=(o.collision||"flip").split(" "),P={};return v=s(_),_[0].preventDefault&&(o.at="left top"),u=v.width,d=v.height,g=v.offset,m=t.extend({},g),t.each(["my","at"],function(){var t,e,i=(o[this]||"").split(" ");1===i.length&&(i=a.test(i[0])?i.concat(["center"]):h.test(i[0])?["center"].concat(i):["center","center"]),i[0]=a.test(i[0])?i[0]:"center",i[1]=h.test(i[1])?i[1]:"center",t=l.exec(i[0]),e=l.exec(i[1]),P[this]=[t?t[0]:0,e?e[0]:0],o[this]=[c.exec(i[0])[0],c.exec(i[1])[0]]}),1===y.length&&(y[1]=y[0]),"right"===o.at[0]?m.left+=u:"center"===o.at[0]&&(m.left+=u/2),"bottom"===o.at[1]?m.top+=d:"center"===o.at[1]&&(m.top+=d/2),p=e(P.at,u,d),m.left+=p[0],m.top+=p[1],this.each(function(){var s,a,h=t(this),l=h.outerWidth(),c=h.outerHeight(),f=i(this,"marginLeft"),v=i(this,"marginTop"),x=l+f+i(this,"marginRight")+w.width,C=c+v+i(this,"marginBottom")+w.height,z=t.extend({},m),H=e(P.my,h.outerWidth(),h.outerHeight());"right"===o.my[0]?z.left-=l:"center"===o.my[0]&&(z.left-=l/2),"bottom"===o.my[1]?z.top-=c:"center"===o.my[1]&&(z.top-=c/2),z.left+=H[0],z.top+=H[1],s={marginLeft:f,marginTop:v},t.each(["left","top"],function(e,i){t.ui.position[y[e]]&&t.ui.position[y[e]][i](z,{targetWidth:u,targetHeight:d,elemWidth:l,elemHeight:c,collisionPosition:s,collisionWidth:x,collisionHeight:C,offset:[p[0]+H[0],p[1]+H[1]],my:o.my,at:o.at,within:b,elem:h})}),o.using&&(a=function(t){var e=g.left-z.left,i=e+u-l,s=g.top-z.top,a=s+d-c,p={target:{element:_,left:g.left,top:g.top,width:u,height:d},element:{element:h,left:z.left,top:z.top,width:l,height:c},horizontal:i<0?"left":e>0?"right":"center",vertical:a<0?"top":s>0?"bottom":"middle"};u<l&&r(e+i)<u&&(p.horizontal="center"),d<c&&r(s+a)<d&&(p.vertical="middle"),n(r(e),r(i))>n(r(s),r(a))?p.important="horizontal":p.important="vertical",o.using.call(this,t,p)}),h.offset(t.extend(z,{using:a}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,o=s.isWindow?s.scrollLeft:s.offset.left,r=s.width,a=t.left-e.collisionPosition.marginLeft,h=o-a,l=a+e.collisionWidth-r-o;e.collisionWidth>r?h>0&&l<=0?(i=t.left+h+e.collisionWidth-r-o,t.left+=h-i):t.left=l>0&&h<=0?o:h>l?o+r-e.collisionWidth:o:h>0?t.left+=h:l>0?t.left-=l:t.left=n(t.left-a,t.left)},top:function(t,e){var i,s=e.within,o=s.isWindow?s.scrollTop:s.offset.top,r=e.within.height,a=t.top-e.collisionPosition.marginTop,h=o-a,l=a+e.collisionHeight-r-o;e.collisionHeight>r?h>0&&l<=0?(i=t.top+h+e.collisionHeight-r-o,t.top+=h-i):t.top=l>0&&h<=0?o:h>l?o+r-e.collisionHeight:o:h>0?t.top+=h:l>0?t.top-=l:t.top=n(t.top-a,t.top)}},flip:{left:function(t,e){var i,s,o=e.within,n=o.offset.left+o.scrollLeft,a=o.width,h=o.isWindow?o.scrollLeft:o.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,p=l+e.collisionWidth-a-h,f="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,u="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,d=-2*e.offset[0];c<0?((i=t.left+f+u+d+e.collisionWidth-a-n)<0||i<r(c))&&(t.left+=f+u+d):p>0&&((s=t.left-e.collisionPosition.marginLeft+f+u+d-h)>0||r(s)<p)&&(t.left+=f+u+d)},top:function(t,e){var i,s,o=e.within,n=o.offset.top+o.scrollTop,a=o.height,h=o.isWindow?o.scrollTop:o.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,p=l+e.collisionHeight-a-h,f="top"===e.my[1],u=f?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,d="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];c<0?((s=t.top+u+d+g+e.collisionHeight-a-n)<0||s<r(c))&&(t.top+=u+d+g):p>0&&((i=t.top-e.collisionPosition.marginTop+u+d+g-h)>0||r(i)<p)&&(t.top+=u+d+g)}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}();var s=(t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,o=e?/(auto|scroll|hidden)/:/(auto|scroll)/,n=this.parents().filter(function(){var e=t(this);return(!s||"static"!==e.css("position"))&&o.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&n.length?n:t(this[0].ownerDocument||document)},t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),!1);t(document).on("mouseup",function(){s=!1});t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){if(!0===t.data(i.target,e.widgetName+".preventClickEvent"))return t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,o=1===e.which,n=!("string"!=typeof this.options.cancel||!e.target.nodeName)&&t(e.target).closest(this.options.cancel).length;return!(o&&!n&&this._mouseCapture(e))||(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=!1!==this._mouseStart(e),!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0))}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||document.documentMode<9)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=!1!==this._mouseStart(this._mouseDownEvent,e),this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var o,n=t.ui[e].prototype;for(o in s)n.plugins[o]=n.plugins[o]||[],n.plugins[o].push([i,s[o]])},call:function(t,e,i,s){var o,n=t.plugins[e];if(n&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(o=0;o<n.length;o++)t.options[n[o][0]]&&n[o][1].apply(t.element,i)}},t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.ui.safeBlur=function(e){e&&"body"!==e.nodeName.toLowerCase()&&t(e).trigger("blur")};t.widget("ui.draggable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this._addClass("ui-draggable"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){if((this.helper||this.element).is(".ui-draggable-dragging"))return void(this.destroyOnClear=!0);this._removeHandleClassName(),this._mouseDestroy()},_mouseCapture:function(e){var i=this.options;return!(this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0)&&(this.handle=this._getHandle(e),!!this.handle&&(this._blurActiveElement(e),this._blockFrames(!0===i.iframeFix?"iframe":i.iframeFix),!0))},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t("<div>").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=t.ui.safeActiveElement(this.document[0]);t(e.target).closest(i).length||t.ui.safeBlur(i)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this._addClass(this.helper,"ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===t(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),!1===this._trigger("start",e)?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(!1===this._trigger("drag",e,s))return this._mouseUp(new t.Event("mouseup",e)),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||!0===this.options.revert||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){!1!==i._trigger("stop",e)&&i._clear()}):!1!==this._trigger("stop",e)&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.trigger("focus"),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp(new t.Event("mouseup",{target:this.element[0]})):this._clear(),this},_getHandle:function(e){return!this.options.handle||!!t(e.target).closest(this.element.find(this.options.handle)).length},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),o=s?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return o.parents("body").length||o.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&o[0]===this.element[0]&&this._setPositionRelative(),o[0]===this.element[0]||/(fixed|absolute)/.test(o.css("position"))||o.css("position","absolute"),o},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,o=this.options,n=this.document[0];return this.relativeContainer=null,o.containment?"window"===o.containment?void(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||n.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]):"document"===o.containment?void(this.containment=[0,0,t(n).width()-this.helperProportions.width-this.margins.left,(t(n).height()||n.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]):o.containment.constructor===Array?void(this.containment=o.containment):("parent"===o.containment&&(o.containment=this.helper[0].parentNode),i=t(o.containment),void((s=i[0])&&(e=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i))):void(this.containment=null)},_convertPositionTo:function(t,e){e||(e=this.position);var i="absolute"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,o,n,r=this.options,a=this._isRootNode(this.scrollParent[0]),h=t.pageX,l=t.pageY;return a&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),t.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),t.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),r.grid&&(o=r.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/r.grid[1])*r.grid[1]:this.originalPageY,l=i?o-this.offset.click.top>=i[1]||o-this.offset.click.top>i[3]?o:o-this.offset.click.top>=i[1]?o-r.grid[1]:o+r.grid[1]:o,n=r.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/r.grid[0])*r.grid[0]:this.originalPageX,h=i?n-this.offset.click.left>=i[0]||n-this.offset.click.left>i[2]?n:n-this.offset.click.left>=i[0]?n-r.grid[0]:n+r.grid[0]:n),"y"===r.axis&&(h=this.originalPageX),"x"===r.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:a?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:a?0:this.offset.scroll.left)}},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i,s){var o=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",e,o))})},stop:function(e,i,s){var o=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,o))})},drag:function(e,i,s){t.each(s.sortables,function(){var o=!1,n=this;n.positionAbs=s.positionAbs,n.helperProportions=s.helperProportions,n.offset.click=s.offset.click,n._intersectsWith(n.containerCache)&&(o=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==n&&this._intersectsWith(this.containerCache)&&t.contains(n.element[0],this.element[0])&&(o=!1),o})),o?(n.isOver||(n.isOver=1,s._parent=i.helper.parent(),n.currentItem=i.helper.appendTo(n.element).data("ui-sortable-item",!0),n.options._helper=n.options.helper,n.options.helper=function(){return i.helper[0]},e.target=n.currentItem[0],n._mouseCapture(e,!0),n._mouseStart(e,!0,!0),n.offset.click.top=s.offset.click.top,n.offset.click.left=s.offset.click.left,n.offset.parent.left-=s.offset.parent.left-n.offset.parent.left,
(function (window, undefined) { !function(){var e=!1,t=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){},Class.extend=function(n){function r(){!e&&this.init&&this.init.apply(this,arguments)}var i=this.prototype;e=!0;var o=new this;e=!1;for(var a in n)o[a]="function"==typeof n[a]&&"function"==typeof i[a]&&t.test(n[a])?function(e,t){return function(){var n=this._super;this._super=i[e];var r=t.apply(this,arguments);return this._super=n,r}}(a,n[a]):n[a];return r.prototype=o,r.prototype.constructor=r,r.extend=arguments.callee,r}}(),function(e,t){"use strict";function n(){}function r(e,t){if(e){"object"==typeof e&&(e=[].slice.call(e));for(var n=0,r=e.length;n<r;n++)t.call(e,e[n],n)}}function i(e,n){var r=Object.prototype.toString.call(n).slice(8,-1);return n!==t&&null!==n&&r===e}function o(e){return i("Function",e)}function a(e){return i("Array",e)}function l(e){var t=e.split("/"),n=t[t.length-1],r=n.indexOf("?");return-1!==r?n.substring(0,r):n}function u(e){e=e||n,e._done||(e(),e._done=1)}function c(e,t,r,i){var o="object"==typeof e?e:{test:e,success:!!t&&(a(t)?t:[t]),failure:!!r&&(a(r)?r:[r]),callback:i||n},l=!!o.test;return l&&o.success?(o.success.push(o.callback),N.load.apply(null,o.success)):l||!o.failure?i():(o.failure.push(o.callback),N.load.apply(null,o.failure)),N}function s(e){var t,n,r={};if("object"==typeof e)for(t in e)!e[t]||(r={name:t,url:e[t]});else r={name:l(e),url:e};return(n=S[r.name])&&n.url===r.url?n:(S[r.name]=r,r)}function p(e){e=e||S;for(var t in e)if(e.hasOwnProperty(t)&&e[t].state!==F)return!1;return!0}function d(e){e.state=P,r(e.onpreload,function(e){e.call()})}function f(e){e.state===t&&(e.state=I,e.onpreload=[],b({url:e.url,type:"cache"},function(){d(e)}))}function h(){var e=arguments,t=e[e.length-1],n=[].slice.call(e,1),i=n[0];return o(t)||(t=null),a(e[0])?(e[0].push(t),N.load.apply(null,e[0]),N):(i?(r(n,function(e){o(e)||!e||f(s(e))}),v(s(e[0]),o(i)?i:function(){N.load.apply(null,n)})):v(s(e[0])),N)}function g(){var e=arguments,t=e[e.length-1],n={};return o(t)||(t=null),a(e[0])?(e[0].push(t),N.load.apply(null,e[0]),N):(r(e,function(e){e!==t&&(e=s(e),n[e.name]=e)}),r(e,function(e){e!==t&&(e=s(e),v(e,function(){p(n)&&u(t)}))}),N)}function v(e,t){return t=t||n,e.state===F?void t():e.state===D?void N.ready(e.name,t):e.state===I?void e.onpreload.push(function(){v(e,t)}):(e.state=D,void b(e,function(){e.state=F,t(),r(k[e.name],function(e){u(e)}),O&&p()&&r(k.ALL,function(e){u(e)})}))}function m(e){e=e||"";var t=e.split("?")[0].split(".");return t[t.length-1].toLowerCase()}function b(t,r){function i(t){t=t||e.event,l.onload=l.onreadystatechange=l.onerror=null,r()}function o(n){n=n||e.event,("load"===n.type||/loaded|complete/.test(l.readyState)&&(!x.documentMode||x.documentMode<9))&&(e.clearTimeout(t.errorTimeout),e.clearTimeout(t.cssTimeout),l.onload=l.onreadystatechange=l.onerror=null,r())}function a(){if(t.state!==F&&t.cssRetries<=20){for(var n=0,r=x.styleSheets.length;n<r;n++)if(x.styleSheets[n].href===l.href)return void o({type:"load"});t.cssRetries++,t.cssTimeout=e.setTimeout(a,250)}}var l,u,c;r=r||n,u=m(t.url),"css"===u?(l=x.createElement("link"),l.type="text/"+(t.type||"css"),l.rel="stylesheet",l.href=t.url,t.cssRetries=0,t.cssTimeout=e.setTimeout(a,500)):(l=x.createElement("script"),l.type="text/"+(t.type||"javascript"),l.src=t.url),l.onload=l.onreadystatechange=o,l.onerror=i,l.async=!1,l.defer=!1,t.errorTimeout=e.setTimeout(function(){i({type:"timeout"})},7e3),c=x.head||x.getElementsByTagName("head")[0],c.insertBefore(l,c.lastChild)}function w(){for(var e,t=x.getElementsByTagName("script"),n=0,r=t.length;n<r;n++)if(!!(e=t[n].getAttribute("data-headjs-load")))return void N.load(e)}function y(e,t){var n,i,l;return e===x?(O?u(t):L.push(t),N):(o(e)&&(t=e,e="ALL"),a(e)?(n={},r(e,function(e){n[e]=S[e],N.ready(e,function(){p(n)&&u(t)})}),N):"string"==typeof e&&o(t)?(i=S[e])&&i.state===F||"ALL"===e&&p()&&O?(u(t),N):(l=k[e],l?l.push(t):l=k[e]=[t],N):N)}function T(){if(!x.body)return e.clearTimeout(N.readyTimeout),void(N.readyTimeout=e.setTimeout(T,50));O||(O=!0,w(),r(L,function(e){u(e)}))}function E(){x.addEventListener?(x.removeEventListener("DOMContentLoaded",E,!1),T()):"complete"===x.readyState&&(x.detachEvent("onreadystatechange",E),T())}var O,A,x=e.document,L=[],k={},S={},C="async"in x.createElement("script")||"MozAppearance"in x.documentElement.style||e.opera,M=e.head_conf&&e.head_conf.head||"head",N=e[M]=e[M]||function(){N.ready.apply(null,arguments)},I=1,P=2,D=3,F=4;if("complete"===x.readyState)T();else if(x.addEventListener)x.addEventListener("DOMContentLoaded",E,!1),e.addEventListener("load",T,!1);else{x.attachEvent("onreadystatechange",E),e.attachEvent("onload",T),A=!1;try{A=!e.frameElement&&x.documentElement}catch(e){}A&&A.doScroll&&function t(){if(!O){try{A.doScroll("left")}catch(n){return e.clearTimeout(N.readyTimeout),void(N.readyTimeout=e.setTimeout(t,50))}T()}}()}N.load=N.js=C?g:h,N.test=c,N.ready=y,N.ready(x,function(){p()&&r(k.ALL,function(e){u(e)}),N.feature&&N.feature("domloaded",!0)})}(window),function(e){"function"==typeof define&&define.amd&&define.amd.jQuery?define(["jquery"],e):e(jQuery)}(function(e){function t(t){return!t||void 0!==t.allowPageScroll||void 0===t.swipe&&void 0===t.swipeStatus||(t.allowPageScroll=c),void 0!==t.click&&void 0===t.tap&&(t.tap=t.click),t||(t={}),t=e.extend({},e.fn.swipe.defaults,t),this.each(function(){var r=e(this),i=r.data(k);i||(i=new n(this,t),r.data(k,i))})}function n(t,n){function S(t){if(!(ce()||e(t.target).closest(n.excludedElements,Xe).length>0)){var r,i=t.originalEvent?t.originalEvent:t,o=A?i.touches[0]:i;return Qe=y,(A?Ye=i.touches.length:t.preventDefault(),Fe=0,ze=null,Ue=null,Re=0,_e=0,je=0,Ve=1,qe=0,Be=he(),He=me(),le(),!A||Ye===n.fingers||n.fingers===b||H()?(pe(0,o),We=Le(),2==Ye&&(pe(1,i.touches[1]),_e=je=ye(Be[0].start,Be[1].start)),(n.swipeStatus||n.pinchStatus)&&(r=F(i,Qe))):r=!1,!1===r)?(Qe=O,F(i,Qe),r):(n.hold&&(et=setTimeout(e.proxy(function(){Xe.trigger("hold",[i.target]),n.hold&&(r=n.hold.call(Xe,i,i.target))},this),n.longTapThreshold)),se(!0),null)}}function C(e){var t=e.originalEvent?e.originalEvent:e;if(Qe!==E&&Qe!==O&&!ue()){var r,i=A?t.touches[0]:t,o=de(i);if(Ze=Le(),A&&(Ye=t.touches.length),n.hold&&clearTimeout(et),Qe=T,2==Ye&&(0==_e?(pe(1,t.touches[1]),_e=je=ye(Be[0].start,Be[1].start)):(de(t.touches[1]),je=ye(Be[0].end,Be[1].end),Ue=Ee(Be[0].end,Be[1].end)),Ve=Te(_e,je),qe=Math.abs(_e-je)),Ye===n.fingers||n.fingers===b||!A||H()){if(ze=xe(o.start,o.end),q(e,ze),Fe=Oe(o.start,o.end),Re=we(),ge(ze,Fe),(n.swipeStatus||n.pinchStatus)&&(r=F(t,Qe)),!n.triggerOnTouchEnd||n.triggerOnTouchLeave){var a=!0;if(n.triggerOnTouchLeave){var l=ke(this);a=Se(o.end,l)}!n.triggerOnTouchEnd&&a?Qe=D(T):n.triggerOnTouchLeave&&!a&&(Qe=D(E)),Qe!=O&&Qe!=E||F(t,Qe)}}else Qe=O,F(t,Qe);!1===r&&(Qe=O,F(t,Qe))}}function M(e){var t=e.originalEvent;return A&&t.touches.length>0?(ae(),!0):(ue()&&(Ye=Ke),Ze=Le(),Re=we(),_()||!R()?(Qe=O,F(t,Qe)):n.triggerOnTouchEnd||0==n.triggerOnTouchEnd&&Qe===T?(e.preventDefault(),Qe=E,F(t,Qe)):!n.triggerOnTouchEnd&&G()?(Qe=E,z(t,Qe,f)):Qe===T&&(Qe=O,F(t,Qe)),se(!1),null)}function N(){Ye=0,Ze=0,We=0,_e=0,je=0,Ve=1,le(),se(!1)}function I(e){var t=e.originalEvent;n.triggerOnTouchLeave&&(Qe=D(E),F(t,Qe))}function P(){Xe.unbind(Me,S),Xe.unbind(De,N),Xe.unbind(Ne,C),Xe.unbind(Ie,M),Pe&&Xe.unbind(Pe,I),se(!1)}function D(e){var t=e,r=V(),i=R(),o=_();return!r||o?t=O:!i||e!=T||n.triggerOnTouchEnd&&!n.triggerOnTouchLeave?!i&&e==E&&n.triggerOnTouchLeave&&(t=O):t=E,t}function F(e,t){var n=void 0;return B()||Y()?n=z(e,t,p):(X()||H())&&!1!==n&&(n=z(e,t,d)),ie()&&!1!==n?n=z(e,t,h):oe()&&!1!==n?n=z(e,t,g):re()&&!1!==n&&(n=z(e,t,f)),t===O&&N(e),t===E&&(A?0==e.touches.length&&N(e):N(e)),n}function z(t,c,s){var v=void 0;if(s==p){if(Xe.trigger("swipeStatus",[c,ze||null,Fe||0,Re||0,Ye,Be]),n.swipeStatus&&!1===(v=n.swipeStatus.call(Xe,t,c,ze||null,Fe||0,Re||0,Ye,Be)))return!1;if(c==E&&Q()){if(Xe.trigger("swipe",[ze,Fe,Re,Ye,Be]),n.swipe&&!1===(v=n.swipe.call(Xe,t,ze,Fe,Re,Ye,Be)))return!1;switch(ze){case r:Xe.trigger("swipeLeft",[ze,Fe,Re,Ye,Be]),n.swipeLeft&&(v=n.swipeLeft.call(Xe,t,ze,Fe,Re,Ye,Be));break;case i:Xe.trigger("swipeRight",[ze,Fe,Re,Ye,Be]),n.swipeRight&&(v=n.swipeRight.call(Xe,t,ze,Fe,Re,Ye,Be));break;case o:Xe.trigger("swipeUp",[ze,Fe,Re,Ye,Be]),n.swipeUp&&(v=n.swipeUp.call(Xe,t,ze,Fe,Re,Ye,Be));break;case a:Xe.trigger("swipeDown",[ze,Fe,Re,Ye,Be]),n.swipeDown&&(v=n.swipeDown.call(Xe,t,ze,Fe,Re,Ye,Be))}}}if(s==d){if(Xe.trigger("pinchStatus",[c,Ue||null,qe||0,Re||0,Ye,Ve,Be]),n.pinchStatus&&!1===(v=n.pinchStatus.call(Xe,t,c,Ue||null,qe||0,Re||0,Ye,Ve,Be)))return!1;if(c==E&&U())switch(Ue){case l:Xe.trigger("pinchIn",[Ue||null,qe||0,Re||0,Ye,Ve,Be]),n.pinchIn&&(v=n.pinchIn.call(Xe,t,Ue||null,qe||0,Re||0,Ye,Ve,Be));break;case u:Xe.trigger("pinchOut",[Ue||null,qe||0,Re||0,Ye,Ve,Be]),n.pinchOut&&(v=n.pinchOut.call(Xe,t,Ue||null,qe||0,Re||0,Ye,Ve,Be))}}return s==f?c!==O&&c!==E||(clearTimeout(Je),clearTimeout(et),K()&&!ee()?($e=Le(),Je=setTimeout(e.proxy(function(){$e=null,Xe.trigger("tap",[t.target]),n.tap&&(v=n.tap.call(Xe,t,t.target))},this),n.doubleTapThreshold)):($e=null,Xe.trigger("tap",[t.target]),n.tap&&(v=n.tap.call(Xe,t,t.target)))):s==h?c!==O&&c!==E||(clearTimeout(Je),$e=null,Xe.trigger("doubletap",[t.target]),n.doubleTap&&(v=n.doubleTap.call(Xe,t,t.target))):s==g&&(c!==O&&c!==E||(clearTimeout(Je),$e=null,Xe.trigger("longtap",[t.target]),n.longTap&&(v=n.longTap.call(Xe,t,t.target)))),v}function R(){var e=!0;return null!==n.threshold&&(e=Fe>=n.threshold),e}function _(){var e=!1;return null!==n.cancelThreshold&&null!==ze&&(e=ve(ze)-Fe>=n.cancelThreshold),e}function j(){return null===n.pinchThreshold||qe>=n.pinchThreshold}function V(){return!n.maxTimeThreshold||!(Re>=n.maxTimeThreshold)}function q(e,t){if(n.allowPageScroll===c||H())e.preventDefault();else{var l=n.allowPageScroll===s;switch(t){case r:(n.swipeLeft&&l||!l&&n.allowPageScroll!=v)&&e.preventDefault();break;case i:(n.swipeRight&&l||!l&&n.allowPageScroll!=v)&&e.preventDefault();break;case o:(n.swipeUp&&l||!l&&n.allowPageScroll!=m)&&e.preventDefault();break;case a:(n.swipeDown&&l||!l&&n.allowPageScroll!=m)&&e.preventDefault()}}}function U(){var e=W(),t=Z(),n=j();return e&&t&&n}function H(){return!!(n.pinchStatus||n.pinchIn||n.pinchOut)}function X(){return!(!U()||!H())}function Q(){var e=V(),t=R(),n=W(),r=Z();return!_()&&r&&n&&t&&e}function Y(){return!!(n.swipe||n.swipeStatus||n.swipeLeft||n.swipeRight||n.swipeUp||n.swipeDown)}function B(){return!(!Q()||!Y())}function W(){return Ye===n.fingers||n.fingers===b||!A}function Z(){return 0!==Be[0].end.x}function G(){return!!n.tap}function K(){return!!n.doubleTap}function $(){return!!n.longTap}function J(){if(null==$e)return!1;var e=Le();return K()&&e-$e<=n.doubleTapThreshold}function ee(){return J()}function te(){return(1===Ye||!A)&&(isNaN(Fe)||Fe<n.threshold)}function ne(){return Re>n.longTapThreshold&&Fe<w}function re(){return!(!te()||!G())}function ie(){return!(!J()||!K())}function oe(){return!(!ne()||!$())}function ae(){Ge=Le(),Ke=event.touches.length+1}function le(){Ge=0,Ke=0}function ue(){var e=!1;if(Ge){Le()-Ge<=n.fingerReleaseThreshold&&(e=!0)}return e}function ce(){return!(!0!==Xe.data(k+"_intouch"))}function se(e){!0===e?(Xe.bind(Ne,C),Xe.bind(Ie,M),Pe&&Xe.bind(Pe,I)):(Xe.unbind(Ne,C,!1),Xe.unbind(Ie,M,!1),Pe&&Xe.unbind(Pe,I,!1)),Xe.data(k+"_intouch",!0===e)}function pe(e,t){var n=void 0!==t.identifier?t.identifier:0;return Be[e].identifier=n,Be[e].start.x=Be[e].end.x=t.pageX||t.clientX,Be[e].start.y=Be[e].end.y=t.pageY||t.clientY,Be[e]}function de(e){var t=void 0!==e.identifier?e.identifier:0,n=fe(t);return n.end.x=e.pageX||e.clientX,n.end.y=e.pageY||e.clientY,n}function fe(e){for(var t=0;t<Be.length;t++)if(Be[t].identifier==e)return Be[t]}function he(){for(var e=[],t=0;t<=5;t++)e.push({start:{x:0,y:0},end:{x:0,y:0},identifier:0});return e}function ge(e,t){t=Math.max(t,ve(e)),He[e].distance=t}function ve(e){if(He[e])return He[e].distance}function me(){var e={};return e[r]=be(r),e[i]=be(i),e[o]=be(o),e[a]=be(a),e}function be(e){return{direction:e,distance:0}}function we(){return Ze-We}function ye(e,t){var n=Math.abs(e.x-t.x),r=Math.abs(e.y-t.y);return Math.round(Math.sqrt(n*n+r*r))}function Te(e,t){return(t/e*1).toFixed(2)}function Ee(){return Ve<1?u:l}function Oe(e,t){return Math.round(Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2)))}function Ae(e,t){var n=e.x-t.x,r=t.y-e.y,i=Math.atan2(r,n),o=Math.round(180*i/Math.PI);return o<0&&(o=360-Math.abs(o)),o}function xe(e,t){var n=Ae(e,t);return n<=45&&n>=0?r:n<=360&&n>=315?r:n>=135&&n<=225?i:n>45&&n<135?a:o}function Le(){return(new Date).getTime()}function ke(t){t=e(t);var n=t.offset();return{left:n.left,right:n.left+t.outerWidth(),top:n.top,bottom:n.top+t.outerHeight()}}function Se(e,t){return e.x>t.left&&e.x<t.right&&e.y>t.top&&e.y<t.bottom}var Ce=A||L||!n.fallbackToMouseEvents,Me=Ce?L?x?"MSPointerDown":"pointerdown":"touchstart":"mousedown",Ne=Ce?L?x?"MSPointerMove":"pointermove":"touchmove":"mousemove",Ie=Ce?L?x?"MSPointerUp":"pointerup":"touchend":"mouseup",Pe=Ce?null:"mouseleave",De=L?x?"MSPointerCancel":"pointercancel":"touchcancel",Fe=0,ze=null,Re=0,_e=0,je=0,Ve=1,qe=0,Ue=0,He=null,Xe=e(t),Qe="start",Ye=0,Be=null,We=0,Ze=0,Ge=0,Ke=0,$e=0,Je=null,et=null;try{Xe.bind(Me,S),Xe.bind(De,N)}catch(t){e.error("events not supported "+Me+","+De+" on jQuery.swipe")}this.enable=function(){return Xe.bind(Me,S),Xe.bind(De,N),Xe},this.disable=function(){return P(),Xe},this.destroy=function(){return P(),Xe.data(k,null),Xe},this.option=function(t,r){if(void 0!==n[t]){if(void 0===r)return n[t];n[t]=r}else e.error("Option "+t+" does not exist on jQuery.swipe.options");return null}}var r="left",i="right",o="up",a="down",l="in",u="out",c="none",s="auto",p="swipe",d="pinch",f="tap",h="doubletap",g="longtap",v="horizontal",m="vertical",b="all",w=10,y="start",T="move",E="end",O="cancel",A="ontouchstart"in window,x=window.navigator.msPointerEnabled&&!window.navigator.pointerEnabled,L=window.navigator.pointerEnabled||window.navigator.msPointerEnabled,k="TouchSwipe",S={fingers:1,threshold:75,cancelThreshold:null,pinchThreshold:20,maxTimeThreshold:null,fingerReleaseThreshold:250,longTapThreshold:500,doubleTapThreshold:200,swipe:null,swipeLeft:null,swipeRight:null,swipeUp:null,swipeDown:null,swipeStatus:null,pinchIn:null,pinchOut:null,pinchStatus:null,click:null,tap:null,doubleTap:null,longTap:null,hold:null,triggerOnTouchEnd:!0,triggerOnTouchLeave:!1,allowPageScroll:"auto",fallbackToMouseEvents:!0,excludedElements:"label, button, input, select, textarea, a, .noSwipe"};e.fn.swipe=function(n){var r=e(this),i=r.data(k);if(i&&"string"==typeof n){if(i[n])return i[n].apply(this,Array.prototype.slice.call(arguments,1));e.error("Method "+n+" does not exist on jQuery.swipe")}else if(!(i||"object"!=typeof n&&n))return t.apply(this,arguments);return r},e.fn.swipe.defaults=S,e.fn.swipe.phases={PHASE_START:y,PHASE_MOVE:T,PHASE_END:E,PHASE_CANCEL:O},e.fn.swipe.directions={LEFT:r,RIGHT:i,UP:o,DOWN:a,IN:l,OUT:u},e.fn.swipe.pageScroll={NONE:c,HORIZONTAL:v,VERTICAL:m,AUTO:s},e.fn.swipe.fingers={ONE:1,TWO:2,THREE:3,ALL:b}}),function(e){(jQuery.browser=jQuery.browser||{}).mobile=/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(e)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(e.substr(0,4))}(navigator.userAgent||navigator.vendor||window.opera),function(e){var t={init:function(){var t=["paddingTop","paddingRight","paddingBottom","paddingLeft","fontSize","lineHeight","fontFamily","width","fontWeight","border-top-width","border-right-width","border-bottom-width","border-left-width","-moz-box-sizing","-webkit-box-sizing","box-sizing"];return this.each(function(){function n(){for(var e=0;e<t.length;e++)a.css(t[e],o.css(t[e]))}function r(){var e=o.val().replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/\n/g,"<br/>");a.html(e+" ").css({width:parseInt(o.width(),10)+"px"}),i()}function i(){var e=a.height(),t="hidden",n=l?e+s+u:e+s;n>d?(n=d,t="auto"):p>n&&(n=p),o.height()!==n&&o.css({overflow:t,height:n+"px"})}if("textarea"!==this.type)return!1;var o=e(this).css({resize:"none",overflow:"hidden"}),a=e("<div></div>").css({position:"absolute",display:"none","word-wrap":"break-word","white-space":"pre-wrap","border-style":"solid"}).appendTo(document.body);n();var l="border-box"==o.css("box-sizing")||"border-box"==o.css("-moz-box-sizing")||"border-box"==o.css("-webkit-box-sizing"),u=parseInt(o.css("border-top-width"))+parseInt(o.css("padding-top"))+parseInt(o.css("padding-bottom"))+parseInt(o.css("border-bottom-width")),c=parseInt(o.css("height"),10),s=parseInt(o.css("line-height"),10)||parseInt(o.css("font-size"),10),p=2*s>c?2*s:c,d=parseInt(o.css("max-height"),10)>-1?parseInt(o.css("max-height"),10):Number.MAX_VALUE;o.bind("keyup change cut paste",function(){r()}),e(window).bind("resize",function(){a.width()!==parseInt(o.width(),10)&&r()}),o.bind("blur",function(){i()}),o.bind("updateHeight",function(){n(),r()}),e(function(){r()})})}};e.fn.flexible=function(n){return t[n]?t[n].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof n&&n?void e.error("Method "+n+" does not exist on jQuery.flexible"):t.init.apply(this,arguments)}}(jQuery),function(e,t,n,r){"use strict";function i(e,t){for(var n=0,r=e.length;n<r;n++)g(e[n],t)}function o(e){for(var t,n=0,r=e.length;n<r;n++)t=e[n],E(t,_[l(t)])}function a(e){return function(t){ne(t)&&(g(t,e),i(t.querySelectorAll(j),e))}}function l(e){var t=e.getAttribute("is"),n=e.nodeName.toUpperCase(),r=q.call(R,t?D+t.toUpperCase():P+n);return t&&-1<r&&!u(n,t)?-1:r}function u(e,t){return-1<j.indexOf(e+'[is="'+t+'"]')}function c(e){var t=e.currentTarget,n=e.attrChange,r=e.attrName,i=e.target;he&&(!i||i===t)&&t.attributeChangedCallback&&"style"!==r&&t.attributeChangedCallback(r,n===e[k]?null:e.prevValue,n===e[C]?null:e.newValue)}function s(e){var t=a(e);return function(e){v.push(t,e.target)}}function p(e){fe&&(fe=!1,e.currentTarget.removeEventListener(N,p)),i((e.target||t).querySelectorAll(j),e.detail===x?x:A),te&&h()}function d(e,t){var n=this;oe.call(n,e,t),m.call(n,{target:n})}function f(e,t){$(e,t),y?y.observe(e,ue):(de&&(e.setAttribute=d,e[O]=w(e),e.addEventListener(I,m)),e.addEventListener(M,c)),e.createdCallback&&he&&(e.created=!0,e.createdCallback(),e.created=!1)}function h(){for(var e,t=0,n=re.length;t<n;t++)e=re[t],V.contains(e)||(re.splice(t,1),g(e,x))}function g(e,t){var n,r=l(e);-1<r&&(T(e,_[r]),r=0,t!==A||e[A]?t===x&&!e[x]&&(e[A]=!1,e[x]=!0,r=1):(e[x]=!1,e[A]=!0,r=1,te&&q.call(re,e)<0&&re.push(e)),r&&(n=e[t+"Callback"])&&n.call(e))}if(!(r in t)){var v,m,b,w,y,T,E,O="__"+r+(1e5*Math.random()>>0),A="attached",x="detached",L="extends",k="ADDITION",S="MODIFICATION",C="REMOVAL",M="DOMAttrModified",N="DOMContentLoaded",I="DOMSubtreeModified",P="<",D="=",F=/^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/,z=["ANNOTATION-XML","COLOR-PROFILE","FONT-FACE","FONT-FACE-SRC","FONT-FACE-URI","FONT-FACE-FORMAT","FONT-FACE-NAME","MISSING-GLYPH"],R=[],_=[],j="",V=t.documentElement,q=R.indexOf||function(e){for(var t=this.length;t--&&this[t]!==e;);return t},U=n.prototype,H=U.hasOwnProperty,X=U.isPrototypeOf,Q=n.defineProperty,Y=n.getOwnPropertyDescriptor,B=n.getOwnPropertyNames,W=n.getPrototypeOf,Z=n.setPrototypeOf,G=!!n.__proto__,K=n.create||function e(t){return t?(e.prototype=t,new e):this},$=Z||(G?function(e,t){return e.__proto__=t,e}:B&&Y?function(){function e(e,t){for(var n,r=B(t),i=0,o=r.length;i<o;i++)n=r[i],H.call(e,n)||Q(e,n,Y(t,n))}return function(t,n){do{e(t,n)}while((n=W(n))&&!X.call(n,t));return t}}():function(e,t){for(var n in t)e[n]=t[n];return e}),J=e.MutationObserver||e.WebKitMutationObserver,ee=(e.HTMLElement||e.Element||e.Node).prototype,te=!X.call(ee,V),ne=te?function(e){return 1===e.nodeType}:function(e){return X.call(ee,e)},re=te&&[],ie=ee.cloneNode,oe=ee.setAttribute,ae=ee.removeAttribute,le=t.createElement,ue=J&&{attributes:!0,characterData:!0,attributeOldValue:!0},ce=J||function(e){de=!1,V.removeEventListener(M,ce)},se=e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.msRequestAnimationFrame||function(e){setTimeout(e,10)},pe=!1,de=!0,fe=!0,he=!0;Z||G?(T=function(e,t){X.call(t,e)||f(e,t)},E=f):(T=function(e,t){e[O]||(e[O]=n(!0),f(e,t))},E=T),te?(de=!1,function(){var e=Y(ee,"addEventListener"),t=e.value,n=function(e){var t=new CustomEvent(M,{bubbles:!0});t.attrName=e,t.prevValue=this.getAttribute(e),t.newValue=null,t[C]=t.attrChange=2,ae.call(this,e),this.dispatchEvent(t)},r=function(e,t){var n=this.hasAttribute(e),r=n&&this.getAttribute(e),i=new CustomEvent(M,{bubbles:!0});oe.call(this,e,t),i.attrName=e,i.prevValue=n?r:null,i.newValue=t,n?i[S]=i.attrChange=1:i[k]=i.attrChange=0,this.dispatchEvent(i)},i=function(e){var t,n=e.currentTarget,r=n[O],i=e.propertyName;r.hasOwnProperty(i)&&(r=r[i],t=new CustomEvent(M,{bubbles:!0}),t.attrName=r.name,t.prevValue=r.value||null,t.newValue=r.value=n[i]||null,null==t.prevValue?t[k]=t.attrChange=0:t[S]=t.attrChange=1,n.dispatchEvent(t))};e.value=function(e,o,a){e===M&&this.attributeChangedCallback&&this.setAttribute!==r&&(this[O]={className:{name:"class",value:this.className}},this.setAttribute=r,this.removeAttribute=n,t.call(this,"propertychange",i)),t.call(this,e,o,a)},Q(ee,"addEventListener",e)}()):J||(V.addEventListener(M,ce),V.setAttribute(O,1),V.removeAttribute(O),de&&(m=function(e){var t,n,r,i=this;if(i===e.target){t=i[O],i[O]=n=w(i);for(r in n){if(!(r in t))return b(0,i,r,t[r],n[r],k);if(n[r]!==t[r])return b(1,i,r,t[r],n[r],S)}for(r in t)if(!(r in n))return b(2,i,r,t[r],n[r],C)}},b=function(e,t,n,r,i,o){var a={attrChange:e,currentTarget:t,attrName:n,prevValue:r,newValue:i};a[o]=e,c(a)},w=function(e){for(var t,n,r={},i=e.attributes,o=0,a=i.length;o<a;o++)t=i[o],"setAttribute"!==(n=t.name)&&(r[n]=t.value);return r})),t[r]=function(e,n){if(r=e.toUpperCase(),pe||(pe=!0,J?(y=function(e,t){function n(e,t){for(var n=0,r=e.length;n<r;t(e[n++]));}return new J(function(r){for(var i,o,a=0,l=r.length;a<l;a++)i=r[a],"childList"===i.type?(n(i.addedNodes,e),n(i.removedNodes,t)):(o=i.target,he&&o.attributeChangedCallback&&"style"!==i.attributeName&&o.attributeChangedCallback(i.attributeName,i.oldValue,o.getAttribute(i.attributeName)))})}(a(A),a(x)),y.observe(t,{childList:!0,subtree:!0})):(v=[],se(function e(){for(;v.length;)v.shift().call(null,v.shift());se(e)}),t.addEventListener("DOMNodeInserted",s(A)),t.addEventListener("DOMNodeRemoved",s(x))),t.addEventListener(N,p),t.addEventListener("readystatechange",p),t.createElement=function(e,n){var r=le.apply(t,arguments),i=""+e,o=q.call(R,(n?D:P)+(n||i).toUpperCase()),a=-1<o;return n&&(r.setAttribute("is",n=n.toLowerCase()),a&&(a=u(i.toUpperCase(),n))),he=!t.createElement.innerHTMLHelper,a&&E(r,_[o]),r},ee.cloneNode=function(e){var t=ie.call(this,!!e),n=l(t);return-1<n&&E(t,_[n]),e&&o(t.querySelectorAll(j)),t}),-2<q.call(R,D+r)+q.call(R,P+r))throw new Error("A "+e+" type is already registered");if(!F.test(r)||-1<q.call(z,r))throw new Error("The type "+e+" is invalid");var r,c=function(){return f?t.createElement(h,r):t.createElement(h)},d=n||U,f=H.call(d,L),h=f?n[L].toUpperCase():r,g=R.push((f?D:P)+r)-1;return j=j.concat(j.length?",":"",f?h+'[is="'+e.toLowerCase()+'"]':h),c.prototype=_[g]=H.call(d,"prototype")?d.prototype:K(ee),i(t.querySelectorAll(j),A),c}}}(window,document,Object,"registerElement"),function(e,t,n){"use strict";function r(){return e.performance!==n&&e.performance.now!==n?e.performance.now():Date.now()}function i(e){return.5*(1-Math.cos(Math.PI*e))}function o(e){if("object"!=typeof e||e.behavior===n||"auto"===e.behavior||"instant"===e.behavior)return!0;if("smooth"===e.behavior)return!1;throw new TypeError(e.behavior+" is not a valid value for enumeration ScrollBehavior")}function a(e,t,n){e.scrollTop=n,e.scrollLeft=t}function l(t,o){function a(){var f,h,g,v=r(),m=(v-d)/s;return m=m>1?1:m,f=i(m),h=l+(t-l)*f,g=u+(o-u)*f,p(h,g),h===t&&g===o?(l=u=d=null,e.cancelAnimationFrame(c),n):(c=e.requestAnimationFrame(a),n)}var l=e.scrollX||e.pageXOffset,u=e.scrollY||e.pageYOffset,d=r();c&&e.cancelAnimationFrame(c),c=e.requestAnimationFrame(a)}function u(o,u){function p(){var t,l,u,m=r(),b=(m-v)/s;return b=b>1?1:b,t=i(b),l=d+(h-d)*t,u=f+(g-f)*t,a(o,l,u),l===h&&u===g?(d=f=v=null,e.cancelAnimationFrame(c),n):(c=e.requestAnimationFrame(p),n)}if(o===t.documentElement||o===t.body)return l(u.left,u.top),n;var d=o.scrollLeft,f=o.scrollTop,h=u.left,g=u.top,v=r();c&&e.cancelAnimationFrame(c),c=e.requestAnimationFrame(p)}if(!("scrollBehavior"in t.documentElement.style)){var c,s=768,p=e.scrollTo,d=e.scrollBy,f=e.Element.prototype.scrollIntoView;e.scroll=e.scrollTo=function(){return o(arguments[0])?p.call(e,arguments[0].left||arguments[0],arguments[0].top||arguments[1]):l.call(e,~~arguments[0].left,~~arguments[0].top)},e.scrollBy=function(){if(o(arguments[0]))return d.call(e,arguments[0].left||arguments[0],arguments[0].top||arguments[1]);var t=e.scrollX||e.pageXOffset,n=e.scrollY||e.pageYOffset;return l(~~arguments[0].left+t,~~arguments[0].top+n)},Element.prototype.scrollIntoView=function(){var n,r,i,a;return o(arguments[0])?f.call(this,arguments[0]||!0):(a=e.getComputedStyle(t.body,null),r=parseInt(a.getPropertyValue("padding-left"),10),i=parseInt(a.getPropertyValue("padding-top"),10),n={top:this.offsetTop-2*i,left:this.offsetLeft-2*r},u(t.body,n))}}}(window,document); })(this);
// WCF.js
-(function (window, undefined) { "use strict";function wcfEval(expression){return eval(expression)}!function(){var e=jQuery.fn.data;jQuery.fn.data=function(t,i){var n=[].slice.call(arguments);if(t)switch(typeof t){case"object":for(var s in t)if(s.match(/ID$/)){var o=t[s];delete t[s],s=s.replace(/ID$/,"-id"),t[s]=o}n[0]=t;break;case"string":t.match(/ID$/)&&(n[0]=t.replace(/ID$/,"-id"))}var a=e.apply(this,n);if(void 0===t)for(var s in a)s.match(/Id$/)&&(a[s.replace(/Id$/,"ID")]=a[s],delete a[s]);return a},window.console||(window.console={});for(var t=["log","info","warn","exception","assert","dir","dirxml","trace","group","groupEnd","groupCollapsed","profile","profileEnd","count","clear","time","timeEnd","timeStamp","table","error"],i=0;i<t.length;i++)void 0===console[t[i]]&&(console[t[i]]=function(){});void 0===console.debug&&(console.debug=function(e){console.log(e)})}(),window.shuffle=function(e){for(var t,i,n=e.length;0!==n;)i=Math.floor(Math.random()*n),n-=1,t=e[n],e[n]=e[i],e[i]=t;return this},function(e){var t=navigator.userAgent.toLowerCase(),i=/(chrome)[ \/]([\w.]+)/.exec(t)||/(webkit)[ \/]([\w.]+)/.exec(t)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(t)||/(msie) ([\w.]+)/.exec(t)||t.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(t)||[],n={browser:i[1]||"",version:i[2]||"0"},s={};n.browser&&(s[n.browser]=!0,s.version=n.version),s.chrome?s.webkit=!0:s.webkit&&(s.safari=!0),e.browser=e.browser||{},e.browser=$.extend(e.browser,s),e.browser.touch=!!("ontouchstart"in window)||!!("msMaxTouchPoints"in window.navigator)&&window.navigator.msMaxTouchPoints>0,e.browser.smartphone="bottom"==$("html").css("caption-side"),e.browser.mozilla&&t.match(/trident/)&&(e.browser.mozilla=!1,e.browser.msie=!0),e.browser.iOS=/\((ipad|iphone|ipod);/.test(t),e.browser.iOS&&$("html").addClass("iOS"),e.browser.android=-1!==t.indexOf("android"),e.browser.editor="redactor",e.browser.ckeditor=!1,e.browser.redactor=!0,e.browser.iOS&&(e.fn.focus=function(e,t){return arguments.length>0?this.on("focus",null,e,t):this.trigger("focus")})}(jQuery),null==window.WCF&&(window.WCF={}),$.extend(!0,{removeArrayValue:function(e,t){return $.grep(e,function(e,i){return t!==e})},wcfEscapeID:function(e){return e.replace(/(:|\.)/g,"\\$1")},wcfIsset:function(e){return!!$("#"+$.wcfEscapeID(e)).length},getLength:function(e){var t=0;for(var i in e)e.hasOwnProperty(i)&&t++;return t}}),$.fn.extend({getTagName:function(){return this.length?this.get(0).tagName.toLowerCase():""},getDimensions:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"inner":i={height:this.innerHeight(),width:this.innerWidth()};break;case"outer":i={height:this.outerHeight(),width:this.outerWidth()};break;default:i={height:this.height(),width:this.width()}}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},getOffsets:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"offset":i=this.offset();break;case"position":default:i=this.position()}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},makePositioned:function(e,t){"absolute"!=e&&"fixed"!=e&&(e="absolute");var i=this.getOffsets("position");return this.css({position:e,left:i.left,margin:0,top:i.top}),t&&this.remove().appentTo("body"),this},disable:function(){return this.attr("disabled","disabled")},enable:function(){return this.removeAttr("disabled")},wcfIdentify:function(){return window.bc_wcfDomUtil.identify(this[0])},getCaret:function(){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return-1}else if(!this.is("textarea"))return-1;var e=0,t=this.get(0);if(document.selection){this.focus();var i=document.selection.createRange();i.moveStart("character",-this.val().length),e=i.text.length}else(t.selectionStart||"0"==t.selectionStart)&&(e=parseInt(t.selectionStart));return e},setCaret:function(e){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return!1}else if(!this.is("textarea"))return!1;var t=this.get(0);if(this.focus(),document.selection){var i=document.selection.createRange();i.moveStart("character",e),i.moveEnd("character",0),i.select()}else(t.selectionStart||"0"==t.selectionStart)&&(t.selectionStart=e,t.selectionEnd=e);return!0},wcfDropIn:function(e,t,i){return e||(e="up"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfDropOut:function(e,t,i){return e||(e="down"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfBlindIn:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfBlindOut:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfHighlight:function(e,t){return this.effect("highlight",e,600,t)},wcfFadeIn:function(e,t){return t&&parseInt(t)||(t=200),this.show(WCF.getEffect(this,"fade"),{},t,e)},wcfFadeOut:function(e,t){return t&&parseInt(t)||(t=200),this.hide(WCF.getEffect(this,"fade"),{},t,e)},cssAsNumber:function(e){if(this.length){var t=this.css(e);if(void 0!==t)return parseInt(t.replace(/px$/,""))}return 0},perfectScrollbar:function(e){var t=require("perfect-scrollbar");return this.each(function(){if("object"==typeof e||void 0===e){var i=e;$(this).data("psID")||t.initialize(this,i)}else{var n=e;"update"===n?t.update(this):"destroy"===n&&t.destroy(this)}return jQuery(this)})}}),$.extend(WCF,{activeDialogs:0,_idCounter:0,getRandomID:function(){return window.bc_wcfDomUtil.getUniqueId()},inArray:function(e,t){return-1!=$.inArray(e,t)},getEffect:function(e,t){return e.is("tr")?"highlight":t},getInlineCSS:function(e){var t={},i=e.attr("style");if(!i)return{};i=i.split(";");for(var n=0,s=i.length;n<s;n++){var o=$.trim(i[n]);""!=o&&(o=o.split(":"),t[$.trim(o[0])]=$.trim(o[1]))}return t},revertInlineCSS:function(e,t,i){for(var n=0,s=i.length;n<s;n++){var o=i[n];t[o]?e.css(o,t[o]):e.css(o,"")}},getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)})},base64toBlob:function(e,t,i){t=t||"",i=i||512;for(var n=atob(e),s=[],o=0;o<n.length;o+=i){for(var a=n.slice(o,o+i),r=new Array(a.length),c=0;c<a.length;c++)r[c]=a.charCodeAt(c);var l=new Uint8Array(r);s.push(l)}return new Blob(s,{type:t})},convertLegacyURL:function(e){return e.replace(/^index\.php\/(.*?)\/\?/,function(e,t){for(var i=t.split(/([A-Z][a-z0-9]+)/),n="",s=0,o=i.length;s<o;s++){var a=i[s].trim();a.length&&(n.length&&(n+="-"),n+=a.toLowerCase())}return"index.php?"+n+"/&"})}}),WCF.Browser={_isChrome:null,isChrome:function(){return null===this._isChrome&&(this._isChrome=!1,/chrom(e|ium)/.test(navigator.userAgent.toLowerCase())&&(this._isChrome=!0)),this._isChrome}},WCF.Dropdown={init:function(e){window.bc_wcfSimpleDropdown.initAll()},initDropdown:function(e,t){window.bc_wcfSimpleDropdown.init(e[0],t)},removeDropdown:function(e){window.bc_wcfSimpleDropdown.destroy(e)},initDropdownFragment:function(e,t){window.bc_wcfSimpleDropdown.initFragment(e[0],t[0])},registerCallback:function(e,t){window.bc_wcfSimpleDropdown.registerCallback(e,t)},_toggle:function(e,t){window.bc_wcfSimpleDropdown._toggle(e,t)},toggleDropdown:function(e){window.bc_wcfSimpleDropdown._toggle(null,e)},getDropdown:function(e){var t=window.bc_wcfSimpleDropdown.getDropdown(e);return t?$(t):null},getDropdownMenu:function(e){var t=window.bc_wcfSimpleDropdown.getDropdownMenu(e);return t?$(t):null},setAlignmentByID:function(e){window.bc_wcfSimpleDropdown.setAlignmentById(e)},setAlignment:function(e,t){window.bc_wcfSimpleDropdown.setAlignment(e[0],t[0])},_closeAll:function(){window.bc_wcfSimpleDropdown.closeAll()},close:function(e){window.bc_wcfSimpleDropdown.close(e)},destroy:function(e){window.bc_wcfSimpleDropdown.destroy(e)}},WCF.Dropdown.Interactive={},WCF.Dropdown.Interactive.Handler={_dropdownContainer:{},_dropdownMenus:{},create:function(){},open:function(){},close:function(){},closeAll:function(){},getOpenDropdown:function(){},getDropdown:function(){}},WCF.Dropdown.Interactive.Instance=Class.extend({_container:{},_itemList:{},_linkList:{},_options:{},_pointer:{},_triggerElement:{},init:function(){},getContainer:function(){},getItemList:function(){},getLinkList:function(){},open:function(){},close:function(){},isOpen:function(){},toggle:function(){},resetItems:function(){},render:function(){},rebuildScrollbar:function(){}}),WCF.Clipboard={init:function(){},reload:function(){}},WCF.PeriodicalExecuter=Class.extend({_callback:null,_delay:0,_intervalID:null,_isExecuting:!1,init:function(e,t){if(!$.isFunction(e))return void console.debug("[WCF.PeriodicalExecuter] Given callback is invalid, aborting.");this._callback=e,this._interval=t,this.resume()},_execute:function(){if(!this._isExecuting)try{this._isExecuting=!0,this._callback(this),this._isExecuting=!1}catch(e){throw this._isExecuting=!1,e}},stop:function(){this._intervalID&&clearInterval(this._intervalID)},resume:function(){this.restart()},restart:function(){this._intervalID&&this.stop(),this._intervalID=setInterval($.proxy(this._execute,this),this._interval)},setInterval:function(e){this._interval=e,this.restart()}}),WCF.LoadingOverlayHandler={show:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.show()})},hide:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.hide()})},updateIcon:function(e,t){var i=void 0===t||t?"addClass":"removeClass";e.find(".icon")[i]("fa-spinner"),e.hasClass("icon")&&e[i]("fa-spinner")}},WCF.Action={},WCF.Action.Proxy=Class.extend({_ajaxRequest:null,init:function(e){this._ajaxRequest=null,e=$.extend(!0,{autoSend:!1,data:{},dataType:"json",after:null,init:null,jsonp:"callback",async:!0,failure:null,showLoadingOverlay:!0,success:null,suppressErrors:!1,type:"POST",url:"index.php?ajax-proxy/&t="+SECURITY_TOKEN,aborted:null,autoAbortPrevious:!1},e),"jsonp"===e.dataType?require(["AjaxJsonp"],function(t){t.send(e.url,e.success,e.failure,{parameterName:e.jsonp})}):require(["AjaxRequest"],function(t){this._ajaxRequest=new t({data:e.data,type:e.type,url:e.url,withCredentials:e.url==="index.php?ajax-proxy/&t="+SECURITY_TOKEN,responseType:"json"===e.dataType?"application/json":"",autoAbort:e.autoAbortPrevious,ignoreError:e.suppressErrors,silent:!e.showLoadingOverlay,failure:e.failure,finalize:e.after,success:e.success}),e.autoSend&&this._ajaxRequest.sendRequest()}.bind(this))},sendRequest:function(e){require(["AjaxRequest"],function(t){null!==this._ajaxRequest&&this._ajaxRequest.sendRequest(e)}.bind(this))},abortPrevious:function(){require(["AjaxRequest"],function(e){null!==this._ajaxRequest&&this._ajaxRequest.abortPrevious()}.bind(this))},setOption:function(e,t){require(["AjaxRequest"],function(i){null!==this._ajaxRequest&&this._ajaxRequest.setOption(e,t)}.bind(this))},showLoadingOverlayOnce:function(){},suppressErrors:function(){},_failure:function(e,t,i){},_success:function(e,t,i){},_after:function(){}}),WCF.Action.SimpleProxy=Class.extend({init:function(e,t){this.options=$.extend(!0,{action:"",className:"",elements:null,eventName:"click"},e),this.callbacks=$.extend(!0,{after:null,failure:null,init:null,success:null},t),this.options.elements&&(this.proxy=new WCF.Action.Proxy(this.callbacks),this.options.elements.each($.proxy(function(e,t){$(t).bind(this.options.eventName,$.proxy(this._handleEvent,this))},this)))},_handleEvent:function(e){this.proxy.setOption("data",{actionName:this.options.action,className:this.options.className,objectIDs:[$(e.target).data("objectID")]}),this.proxy.sendRequest()}}),WCF.Action.Delete=Class.extend({_buttonSelector:"",_callback:{},_className:"",_containerSelector:"",_containers:{},init:function(){},_initElements:function(){},_click:function(){},_didTriggerEffect:function(){},_execute:function(){},_sendRequest:function(){},_success:function(){},setCallback:function(){},triggerEffect:function(){}}),WCF.Action.NestedDelete=WCF.Action.Delete.extend({triggerEffect:function(){},_buttonSelector:"",_callback:{},_className:"",_containerSelector:"",_containers:{},init:function(){},_initElements:function(){},_click:function(){},_didTriggerEffect:function(){},_execute:function(){},_sendRequest:function(){},_success:function(){},setCallback:function(){}}),WCF.Action.Toggle=Class.extend({_buttonSelector:"",_className:"",_containerSelector:"",_containers:{},init:function(){},_initElements:function(){},_click:function(){},_execute:function(){},_sendRequest:function(){},_success:function(){},triggerEffect:function(){},_toggleButton:function(){}}),WCF.Action.Scroll=Class.extend({_callback:null,_reference:null,_target:null,_threshold:0,init:function(e,t,i,n){return this._threshold=parseInt(e),0===this._threshold?void console.debug("[WCF.Action.Scroll] Given threshold is invalid, aborting."):($.isFunction(t)&&(this._callback=t),null===this._callback?void console.debug("[WCF.Action.Scroll] Given callback is invalid, aborting."):(this._reference=$(i||window),this._target=$(n||document),this.start(),void this._scroll()))},_scroll:function(){var e=this._target.height(),t=this._reference.scrollTop();e-(this._reference.height()+t)<this._threshold&&this._callback(this)},start:function(){this._reference.on("scroll",$.proxy(this._scroll,this))},stop:function(){this._reference.off("scroll")}}),WCF.Date={},WCF.Date.Picker={init:function(){}},WCF.Date.Util={gmdate:function(e){var t=e||new Date;return Math.round(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDay(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds())/1e3)},getTimezoneDate:function(e,t){var i=new Date(e),n=6e4*i.getTimezoneOffset();return new Date(e+n+t)}},WCF.Dictionary=Class.extend({_variables:{},init:function(){this._variables={}},add:function(e,t){this._variables[e]=t},addObject:function(e){for(var t in e)this.add(t,e[t])},addDictionary:function(e){e.each($.proxy(function(e){this.add(e.key,e.value)},this))},get:function(e){return this.isset(e)?this._variables[e]:null},isset:function(e){return this._variables.hasOwnProperty(e)},remove:function(e){delete this._variables[e]},each:function(e){if($.isFunction(e))for(var t in this._variables){var i=this._variables[t],n={key:t,value:i};e(n)}},count:function(){return $.getLength(this._variables)},isEmpty:function(){return!this.count()}}),null==window.WCF.Language&&(WCF.Language={add:function(e,t){require(["Language"],function(i){i.add(e,t)})},addObject:function(e){require(["Language"],function(t){t.addObject(e)})},get:function(e,t){throw new Error('Call to deprecated WCF.Language.get("'+e+'")')}}),WCF.Number={round:function(e,t){return t=Math.pow(10,t||0),Math.round(e*t)/t}},WCF.String={addThousandsSeparator:function(e){return String(e).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g,"$1"+WCF.Language.get("wcf.global.thousandsSeparator"))},escapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")},escapeRegExp:function(e){return String(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},formatNumeric:function(e,t){e=String(WCF.Number.round(e,t||2));var i=e.split(".");return e=this.addThousandsSeparator(i[0]),i.length>1&&(e+=WCF.Language.get("wcf.global.decimalPoint")+i[1]),e=e.replace("-","−")},lcfirst:function(e){return String(e).substring(0,1).toLowerCase()+e.substring(1)},ucfirst:function(e){return String(e).substring(0,1).toUpperCase()+e.substring(1)},unescapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">")}},WCF.TabMenu={init:function(){require(["WoltLabSuite/Core/Ui/TabMenu"],function(e){e.setup()})},reload:function(){this.init()}},WCF.Template=Class.extend({init:function(e){var t=new WCF.Dictionary,i=0;e=e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/g,"\\n"),e=e.replace(/\{literal\}(.*?)\{\/literal\}/g,$.proxy(function(e){var i="@@@@@@@@@@@"+Math.random()+"@@@@@@@@@@@";return t.add(i,e.replace(/\{\/?literal\}/g,"")),i},this)),e=e.replace(/\{\*.*?\*\}/g,"");var n=function(e){for(var t=e.split(""),i={},n=!0,s="",o="",a=!1,r=!1,c=!1,l=0,u=t.length;l<u;l++){var h=t[l];n&&"="!=h&&" "!=h?s+=h:n&&"="==h?(n=!1,r=!1,a=!1,c=!1):n||r||a||" "!=h?n||!r||c||"'"!=h?n||r||a||"'"!=h?n||!a||c||'"'!=h?n||r||a||'"'!=h?n||!a&&!r||c||"\\"!=h?n||(c=!1,o+=h):(c=!0,o+=h):(a=!0,o+=h):(a=!1,o+=h):(r=!0,o+=h):(r=!1,o+=h):(n=!0,i[s]=o,o=s="")}if(i[s]=o,a||r||c)throw new Error('Syntax error in parameterList: "'+e+'"');return i},s=function(e){return e.replace(/\\n/g,"\n").replace(/\\\\/g,"\\").replace(/\\'/g,"'")};e=e.replace(/\{(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.escapeHTML("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{#(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.formatNumeric("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{@(\$[^\}]+?)\}/g,function(e,t){return"' + "+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+" + '"}).replace(/\{lang\}(.+?)\{\/lang\}/g,function(e,t){return"' + WCF.Language.get('"+t+"', v) + '"}).replace(/\{include (.+?)\}/g,function(e,t){t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var i=n(t);if(void 0===i.file)throw new Error("Missing file attribute in include-tag");return i.file=i.file.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"' + "+i.file+".fetch(v) + '"}).replace(/\{if (.+?)\}/g,function(e,t){return"';\nif ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{else ?if (.+?)\}/g,function(e,t){return"';\n}\nelse if ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{implode (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in implode-tag");if(void 0===s.item)throw new Error("Missing item attribute in implode-tag");return void 0===s.glue&&(s.glue="', '"),s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\nvar $implode_"+i+" = false;\nfor ($implodeKey_"+i+" in "+s.from+") {\n\tv["+s.item+"] = "+s.from+"[$implodeKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $implodeKey_"+i+";\n":"")+"\tif ($implode_"+i+") $output += "+s.glue+";\n\t$implode_"+i+" = true;\n\t$output += '"}).replace(/\{foreach (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in foreach-tag");if(void 0===s.item)throw new Error("Missing item attribute in foreach-tag");return s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\n$foreach_"+i+" = false;\nfor ($foreachKey_"+i+" in "+s.from+") {\n\t$foreach_"+i+" = true;\n\tbreak;\n}\nif ($foreach_"+i+") {\n\tfor ($foreachKey_"+i+" in "+s.from+") {\n\t\tv["+s.item+"] = "+s.from+"[$foreachKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $foreachKey_"+i+";\n":"")+"\t\t$output += '"}).replace(/\{foreachelse\}/g,"';\n\t}\n}\nelse {\n\t{\n\t\t$output += '").replace(/\{\/foreach\}/g,"';\n\t}\n}\n$output += '").replace(/\{else\}/g,"';\n}\nelse {\n\t$output += '").replace(/\{\/(if|implode)\}/g,"';\n}\n$output += '");for(var o in WCF.Template.callbacks)e=WCF.Template.callbacks[o](e);e=e.replace("{ldelim}","{").replace("{rdelim}","}"),t.each(function(t){e=e.replace(t.key,t.value)}),e="$output += '"+e+"';";try{this.fetch=new Function("v","v = window.$.extend({}, v, { __wcf: window.WCF, __window: window }); var $output = ''; "+e+" return $output;")}catch(t){throw console.debug("var $output = ''; "+e+" return $output;"),t}},fetch:function(e){}}),WCF.Template.callbacks=[],WCF.ToggleOptions=Class.extend({_element:null,_showItems:[],_hideItems:[],_callback:null,init:function(e,t,i,n){this._element=$("#"+e),this._showItems=t,this._hideItems=i,void 0!==n&&(this._callback=n),this._element.click($.proxy(this._toggle,this)),this._toggle()},_toggle:function(){if(this._element.prop("checked")){for(var e=0,t=this._showItems.length;e<t;e++){var i=this._showItems[e];$("#"+i).show()}for(var e=0,t=this._hideItems.length;e<t;e++){var i=this._hideItems[e];$("#"+i).hide()}null!==this._callback&&this._callback()}}}),WCF.Collapsible={},WCF.Collapsible.Simple={init:function(){$(".jsCollapsible").each($.proxy(function(e,t){this._initButton(t)},this))},_initButton:function(e){var t=$(e);t.data("isOpen")||$("#"+t.data("collapsibleContainer")).hide(),t.click($.proxy(this._toggle,this))},_toggle:function(e){var t=$(e.currentTarget),i=t.data("isOpen"),n=$("#"+$.wcfEscapeID(t.data("collapsibleContainer")));return i?(n.stop().wcfBlindOut("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!1):(n.stop().wcfBlindIn("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!0),t.data("isOpen",i),e.stopPropagation(),!1},_toggleImage:function(e){var t=e.find("span.icon");e.data("isOpen")?t.removeClass("fa-chevron-right").addClass("fa-chevron-down"):t.removeClass("fa-chevron-down").addClass("fa-chevron-right")}},WCF.Collapsible.Remote=Class.extend({_className:"",_containers:{},_containerData:{},_proxy:null,init:function(e){this._className=e,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._init(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Collapsible.Remote",$.proxy(this._init,this))},_init:function(e){this._getContainers().each($.proxy(function(e,t){var i=$(t),n=i.wcfIdentify();void 0===this._containers[n]&&(this._containers[n]=i,this._initContainer(n))},this))},_initContainer:function(e){var t=this._getTarget(e),i=this._getButtonContainer(e),n=this._createButton(e,i);this._containerData[e]={button:n,buttonContainer:i,isOpen:this._containers[e].data("isOpen"),target:t},this._containers[e].data("isOpen")||$("#"+e).addClass("jsCollapsed")},_getContainers:function(){},_getTarget:function(e){},_getButtonContainer:function(e){},_createButton:function(e,t){var i=elBySel(".jsStaticCollapsibleButton",t[0]);return null!==i&&i.parentNode===t[0]?(i.classList.remove("jsStaticCollapsibleButton"),i=$(i)):i=$('<span class="collapsibleButton jsTooltip pointer icon icon16 fa-chevron-down" title="'+WCF.Language.get("wcf.global.button.collapsible")+'">').prependTo(t),i.data("containerID",e).click($.proxy(this._toggleContainer,this)),i},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",o=n?"close":"open";this._proxy.setOption("data",{actionName:"loadContainer",className:this._className,interfaceName:"wcf\\data\\ILoadableContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:o},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),$("#"+i).toggleClass("jsCollapsed")},_exchangeIcon:function(e,t){t=t||"spinner",e.removeClass("fa-chevron-down fa-chevron-right fa-spinner").addClass("fa-"+t)},_getObjectID:function(e){return $("#"+e).data("objectID")},_getAdditionalParameters:function(e){return{}},_updateContent:function(e,t,i){this._containerData[e].target.html(t)},_success:function(e,t,i){if(e.returnValues.containerID){var n=e.returnValues.containerID;if(this._containers[n]){this._containerData[n].isOpen=!!e.returnValues.isOpen;var s=e.returnValues.isOpen?"open":"close";this._updateContent(n,$.trim(e.returnValues.content),s)}}}}),WCF.Collapsible.SimpleRemote=WCF.Collapsible.Remote.extend({init:function(e){this._super(e),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1})},_initContainer:function(e){this._super(e),this._containerData[e].isOpen||(this._containerData[e].target.hide(),this._exchangeIcon(this._containerData[e].button,"chevron-right"))},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",o=n?"close":"open";this._proxy.setOption("data",{actionName:"toggleContainer",className:this._className,interfaceName:"wcf\\data\\IToggleContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:o},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),this._exchangeIcon(this._containerData[i].button,"open"===o?"chevron-down":"chevron-right"),"open"===o?this._containerData[i].target.show():this._containerData[i].target.hide(),$("#"+i).toggleClass("jsCollapsed"),this._containerData[i].isOpen="open"===o}}),WCF.User={userID:0,username:"",init:function(e,t){this.userID=e,this.username=t}},WCF.Effect={},WCF.Effect.Scroll=Class.extend({scrollTo:function(e,t,i){if(!e.length)return!0;var n=e.getOffsets("offset").top,s=$(document).height(),o=$(window).height();return n>s-o&&(n=s-o)<0&&(n=0),!0===i?$("html,body").scrollTop(n):$("html,body").animate({scrollTop:n},400,function(e,t,i,n,s){return-n*((t=t/s-1)*t*t*t-1)+i}),!1}}),WCF.CloseOverlayHandler={addCallback:function(e,t){require(["Ui/CloseOverlay"],function(i){i.add(e,t)})},removeCallback:function(e){require(["Ui/CloseOverlay"],function(t){t.remove(e)})},forceExecution:function(){require(["Ui/CloseOverlay"],function(e){e.execute()})}},WCF.DOMNodeInsertedHandler={addCallback:function(e,t){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.add("__legacy__",t)})},_executeCallbacks:function(){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.trigger()})},execute:function(){this._executeCallbacks()}},WCF.DOMNodeRemovedHandler={_callbacks:new WCF.Dictionary,_isExecuting:!1,_isListening:!1,addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.DOMNodeRemovedHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){if(window.MutationObserver){new MutationObserver(function(e){var t=!1;e.forEach(function(e){e.removedNodes.length&&(t=!0)}.bind(this)),t&&this._executeCallbacks({})}.bind(this)).observe(document.body,{childList:!0,subtree:!0})}else $(document).bind("DOMNodeRemoved",$.proxy(this._executeCallbacks,this));this._isListening=!0}},_executeCallbacks:function(e){this._isExecuting||(this._isExecuting=!0,this._callbacks.each(function(t){t.value(e)}),this._isExecuting=!1)}},WCF.Option={},WCF.Option.Handler=Class.extend({init:function(){},_initOptions:function(){},_initOption:function(){},_handleChange:function(){},_change:function(){},_execute:function(){},_enableOption:function(){},_enableOptionElement:function(){},_enableOptions:function(){}}),WCF.PageVisibilityHandler={_callbacks:new WCF.Dictionary,_isListening:!1,_hiddenFieldName:"",addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.PageVisibilityHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){var e=null;void 0!==document.hidden?(this._hiddenFieldName="hidden",e="visibilitychange"):void 0!==document.mozHidden?(this._hiddenFieldName="mozHidden",e="mozvisibilitychange"):void 0!==document.msHidden?(this._hiddenFieldName="msHidden",e="msvisibilitychange"):void 0!==document.webkitHidden&&(this._hiddenFieldName="webkitHidden",e="webkitvisibilitychange"),null===e?console.debug("[WCF.PageVisibilityHandler] This browser does not support the page visibility API."):$(document).on(e,$.proxy(this._executeCallbacks,this)),this._isListening=!0}},_executeCallbacks:function(e){if(!this._isExecuting){this._isExecuting=!0;var t=document[this._hiddenFieldName];this._callbacks.each(function(e){e.value(t)}),this._isExecuting=!1}}},WCF.Table={},WCF.Table.EmptyTableHandler=Class.extend({_options:{},_rowClassName:"",init:function(e,t,i){this._rowClassName=t,this._tableContainer=e,this._options=$.extend(!0,{emptyMessage:null,messageType:"info",refreshPage:!1,updatePageNumber:!1,isTable:0!==this._tableContainer.find("table").length},i||{}),WCF.DOMNodeRemovedHandler.addCallback("WCF.Table.EmptyTableHandler."+t,$.proxy(this._remove,this))},_getRowCount:function(){return this._tableContainer.find((this._options.isTable?"table tr.":".tabularList .")+this._rowClassName).length},_handleEmptyTable:function(){if(this._options.emptyMessage)this._tableContainer.replaceWith($("<p />").addClass(this._options.messageType).text(this._options.emptyMessage));else if(this._options.refreshPage)if(this._options.updatePageNumber){var e=window.location.href.match(/(\?|&)pageNo=(\d+)/g);if(e){var t=e[e.length-1].match(/\d+/g);this._options.updatePageNumber>0?t++:t--,window.location=window.location.href.replace(e[e.length-1],e[e.length-1][0]+"pageNo="+t)}}else window.location.reload();else this._tableContainer.remove()},_remove:function(e){if($.getLength(e)){var t=$(e.target);if(t.hasClass(this._rowClassName))if(this._options.isTable){var i=t.parents("tbody:eq(0)");1==i.children("tr").length&&this._handleEmptyTable()}else 1===this._getRowCount()&&this._handleEmptyTable()}else this._getRowCount()||this._handleEmptyTable()}}),WCF.Search={},WCF.Search.Base=Class.extend({_callback:null,_caretAt:-1,_className:"",_commaSeperated:!1,_delay:0,_excludedSearchValues:[],_itemCount:0,_itemIndex:-1,_list:null,_oldSearchString:[],_proxy:null,_searchInput:null,_triggerLength:3,_timer:null,init:function(e,t,i,n,s){return null===t||void 0===t||$.isFunction(t)?(this._callback=t||null,this._caretAt=-1,this._delay=0,this._excludedSearchValues=[],i&&(this._excludedSearchValues=i),this._searchInput=$(e),this._searchInput.length?(this._searchInput.keydown($.proxy(this._keyDown,this)).keyup($.proxy(this._keyUp,this)).wrap('<span class="dropdown" />'),$.browser.mozilla&&$.browser.touch&&this._searchInput.on("input",$.proxy(this._keyUp,this)),this._list=$('<ul class="dropdownMenu" />').insertAfter(this._searchInput),this._commaSeperated=!!n,this._oldSearchString=[],this._itemCount=0,this._itemIndex=-1,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!0===s,success:$.proxy(this._success,this),autoAbortPrevious:!0}),this._searchInput.is("input")&&this._searchInput.attr("autocomplete","off"),this._searchInput.blur($.proxy(this._blur,this)),void WCF.Dropdown.initDropdownFragment(this._searchInput.parent(),this._list)):void console.debug("[WCF.Search.Base] Selector '"+e+"' for search input is invalid, aborting.")):void console.debug("[WCF.Search.Base] The given callback is invalid, aborting.")},_blur:function(){var e=this;new WCF.PeriodicalExecuter(function(t){e._list.is(":visible")&&e._clearList(!1),t.stop()},250)},_keyDown:function(e){if(e.which===$.ui.keyCode.ENTER){var t=this._searchInput.parents(".dropdown");t.data("disableAutoFocus")?-1!==this._itemIndex&&e.preventDefault():(t.data("preventSubmit")||-1!==this._itemIndex)&&e.preventDefault()}},_keyUp:function(e){switch(e.which){case 37:case 39:return;case 38:return void this._selectPreviousItem();case 40:return void this._selectNextItem();case 13:return this._selectElement(e)}var t=this._getSearchString(e);if(""===t)this._clearList(!1);else if(t.length>=this._triggerLength){var i={data:{excludedSearchValues:this._excludedSearchValues,searchString:t}};if(this._delay){null!==this._timer&&this._timer.stop();var n=this;this._timer=new WCF.PeriodicalExecuter(function(){n._queryServer(i),n._timer.stop(),n._timer=null},this._delay)}else this._queryServer(i)}else this._clearList(!1)},_queryServer:function(e){this._searchInput.parents(".searchBar").addClass("loading"),this._proxy.setOption("data",{actionName:"getSearchResultList",className:this._className,interfaceName:"wcf\\data\\ISearchAction",parameters:this._getParameters(e)}),this._proxy.sendRequest()},setDelay:function(e){this._delay=e},_selectNextItem:function(){0!==this._itemCount&&(this._itemIndex++,this._itemIndex===this._itemCount&&(this._itemIndex=0),this._highlightSelectedElement())},_selectPreviousItem:function(){0!==this._itemCount&&(this._itemIndex--,-1===this._itemIndex&&(this._itemIndex=this._itemCount-1),this._highlightSelectedElement())},
-_highlightSelectedElement:function(){this._list.find("li").removeClass("dropdownNavigationItem"),this._list.find("li:eq("+this._itemIndex+")").addClass("dropdownNavigationItem")},_selectElement:function(e){return 0===this._itemCount||(this._list.find("li.dropdownNavigationItem").trigger("click"),!1)},_getSearchString:function(e){var t=$.trim(this._searchInput.val());if(this._commaSeperated){if((e.keyCode||e.which)==$.ui.keyCode.COMMA)return"";for(var i=t.split(","),n=i.length,s=0;s<n;s++)i[s]=$.trim(i[s]);for(var s=0;s<n;s++){var o=i[s];if(!this._oldSearchString[s]){t=o;break}if(o!=this._oldSearchString[s]){t=o,this._caretAt=s;break}}this._oldSearchString=i}return t},_getParameters:function(e){return e},_success:function(e,t,i){if(this._clearList(!1),this._searchInput.parents(".searchBar").removeClass("loading"),$.getLength(e.returnValues))for(var n in e.returnValues){var s=e.returnValues[n];this._createListItem(s)}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var o=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(o).hasClass("dropdownOpen")||(WCF.Dropdown.toggleDropdown(o),this._openDropdown()),this._itemIndex=-1,WCF.Dropdown.getDropdown(o).data("disableAutoFocus")||this._selectNextItem()},_openDropdown:function(){},_handleEmptyResult:function(){return!1},_createListItem:function(e){var t=$("<li><span>"+WCF.String.escapeHTML(e.label)+"</span></li>").appendTo(this._list);return t.data("objectID",e.objectID).data("label",e.label).click($.proxy(this._executeCallback,this)),this._itemCount++,t},_executeCallback:function(e){var t=!1,i=$(e.currentTarget);if(this._commaSeperated){var n=i.data("label");this._oldSearchString[this._caretAt]=n,this._searchInput.val(this._oldSearchString.join(", ")),$.browser.webkit&&this._searchInput.css({display:"block"});var s=this._searchInput.val().toLowerCase().indexOf(n.toLowerCase())+n.length;this._searchInput.focus().setCaret(s)}else null===this._callback?this._searchInput.val(i.data("label")):t=!0===this._callback(i.data());this._clearList(t)},_clearList:function(e){e&&!this._commaSeperated&&this._searchInput.val(""),WCF.Dropdown.getDropdown(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),WCF.Dropdown.getDropdownMenu(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),this._list.end().empty(),WCF.CloseOverlayHandler.removeCallback("WCF.Search.Base"),this._itemCount=0,this._itemIndex=-1},addExcludedSearchValue:function(e){WCF.inArray(e,this._excludedSearchValues)||this._excludedSearchValues.push(e)},removeExcludedSearchValue:function(e){var t=$.inArray(e,this._excludedSearchValues);-1!=t&&this._excludedSearchValues.splice(t,1)}}),WCF.Search.User=WCF.Search.Base.extend({_className:"wcf\\data\\user\\UserAction",_includeUserGroups:!1,init:function(e,t,i,n,s){this._includeUserGroups=i,this._super(e,t,n,s)},_getParameters:function(e){return e.data.includeUserGroups=this._includeUserGroups?1:0,e},_createListItem:function(e){var t=this._super(e),i=null;if(e.icon?i=$(e.icon):this._includeUserGroups&&"group"===e.type&&(i=$('<span class="icon icon16 fa-users" />')),i){var n=t.find("span").detach(),s=$("<div />").addClass("box16").appendTo(t);s.append(i),s.append($("<div />").append(n))}return t.data("type",e.type),t}}),WCF.System={},WCF.System.Dependency={},WCF.System.Dependency.Manager={_callbacks:{},_loaded:[],_setupCallbacks:{},register:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Callback for identifier '"+e+"' is invalid, aborting.");WCF.inArray(e,this._loaded)?setTimeout(function(){t()},1):(this._callbacks[e]||(this._callbacks[e]=[]),this._callbacks[e].push(t))},setup:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Setup callback for identifier '"+e+"' is invalid, aborting.");this._setupCallbacks[e]||(this._setupCallbacks[e]=[]),this._setupCallbacks[e].push(t)},invoke:function(e){if(this._setupCallbacks[e]){for(var t=0,i=this._setupCallbacks[e].length;t<i;t++)this._setupCallbacks[e][t]();delete this._setupCallbacks[e]}if(this._loaded.push(e),this._callbacks[e]){for(var t=0,i=this._callbacks[e].length;t<i;t++)this._callbacks[e][t]();delete this._callbacks[e]}},reset:function(e){var t=this._loaded.indexOf(e);-1!==t&&this._loaded.splice(t,1)}},WCF.System.FlexibleMenu={init:function(){},registerMenu:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.register(e)})},rebuild:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.rebuild(e)})}},WCF.System.Mobile={},WCF.System.ObjectStore={_objects:{},add:function(e,t){void 0===this._objects[e]&&(this._objects[e]=[]),this._objects[e].push(t)},invoke:function(e,t){if(this._objects[e])for(var i=0;i<this._objects[e].length;i++)t(this._objects[e][i])}},WCF.System.Captcha={_registeredCaptchas:[],addCallback:function(e,t){require(["WoltLabSuite/Core/Controller/Captcha"],function(i){try{i.add(e,t),this._registeredCaptchas.push(e)}catch(e){if(e instanceof TypeError)return void console.debug("[WCF.System.Captcha] Given callback is no function")}}.bind(this))},getData:function(e){var t;if(-1===this._registeredCaptchas.indexOf(e))return t;var i=require("WoltLabSuite/Core/Controller/Captcha");try{t=i.getData(e)}catch(t){console.debug('[WCF.System.Captcha] Unknow captcha id "'+e+'"')}return t},removeCallback:function(e){require(["WoltLabSuite/Core/Controller/Captcha"],function(t){try{t.delete(e),this._registeredCaptchas.splice(this._registeredCaptchas.indexOf(item),1)}catch(e){}}.bind(this))}},WCF.System.Page={},WCF.System.Notification=Class.extend({_cssClassNames:"",_message:"",init:function(e,t){this._cssClassNames=t||"",this._message=e||""},show:function(e,t,i,n){require(["Ui/Notification"],function(t){t.show(i||this._message,e,n||this._cssClassNames)}.bind(this))}}),WCF.System.Confirmation={show:function(e,t,i,n,s){if("object"==typeof n){var o=$("<div />");o.append(n),n=o.html()}require(["Ui/Confirmation"],function(o){o.show({legacyCallback:t,message:e,parameters:i,template:n||"",messageIsHtml:!0===s})})}},WCF.System.DisableScrolling={_depth:0,_oldOverflow:null,disable:function(){$.browser.touch||(0===this._depth&&(this._oldOverflow=$(document.body).css("overflow"),$(document.body).css("overflow","hidden")),this._depth++)},enable:function(){0!==this._depth&&0===--this._depth&&$(document.body).css("overflow",this._oldOverflow)}},WCF.System.DisableZoom={_depth:0,_oldViewportSettings:null,disable:function(){if(0===this._depth){var e=$("meta[name=viewport]");this._oldViewportSettings=e.attr("content"),e.attr("content",this._oldViewportSettings+",maximum-scale=1")}this._depth++},enable:function(){0!==this._depth&&0===--this._depth&&$("meta[name=viewport]").attr("content",this._oldViewportSettings)}},WCF.System.Fullscreen={enterFullscreen:function(e){e.requestFullscreen?e.requestFullscreen():e.msRequestFullscreen?e.msRequestFullscreen():e.mozRequestFullScreen?e.mozRequestFullScreen():e.webkitRequestFullscreen&&e.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)},toggleFullscreen:function(e){null===this.getFullscreenElement()?this.enterFullscreen(e):this.exitFullscreen()},getFullscreenElement:function(){return document.fullscreenElement?document.fullscreenElement:document.mozFullScreenElement?document.mozFullScreenElement:document.webkitFullscreenElement?document.webkitFullscreenElement:document.msFullscreenElement?document.msFullscreenElement:null},exitFullscreen:function(){document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen()},isSupported:function(){return!!(document.documentElement.requestFullscreen||document.documentElement.msRequestFullscreen||document.documentElement.mozRequestFullScreen||document.documentElement.webkitRequestFullscreen)}},WCF.System.PageNavigation={init:function(e,t){require(["WoltLabSuite/Core/Ui/Page/JumpTo"],function(i){for(var n=elBySelAll(e),s=0,o=n.length;s<o;s++)i.init(n[s],t)})}},WCF.System.KeepAlive=Class.extend({init:function(e){new WCF.PeriodicalExecuter(function(e){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"keepAlive",className:"wcf\\data\\session\\SessionAction"},failure:function(){e.stop()},showLoadingOverlay:!1,success:function(e){WCF.System.PushNotification.executeCallbacks(e)},suppressErrors:!0})},1e3*e)}}),WCF.System.PushNotification={_callbacks:{},addCallback:function(e,t){void 0===this._callbacks[e]&&(this._callbacks[e]=[]),this._callbacks[e].push(t)},executeCallbacks:function(e){for(var t in e.returnValues)if(void 0!==this._callbacks[t])for(var i=0;i<this._callbacks[t].length;i++)this._callbacks[t][i](e.returnValues[t])}},WCF.System.Event={addListener:function(e,t,i){return window.__wcf_bc_eventHandler.add(e,t,i)},removeListener:function(e,t,i){return window.__wcf_bc_eventHandler.remove(e,t,i)},removeAllListeners:function(e,t){return window.__wcf_bc_eventHandler.removeAll(e,t)},fireEvent:function(e,t,i){window.__wcf_bc_eventHandler.fire(e,t,i)}},WCF.System.Worker=Class.extend({_aborted:!1,_actionName:"",_callback:{},_className:"",_dialog:{},_proxy:{},_title:"",init:function(){},_success:function(){}}),WCF.InlineEditor=Class.extend({_callbacks:{},_dropdowns:{},_elements:{},_notification:{},_options:{},_proxy:{},_triggerElements:{},_updateData:{},init:function(){},_closeAll:function(){},_setOptions:function(){},registerCallback:function(){},_getTriggerElement:function(){},_show:function(){},_validate:function(){},_validateCallbacks:function(){},_success:function(){},_updateState:function(){},_click:function(){},_execute:function(){},_executeCallback:function(){},_hide:function(){}}),WCF.Upload=Class.extend({_name:"",_buttonSelector:{},_fileListSelector:{},_fileUpload:{},_className:"",_iframe:{},_internalFileID:0,_options:{},_uploadMatrix:{},_supportsAJAXUpload:!0,_overlay:{},init:function(){},_createButton:function(){},_insertButton:function(){},_removeButton:function(){},_upload:function(){},_createUploadMatrix:function(){},_success:function(){},_error:function(){},_progress:function(){},_getParameters:function(){},_initFile:function(){},_showOverlay:function(){},_evaluateResponse:function(){},_getFilename:function(){}}),WCF.Upload.Parallel=WCF.Upload.extend({init:function(){},_upload:function(){},_sendRequest:function(){},_createUploadMatrix:function(){},_success:function(){},_progress:function(){},_showOverlay:function(){},_evaluateResponse:function(){},_name:"",_buttonSelector:{},_fileListSelector:{},_fileUpload:{},_className:"",_iframe:{},_internalFileID:0,_options:{},_uploadMatrix:{},_supportsAJAXUpload:!0,_overlay:{},_createButton:function(){},_insertButton:function(){},_removeButton:function(){},_error:function(){},_getParameters:function(){},_initFile:function(){},_getFilename:function(){}}),WCF.Sortable={},WCF.Sortable.List=Class.extend({_additionalParameters:{},_className:"",_containerID:"",_container:{},_notification:{},_offset:0,_options:{},_proxy:{},_structure:{},init:function(){},_tableRowHelper:function(){},_submit:function(){},_success:function(){}}),WCF.Popover=Class.extend({_activeElementID:"",_identifier:"",_popoverObj:null,init:function(e){var t=!1;require(["Environment"],function(e){"desktop"!==e.platform()&&(t=!0)}.bind(this)),t||(this._activeElementID="",this._identifier=e,require(["WoltLabSuite/Core/Controller/Popover"],function(t){t.init({attributeName:"legacy",className:e,identifier:this._identifier,legacy:!0,loadCallback:this._legacyLoad.bind(this)})}.bind(this)))},_initContainers:function(){},_legacyLoad:function(e,t){this._activeElementID=e,this._popoverObj=t,this._loadContent()},_insertContent:function(e,t){this._popoverObj.setContent(this._identifier,e,t)}}),WCF.EditableItemList=Class.extend({_allowCustomInput:!1,_className:"",_data:{},_form:null,_itemList:null,_objectID:0,_objectTypeID:0,_search:null,_searchInput:null,init:function(e,t){if(this._itemList=$(e),this._searchInput=$(t),this._data={},!this._itemList.length||!this._searchInput.length)return void console.debug("[WCF.EditableItemList] Item list and/or search input do not exist, aborting.");if(this._objectID=this._getObjectID(),this._objectTypeID=this._getObjectTypeID(),this._itemList.find(".jsEditableItem").click($.proxy(this._click,this)),this._itemList.children("ul").length||$("<ul />").appendTo(this._itemList),this._itemList=this._itemList.children("ul"),this._form=this._itemList.parents("form").submit($.proxy(this._submit,this)),this._allowCustomInput){var i=this;this._searchInput.keydown($.proxy(this._keyDown,this)).keypress($.proxy(this._keyPress,this)).on("paste",function(){setTimeout(function(){i._onPaste()},100)})}this._searchInput.parents(".dropdown").data("preventSubmit",!0)},_keyDown:function(e){return null!==e||this._keyPress(null)},_keyPress:function(e){if(null===e||44===e.charCode||e.charCode===$.ui.keyCode.ENTER||$.browser.mozilla&&e.keyCode===$.ui.keyCode.ENTER){if(null!==e&&e.charCode===$.ui.keyCode.ENTER&&this._search&&-1!==this._search._itemIndex)return!1;var t=$.trim(this._searchInput.val());return e&&44===e.charCode&&(t=t.substring(0,this._searchInput.getCaret())),""===t?!0:(this.addItem({objectID:0,label:t}),e&&44===e.charCode?this._searchInput.val($.trim(this._searchInput.val().substr(this._searchInput.getCaret()))):this._searchInput.val(""),null!==e&&e.stopPropagation(),!1)}return!0},_onPaste:function(){var e=$.trim(this._searchInput.val());e=e.split(",");for(var t=0,i=e.length;t<i;t++){var n=$.trim(e[t]);""!==n&&this.addItem({objectID:0,label:n})}this._searchInput.val("")},load:function(e){},_click:function(e){var t=$(e.currentTarget),i=t.data("objectID"),n=t.data("label");return this._search&&this._search.removeExcludedSearchValue(n),this._removeItem(i,n),t.remove(),e.stopPropagation(),!1},_getObjectID:function(){return 0},_getObjectTypeID:function(){return 0},addItem:function(e){return!(!this._data[e.objectID]||0===e.objectID&&this._allowCustomInput)||($('<li class="badge">'+WCF.String.escapeHTML(e.label)+"</li>").data("objectID",e.objectID).data("label",e.label).appendTo(this._itemList).click($.proxy(this._click,this)),this._search&&this._search.addExcludedSearchValue(e.label),this._addItem(e.objectID,e.label),!0)},clearList:function(){this._itemList.children("li").each($.proxy(function(e,t){var i=$(t);this._search&&this._search.removeExcludedSearchValue(i.data("label")),i.remove(),this._removeItem(i.data("objectID"),i.data("label"))},this))},_submit:function(){this._keyDown(null)},_addItem:function(e,t){this._data[e]=t},_removeItem:function(e,t){delete this._data[e]},getSearchInput:function(){return this._searchInput}}),WCF.Language.Chooser=Class.extend({init:function(e,t,i,n,s,o){require(["WoltLabSuite/Core/Language/Chooser"],function(a){a.init(e,t,i,n,s,o)})}}),WCF.Style={},WCF.UserPanel=Class.extend({_container:null,_didLoad:!1,_link:null,_noItems:"",_revertOnEmpty:!0,init:function(e){if(this._container=$("#"+e),this._didLoad=!1,this._revertOnEmpty=!0,1!=this._container.length)return void console.debug("[WCF.UserPanel] Unable to find container identified by '"+e+"', aborting.");this._convert()},_convert:function(){this._container.addClass("dropdown"),this._link=this._container.children("a").remove();var e=$('<a href="'+this._link.attr("href")+'" class="dropdownToggle">'+this._link.html()+"</a>").appendTo(this._container).click($.proxy(this._click,this)),t=$('<ul class="dropdownMenu" />').appendTo(this._container);$('<li class="jsDropdownPlaceholder"><span>'+WCF.Language.get("wcf.global.loading")+"</span></li>").appendTo(t),this._addDefaultItems(t),this._container.dblclick($.proxy(function(){return window.location=this._link.attr("href"),!1},this)),WCF.Dropdown.initDropdown(e,!1)},_addDefaultItems:function(e){},_addDivider:function(e){$('<li class="dropdownDivider" />').appendTo(e)},_click:function(e){e.preventDefault(),this._didLoad||(new WCF.Action.Proxy({autoSend:!0,data:this._getParameters(),success:$.proxy(this._success,this)}),this._didLoad=!0)},_getParameters:function(){return{}},_success:function(e,t,i){var n=WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify());n.children(".jsDropdownPlaceholder").remove(),e.returnValues&&e.returnValues.template?($(""+e.returnValues.template).prependTo(n),this._updateBadge(e.returnValues.totalCount),this._after(n)):($("<li><span>"+WCF.Language.get(this._noItems)+"</span></li>").prependTo(n),this._updateBadge(0))},_updateBadge:function(e){if(e=parseInt(e)||0){var t=this._container.find(".badge");t.length||(t=$('<span class="badge badgeUpdate" />').appendTo(this._container.children(".dropdownToggle")),t.before(" ")),t.html(e)}else this._container.find(".badge").remove()},_after:function(e){}}),jQuery.fn.extend({wcfDialog:function(e){var t=arguments;return require(["Dom/Util","Ui/Dialog"],function(i,n){var s=i.identify(this[0]);if("close"===e)n.close(s);else if("render"===e)n.rebuild(s);else if("option"===e)3===t.length&&("title"===t[1]&&"string"==typeof t[2]?n.setTitle(s,t[2]):0===t[1].indexOf("on")?n.setCallback(s,t[1],t[2]):"closeConfirmMessage"===t[1]&&null===t[2]&&n.setCallback(s,"onBeforeClose",null));else{this[0].parentNode.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&document.body.appendChild(this[0]);var o=1===t.length&&"object"==typeof t[0]?t[0]:{};n.openStatic(s,null,o),o.hasOwnProperty("title")&&n.setTitle(s,o.title)}}.bind(this)),this}}),$.widget("ui.wcfSlideshow",{_buttonList:null,_count:0,_index:0,_itemList:null,_items:null,_timer:null,_width:0,options:{cycle:!0,cycleInterval:5,itemGap:50},_create:function(){this._itemList=this.element.children("ul"),this._items=this._itemList.children("li"),this._count=this._items.length,this._index=0,this._count>1&&this._initSlideshow()},_initSlideshow:function(){var e=$(this._items.get(0)).outerHeight();this._items.addClass("slideshowItem"),this._width=this.element.css("height",e).innerWidth(),this._itemList.addClass("slideshowItemList").css("left",0),this._items.each($.proxy(function(t,i){$(i).show().css({height:e,left:(this._width+this.options.itemGap)*t,width:this._width})},this)),this.element.css({height:e,width:this._width}).hover($.proxy(this._hoverIn,this),$.proxy(this._hoverOut,this)),this._buttonList=$('<ul class="slideshowButtonList" />').appendTo(this.element);for(var t=0;t<this._count;t++){var i=$('<li><a><span class="icon icon16 fa-circle" /></a></li>').data("index",t).click($.proxy(this._click,this)).appendTo(this._buttonList);0==t&&i.find(".icon").addClass("active")}this._resetTimer(),$(window).resize($.proxy(this._resize,this))},rebuildHeight:function(){var e=$(this._items.get(0)).css("height","auto"),t=e.outerHeight();this._items.css("height",t+"px"),this.element.css("height",t+"px")},_resize:function(){this._width=this.element.css("width","auto").innerWidth(),this._items.each($.proxy(function(e,t){$(t).css({left:(this._width+this.options.itemGap)*e,width:this._width})},this)),this._index--,this.moveTo(null)},_hoverIn:function(){null!==this._timer&&this._timer.stop()},_hoverOut:function(){this._resetTimer()},_resetTimer:function(){if(this.options.cycle){null!==this._timer&&this._timer.stop();var e=this;this._timer=new WCF.PeriodicalExecuter(function(){e.moveTo(null)},1e3*this.options.cycleInterval)}},_click:function(e){this.moveTo($(e.currentTarget).data("index")),this._resetTimer()},moveTo:function(e){this._index=null===e?this._index+1:e,this._index==this._count&&(this._index=0),$(this._buttonList.find(".icon").removeClass("active").get(this._index)).addClass("active"),this._itemList.css("left",this._index*(this._width+this.options.itemGap)*-1),this._trigger("moveTo",null,{index:this._index})},getItem:function(e){return this._items[e]?this._items[e]:null}}),jQuery.fn.extend({datepicker:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);switch(e){case"destroy":window.__wcf_bc_datePicker.destroy(t);break;case"getDate":return window.__wcf_bc_datePicker.getDate(t);case"option":if("onClose"===i[0])return i.length>1?this.datepicker("setOption","onClose",i[1]):function(){};console.warn("datepicker('option') supports only 'onClose'.");break;case"setDate":window.__wcf_bc_datePicker.setDate(t,i[0]);break;case"setOption":"onClose"===i[0]?window.__wcf_bc_datePicker.setCloseCallback(t,i[1]):console.warn("datepicker('setOption') supports only 'onClose'.");break;default:console.debug("Unsupported method '"+e+"' for datepicker()")}return this}}),jQuery.fn.extend({wcfTabs:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);require(["Dom/Util","WoltLabSuite/Core/Ui/TabMenu"],function(n,s){var o=s.getTabMenu(n.identify(t));null!==o&&o[e].apply(o,i)})}}),$.widget("ui.wcfPages",{_api:null,SHOW_LINKS:11,SHOW_SUB_LINKS:20,options:{activePage:1,maxPage:1},_create:function(){require(["WoltLabSuite/Core/Ui/Pagination"],function(e){this._api=new e(this.element[0],{activePage:this.options.activePage,maxPage:this.options.maxPage,callbackShouldSwitch:function(e){return!1!==this._trigger("shouldSwitch",void 0,{nextPage:e})}.bind(this),callbackSwitch:function(e){this._trigger("switched",void 0,{activePage:e})}.bind(this)})}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this._api=null,this.element[0].innerHTML=""},_setOption:function(e,t){if("activePage"==e&&t!=this.options[e]&&t>0&&t<=this.options.maxPage){var i=this._trigger("shouldSwitch",void 0,{nextPage:t});i||void 0!==i?this._api.switchPage(t):this._trigger("notSwitched",void 0,{activePage:t})}return this}}),WCF.Category={},WCF.Category.NestedList=Class.extend({_categories:{},init:function(){},_updateSelection:function(){}}),WCF.Category.FlexibleCategoryList=Class.extend({_list:{},_categories:{},init:function(){},_buildStructure:function(){},_updateSelection:function(){}}),WCF.Condition={},WCF.Notice={}; })(this);
+(function (window, undefined) { "use strict";function wcfEval(expression){return eval(expression)}!function(){var e=jQuery.fn.data;jQuery.fn.data=function(t,i){var n=[].slice.call(arguments);if(t)switch(typeof t){case"object":for(var s in t)if(s.match(/ID$/)){var o=t[s];delete t[s],s=s.replace(/ID$/,"-id"),t[s]=o}n[0]=t;break;case"string":t.match(/ID$/)&&(n[0]=t.replace(/ID$/,"-id"))}var a=e.apply(this,n);if(void 0===t)for(var s in a)s.match(/Id$/)&&(a[s.replace(/Id$/,"ID")]=a[s],delete a[s]);return a},window.console||(window.console={});for(var t=["log","info","warn","exception","assert","dir","dirxml","trace","group","groupEnd","groupCollapsed","profile","profileEnd","count","clear","time","timeEnd","timeStamp","table","error"],i=0;i<t.length;i++)void 0===console[t[i]]&&(console[t[i]]=function(){});void 0===console.debug&&(console.debug=function(e){console.log(e)})}(),window.shuffle=function(e){for(var t,i,n=e.length;0!==n;)i=Math.floor(Math.random()*n),n-=1,t=e[n],e[n]=e[i],e[i]=t;return this},function(e){var t=navigator.userAgent.toLowerCase(),i=/(chrome)[ \/]([\w.]+)/.exec(t)||/(webkit)[ \/]([\w.]+)/.exec(t)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(t)||/(msie) ([\w.]+)/.exec(t)||t.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(t)||[],n={browser:i[1]||"",version:i[2]||"0"},s={};n.browser&&(s[n.browser]=!0,s.version=n.version),s.chrome?s.webkit=!0:s.webkit&&(s.safari=!0),e.browser=e.browser||{},e.browser=$.extend(e.browser,s),e.browser.touch=!!("ontouchstart"in window)||!!("msMaxTouchPoints"in window.navigator)&&window.navigator.msMaxTouchPoints>0,e.browser.smartphone="bottom"==$("html").css("caption-side"),e.browser.mozilla&&t.match(/trident/)&&(e.browser.mozilla=!1,e.browser.msie=!0),e.browser.iOS=/\((ipad|iphone|ipod);/.test(t),e.browser.iOS&&$("html").addClass("iOS"),e.browser.android=-1!==t.indexOf("android"),e.browser.editor="redactor",e.browser.ckeditor=!1,e.browser.redactor=!0,e.browser.iOS&&(e.fn.focus=function(e,t){return arguments.length>0?this.on("focus",null,e,t):this.trigger("focus")})}(jQuery),null==window.WCF&&(window.WCF={}),$.extend(!0,{removeArrayValue:function(e,t){return $.grep(e,function(e,i){return t!==e})},wcfEscapeID:function(e){return e.replace(/(:|\.)/g,"\\$1")},wcfIsset:function(e){return!!$("#"+$.wcfEscapeID(e)).length},getLength:function(e){var t=0;for(var i in e)e.hasOwnProperty(i)&&t++;return t}}),$.fn.extend({getTagName:function(){return this.length?this.get(0).tagName.toLowerCase():""},getDimensions:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"inner":i={height:this.innerHeight(),width:this.innerWidth()};break;case"outer":i={height:this.outerHeight(),width:this.outerWidth()};break;default:i={height:this.height(),width:this.width()}}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},getOffsets:function(e){var t={},i={},n=!1;switch(this.is(":hidden")&&(t=WCF.getInlineCSS(this),n=!0,this.css({display:"block",visibility:"hidden"})),e){case"offset":i=this.offset();break;case"position":default:i=this.position()}return n&&WCF.revertInlineCSS(this,t,["display","visibility"]),i},makePositioned:function(e,t){"absolute"!=e&&"fixed"!=e&&(e="absolute");var i=this.getOffsets("position");return this.css({position:e,left:i.left,margin:0,top:i.top}),t&&this.remove().appentTo("body"),this},disable:function(){return this.attr("disabled","disabled")},enable:function(){return this.removeAttr("disabled")},wcfIdentify:function(){return window.bc_wcfDomUtil.identify(this[0])},getCaret:function(){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return-1}else if(!this.is("textarea"))return-1;var e=0,t=this.get(0);if(document.selection){this.focus();var i=document.selection.createRange();i.moveStart("character",-this.val().length),e=i.text.length}else(t.selectionStart||"0"==t.selectionStart)&&(e=parseInt(t.selectionStart));return e},setCaret:function(e){if(this.is("input")){if("text"!=this.attr("type")&&"password"!=this.attr("type"))return!1}else if(!this.is("textarea"))return!1;var t=this.get(0);if(this.focus(),document.selection){var i=document.selection.createRange();i.moveStart("character",e),i.moveEnd("character",0),i.select()}else(t.selectionStart||"0"==t.selectionStart)&&(t.selectionStart=e,t.selectionEnd=e);return!0},wcfDropIn:function(e,t,i){return e||(e="up"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfDropOut:function(e,t,i){return e||(e="down"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"drop"),{direction:e},i,t)},wcfBlindIn:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.show(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfBlindOut:function(e,t,i){return e||(e="vertical"),i&&parseInt(i)||(i=200),this.hide(WCF.getEffect(this,"blind"),{direction:e},i,t)},wcfHighlight:function(e,t){return this.effect("highlight",e,600,t)},wcfFadeIn:function(e,t){return t&&parseInt(t)||(t=200),this.show(WCF.getEffect(this,"fade"),{},t,e)},wcfFadeOut:function(e,t){return t&&parseInt(t)||(t=200),this.hide(WCF.getEffect(this,"fade"),{},t,e)},cssAsNumber:function(e){if(this.length){var t=this.css(e);if(void 0!==t)return parseInt(t.replace(/px$/,""))}return 0},perfectScrollbar:function(e){var t=require("perfect-scrollbar");return this.each(function(){if("object"==typeof e||void 0===e){var i=e;$(this).data("psID")||t.initialize(this,i)}else{var n=e;"update"===n?t.update(this):"destroy"===n&&t.destroy(this)}return jQuery(this)})}}),$.extend(WCF,{activeDialogs:0,_idCounter:0,getRandomID:function(){return window.bc_wcfDomUtil.getUniqueId()},inArray:function(e,t){return-1!=$.inArray(e,t)},getEffect:function(e,t){return e.is("tr")?"highlight":t},getInlineCSS:function(e){var t={},i=e.attr("style");if(!i)return{};i=i.split(";");for(var n=0,s=i.length;n<s;n++){var o=$.trim(i[n]);""!=o&&(o=o.split(":"),t[$.trim(o[0])]=$.trim(o[1]))}return t},revertInlineCSS:function(e,t,i){for(var n=0,s=i.length;n<s;n++){var o=i[n];t[o]?e.css(o,t[o]):e.css(o,"")}},getUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)})},base64toBlob:function(e,t,i){t=t||"",i=i||512;for(var n=atob(e),s=[],o=0;o<n.length;o+=i){for(var a=n.slice(o,o+i),r=new Array(a.length),c=0;c<a.length;c++)r[c]=a.charCodeAt(c);var l=new Uint8Array(r);s.push(l)}return new Blob(s,{type:t})},convertLegacyURL:function(e){return e.replace(/^index\.php\/(.*?)\/\?/,function(e,t){for(var i=t.split(/([A-Z][a-z0-9]+)/),n="",s=0,o=i.length;s<o;s++){var a=i[s].trim();a.length&&(n.length&&(n+="-"),n+=a.toLowerCase())}return"index.php?"+n+"/&"})}}),WCF.Browser={_isChrome:null,isChrome:function(){return null===this._isChrome&&(this._isChrome=!1,/chrom(e|ium)/.test(navigator.userAgent.toLowerCase())&&(this._isChrome=!0)),this._isChrome}},WCF.Dropdown={init:function(e){window.bc_wcfSimpleDropdown.initAll()},initDropdown:function(e,t){window.bc_wcfSimpleDropdown.init(e[0],t)},removeDropdown:function(e){window.bc_wcfSimpleDropdown.destroy(e)},initDropdownFragment:function(e,t){window.bc_wcfSimpleDropdown.initFragment(e[0],t[0])},registerCallback:function(e,t){window.bc_wcfSimpleDropdown.registerCallback(e,t)},_toggle:function(e,t){window.bc_wcfSimpleDropdown._toggle(e,t)},toggleDropdown:function(e,t){window.bc_wcfSimpleDropdown._toggle(null,e,null,t)},getDropdown:function(e){var t=window.bc_wcfSimpleDropdown.getDropdown(e);return t?$(t):null},getDropdownMenu:function(e){var t=window.bc_wcfSimpleDropdown.getDropdownMenu(e);return t?$(t):null},setAlignmentByID:function(e){window.bc_wcfSimpleDropdown.setAlignmentById(e)},setAlignment:function(e,t){window.bc_wcfSimpleDropdown.setAlignment(e[0],t[0])},_closeAll:function(){window.bc_wcfSimpleDropdown.closeAll()},close:function(e){window.bc_wcfSimpleDropdown.close(e)},destroy:function(e){window.bc_wcfSimpleDropdown.destroy(e)}},WCF.Dropdown.Interactive={},WCF.Dropdown.Interactive.Handler={_dropdownContainer:{},_dropdownMenus:{},create:function(){},open:function(){},close:function(){},closeAll:function(){},getOpenDropdown:function(){},getDropdown:function(){}},WCF.Dropdown.Interactive.Instance=Class.extend({_container:{},_itemList:{},_linkList:{},_options:{},_pointer:{},_triggerElement:{},init:function(){},getContainer:function(){},getItemList:function(){},getLinkList:function(){},open:function(){},close:function(){},isOpen:function(){},toggle:function(){},resetItems:function(){},render:function(){},rebuildScrollbar:function(){}}),WCF.Clipboard={init:function(){},reload:function(){}},WCF.PeriodicalExecuter=Class.extend({_callback:null,_delay:0,_intervalID:null,_isExecuting:!1,init:function(e,t){if(!$.isFunction(e))return void console.debug("[WCF.PeriodicalExecuter] Given callback is invalid, aborting.");this._callback=e,this._interval=t,this.resume()},_execute:function(){if(!this._isExecuting)try{this._isExecuting=!0,this._callback(this),this._isExecuting=!1}catch(e){throw this._isExecuting=!1,e}},stop:function(){this._intervalID&&clearInterval(this._intervalID)},resume:function(){this.restart()},restart:function(){this._intervalID&&this.stop(),this._intervalID=setInterval($.proxy(this._execute,this),this._interval)},setInterval:function(e){this._interval=e,this.restart()}}),WCF.LoadingOverlayHandler={show:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.show()})},hide:function(){require(["WoltLabSuite/Core/Ajax/Status"],function(e){e.hide()})},updateIcon:function(e,t){var i=void 0===t||t?"addClass":"removeClass";e.find(".icon")[i]("fa-spinner"),e.hasClass("icon")&&e[i]("fa-spinner")}},WCF.Action={},WCF.Action.Proxy=Class.extend({_ajaxRequest:null,init:function(e){this._ajaxRequest=null,e=$.extend(!0,{autoSend:!1,data:{},dataType:"json",after:null,init:null,jsonp:"callback",async:!0,failure:null,showLoadingOverlay:!0,success:null,suppressErrors:!1,type:"POST",url:"index.php?ajax-proxy/&t="+SECURITY_TOKEN,aborted:null,autoAbortPrevious:!1},e),"jsonp"===e.dataType?require(["AjaxJsonp"],function(t){t.send(e.url,e.success,e.failure,{parameterName:e.jsonp})}):require(["AjaxRequest"],function(t){this._ajaxRequest=new t({data:e.data,type:e.type,url:e.url,withCredentials:e.url==="index.php?ajax-proxy/&t="+SECURITY_TOKEN,responseType:"json"===e.dataType?"application/json":"",autoAbort:e.autoAbortPrevious,ignoreError:e.suppressErrors,silent:!e.showLoadingOverlay,failure:e.failure,finalize:e.after,success:e.success}),e.autoSend&&this._ajaxRequest.sendRequest()}.bind(this))},sendRequest:function(e){require(["AjaxRequest"],function(t){null!==this._ajaxRequest&&this._ajaxRequest.sendRequest(e)}.bind(this))},abortPrevious:function(){require(["AjaxRequest"],function(e){null!==this._ajaxRequest&&this._ajaxRequest.abortPrevious()}.bind(this))},setOption:function(e,t){require(["AjaxRequest"],function(i){null!==this._ajaxRequest&&this._ajaxRequest.setOption(e,t)}.bind(this))},showLoadingOverlayOnce:function(){},suppressErrors:function(){},_failure:function(e,t,i){},_success:function(e,t,i){},_after:function(){}}),WCF.Action.SimpleProxy=Class.extend({init:function(e,t){this.options=$.extend(!0,{action:"",className:"",elements:null,eventName:"click"},e),this.callbacks=$.extend(!0,{after:null,failure:null,init:null,success:null},t),this.options.elements&&(this.proxy=new WCF.Action.Proxy(this.callbacks),this.options.elements.each($.proxy(function(e,t){$(t).bind(this.options.eventName,$.proxy(this._handleEvent,this))},this)))},_handleEvent:function(e){this.proxy.setOption("data",{actionName:this.options.action,className:this.options.className,objectIDs:[$(e.target).data("objectID")]}),this.proxy.sendRequest()}}),WCF.Action.Delete=Class.extend({_buttonSelector:"",_callback:{},_className:"",_containerSelector:"",_containers:{},init:function(){},_initElements:function(){},_click:function(){},_didTriggerEffect:function(){},_execute:function(){},_sendRequest:function(){},_success:function(){},setCallback:function(){},triggerEffect:function(){}}),WCF.Action.NestedDelete=WCF.Action.Delete.extend({triggerEffect:function(){},_buttonSelector:"",_callback:{},_className:"",_containerSelector:"",_containers:{},init:function(){},_initElements:function(){},_click:function(){},_didTriggerEffect:function(){},_execute:function(){},_sendRequest:function(){},_success:function(){},setCallback:function(){}}),WCF.Action.Toggle=Class.extend({_buttonSelector:"",_className:"",_containerSelector:"",_containers:{},init:function(){},_initElements:function(){},_click:function(){},_execute:function(){},_sendRequest:function(){},_success:function(){},triggerEffect:function(){},_toggleButton:function(){}}),WCF.Action.Scroll=Class.extend({_callback:null,_reference:null,_target:null,_threshold:0,init:function(e,t,i,n){return this._threshold=parseInt(e),0===this._threshold?void console.debug("[WCF.Action.Scroll] Given threshold is invalid, aborting."):($.isFunction(t)&&(this._callback=t),null===this._callback?void console.debug("[WCF.Action.Scroll] Given callback is invalid, aborting."):(this._reference=$(i||window),this._target=$(n||document),this.start(),void this._scroll()))},_scroll:function(){var e=this._target.height(),t=this._reference.scrollTop();e-(this._reference.height()+t)<this._threshold&&this._callback(this)},start:function(){this._reference.on("scroll",$.proxy(this._scroll,this))},stop:function(){this._reference.off("scroll")}}),WCF.Date={},WCF.Date.Picker={init:function(){}},WCF.Date.Util={gmdate:function(e){var t=e||new Date;return Math.round(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDay(),t.getUTCHours(),t.getUTCMinutes(),t.getUTCSeconds())/1e3)},getTimezoneDate:function(e,t){var i=new Date(e),n=6e4*i.getTimezoneOffset();return new Date(e+n+t)}},WCF.Dictionary=Class.extend({_variables:{},init:function(){this._variables={}},add:function(e,t){this._variables[e]=t},addObject:function(e){for(var t in e)this.add(t,e[t])},addDictionary:function(e){e.each($.proxy(function(e){this.add(e.key,e.value)},this))},get:function(e){return this.isset(e)?this._variables[e]:null},isset:function(e){return this._variables.hasOwnProperty(e)},remove:function(e){delete this._variables[e]},each:function(e){if($.isFunction(e))for(var t in this._variables){var i=this._variables[t],n={key:t,value:i};e(n)}},count:function(){return $.getLength(this._variables)},isEmpty:function(){return!this.count()}}),null==window.WCF.Language&&(WCF.Language={add:function(e,t){require(["Language"],function(i){i.add(e,t)})},addObject:function(e){require(["Language"],function(t){t.addObject(e)})},get:function(e,t){throw new Error('Call to deprecated WCF.Language.get("'+e+'")')}}),WCF.Number={round:function(e,t){return t=Math.pow(10,t||0),Math.round(e*t)/t}},WCF.String={addThousandsSeparator:function(e){return String(e).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g,"$1"+WCF.Language.get("wcf.global.thousandsSeparator"))},escapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")},escapeRegExp:function(e){return String(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},formatNumeric:function(e,t){e=String(WCF.Number.round(e,t||2));var i=e.split(".");return e=this.addThousandsSeparator(i[0]),i.length>1&&(e+=WCF.Language.get("wcf.global.decimalPoint")+i[1]),e=e.replace("-","−")},lcfirst:function(e){return String(e).substring(0,1).toLowerCase()+e.substring(1)},ucfirst:function(e){return String(e).substring(0,1).toUpperCase()+e.substring(1)},unescapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">")}},WCF.TabMenu={init:function(){require(["WoltLabSuite/Core/Ui/TabMenu"],function(e){e.setup()})},reload:function(){this.init()}},WCF.Template=Class.extend({init:function(e){var t=new WCF.Dictionary,i=0;e=e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/g,"\\n"),e=e.replace(/\{literal\}(.*?)\{\/literal\}/g,$.proxy(function(e){var i="@@@@@@@@@@@"+Math.random()+"@@@@@@@@@@@";return t.add(i,e.replace(/\{\/?literal\}/g,"")),i},this)),e=e.replace(/\{\*.*?\*\}/g,"");var n=function(e){for(var t=e.split(""),i={},n=!0,s="",o="",a=!1,r=!1,c=!1,l=0,u=t.length;l<u;l++){var h=t[l];n&&"="!=h&&" "!=h?s+=h:n&&"="==h?(n=!1,r=!1,a=!1,c=!1):n||r||a||" "!=h?n||!r||c||"'"!=h?n||r||a||"'"!=h?n||!a||c||'"'!=h?n||r||a||'"'!=h?n||!a&&!r||c||"\\"!=h?n||(c=!1,o+=h):(c=!0,o+=h):(a=!0,o+=h):(a=!1,o+=h):(r=!0,o+=h):(r=!1,o+=h):(n=!0,i[s]=o,o=s="")}if(i[s]=o,a||r||c)throw new Error('Syntax error in parameterList: "'+e+'"');return i},s=function(e){return e.replace(/\\n/g,"\n").replace(/\\\\/g,"\\").replace(/\\'/g,"'")};e=e.replace(/\{(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.escapeHTML("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{#(\$[^\}]+?)\}/g,function(e,t){return"' + WCF.String.formatNumeric("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") + '"}).replace(/\{@(\$[^\}]+?)\}/g,function(e,t){return"' + "+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+" + '"}).replace(/\{lang\}(.+?)\{\/lang\}/g,function(e,t){return"' + WCF.Language.get('"+t+"', v) + '"}).replace(/\{include (.+?)\}/g,function(e,t){t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var i=n(t);if(void 0===i.file)throw new Error("Missing file attribute in include-tag");return i.file=i.file.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"' + "+i.file+".fetch(v) + '"}).replace(/\{if (.+?)\}/g,function(e,t){return"';\nif ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{else ?if (.+?)\}/g,function(e,t){return"';\n}\nelse if ("+(t=s(t.replace(/\$([^.\[\(\)\]\s]+)/g,"(v['$1'])")))+") {\n\t$output += '"}).replace(/\{implode (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in implode-tag");if(void 0===s.item)throw new Error("Missing item attribute in implode-tag");return void 0===s.glue&&(s.glue="', '"),s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\nvar $implode_"+i+" = false;\nfor ($implodeKey_"+i+" in "+s.from+") {\n\tv["+s.item+"] = "+s.from+"[$implodeKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $implodeKey_"+i+";\n":"")+"\tif ($implode_"+i+") $output += "+s.glue+";\n\t$implode_"+i+" = true;\n\t$output += '"}).replace(/\{foreach (.+?)\}/g,function(e,t){i++,t=t.replace(/\\\\/g,"\\").replace(/\\'/g,"'");var s=n(t);if(void 0===s.from)throw new Error("Missing from attribute in foreach-tag");if(void 0===s.item)throw new Error("Missing item attribute in foreach-tag");return s.from=s.from.replace(/\$([^.\[\(\)\]\s]+)/g,"(v.$1)"),"';\n$foreach_"+i+" = false;\nfor ($foreachKey_"+i+" in "+s.from+") {\n\t$foreach_"+i+" = true;\n\tbreak;\n}\nif ($foreach_"+i+") {\n\tfor ($foreachKey_"+i+" in "+s.from+") {\n\t\tv["+s.item+"] = "+s.from+"[$foreachKey_"+i+"];\n"+(void 0!==s.key?"\t\tv["+s.key+"] = $foreachKey_"+i+";\n":"")+"\t\t$output += '"}).replace(/\{foreachelse\}/g,"';\n\t}\n}\nelse {\n\t{\n\t\t$output += '").replace(/\{\/foreach\}/g,"';\n\t}\n}\n$output += '").replace(/\{else\}/g,"';\n}\nelse {\n\t$output += '").replace(/\{\/(if|implode)\}/g,"';\n}\n$output += '");for(var o in WCF.Template.callbacks)e=WCF.Template.callbacks[o](e);e=e.replace("{ldelim}","{").replace("{rdelim}","}"),t.each(function(t){e=e.replace(t.key,t.value)}),e="$output += '"+e+"';";try{this.fetch=new Function("v","v = window.$.extend({}, v, { __wcf: window.WCF, __window: window }); var $output = ''; "+e+" return $output;")}catch(t){throw console.debug("var $output = ''; "+e+" return $output;"),t}},fetch:function(e){}}),WCF.Template.callbacks=[],WCF.ToggleOptions=Class.extend({_element:null,_showItems:[],_hideItems:[],_callback:null,init:function(e,t,i,n){this._element=$("#"+e),this._showItems=t,this._hideItems=i,void 0!==n&&(this._callback=n),this._element.click($.proxy(this._toggle,this)),this._toggle()},_toggle:function(){if(this._element.prop("checked")){for(var e=0,t=this._showItems.length;e<t;e++){var i=this._showItems[e];$("#"+i).show()}for(var e=0,t=this._hideItems.length;e<t;e++){var i=this._hideItems[e];$("#"+i).hide()}null!==this._callback&&this._callback()}}}),WCF.Collapsible={},WCF.Collapsible.Simple={init:function(){$(".jsCollapsible").each($.proxy(function(e,t){this._initButton(t)},this))},_initButton:function(e){var t=$(e);t.data("isOpen")||$("#"+t.data("collapsibleContainer")).hide(),t.click($.proxy(this._toggle,this))},_toggle:function(e){var t=$(e.currentTarget),i=t.data("isOpen"),n=$("#"+$.wcfEscapeID(t.data("collapsibleContainer")));return i?(n.stop().wcfBlindOut("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!1):(n.stop().wcfBlindIn("vertical",$.proxy(function(){this._toggleImage(t)},this)),i=!0),t.data("isOpen",i),e.stopPropagation(),!1},_toggleImage:function(e){var t=e.find("span.icon");e.data("isOpen")?t.removeClass("fa-chevron-right").addClass("fa-chevron-down"):t.removeClass("fa-chevron-down").addClass("fa-chevron-right")}},WCF.Collapsible.Remote=Class.extend({_className:"",_containers:{},_containerData:{},_proxy:null,init:function(e){this._className=e,this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._init(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Collapsible.Remote",$.proxy(this._init,this))},_init:function(e){this._getContainers().each($.proxy(function(e,t){var i=$(t),n=i.wcfIdentify();void 0===this._containers[n]&&(this._containers[n]=i,this._initContainer(n))},this))},_initContainer:function(e){var t=this._getTarget(e),i=this._getButtonContainer(e),n=this._createButton(e,i);this._containerData[e]={button:n,buttonContainer:i,isOpen:this._containers[e].data("isOpen"),target:t},this._containers[e].data("isOpen")||$("#"+e).addClass("jsCollapsed")},_getContainers:function(){},_getTarget:function(e){},_getButtonContainer:function(e){},_createButton:function(e,t){var i=elBySel(".jsStaticCollapsibleButton",t[0]);return null!==i&&i.parentNode===t[0]?(i.classList.remove("jsStaticCollapsibleButton"),i=$(i)):i=$('<span class="collapsibleButton jsTooltip pointer icon icon16 fa-chevron-down" title="'+WCF.Language.get("wcf.global.button.collapsible")+'">').prependTo(t),i.data("containerID",e).click($.proxy(this._toggleContainer,this)),i},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",o=n?"close":"open";this._proxy.setOption("data",{actionName:"loadContainer",className:this._className,interfaceName:"wcf\\data\\ILoadableContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:o},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),$("#"+i).toggleClass("jsCollapsed")},_exchangeIcon:function(e,t){t=t||"spinner",e.removeClass("fa-chevron-down fa-chevron-right fa-spinner").addClass("fa-"+t)},_getObjectID:function(e){return $("#"+e).data("objectID")},_getAdditionalParameters:function(e){return{}},_updateContent:function(e,t,i){this._containerData[e].target.html(t)},_success:function(e,t,i){if(e.returnValues.containerID){var n=e.returnValues.containerID;if(this._containers[n]){this._containerData[n].isOpen=!!e.returnValues.isOpen;var s=e.returnValues.isOpen?"open":"close";this._updateContent(n,$.trim(e.returnValues.content),s)}}}}),WCF.Collapsible.SimpleRemote=WCF.Collapsible.Remote.extend({init:function(e){this._super(e),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1})},_initContainer:function(e){this._super(e),this._containerData[e].isOpen||(this._containerData[e].target.hide(),this._exchangeIcon(this._containerData[e].button,"chevron-right"))},_toggleContainer:function(e){var t=$(e.currentTarget),i=t.data("containerID"),n=this._containerData[i].isOpen,s=n?"open":"close",o=n?"close":"open";this._proxy.setOption("data",{actionName:"toggleContainer",className:this._className,interfaceName:"wcf\\data\\IToggleContainerAction",objectIDs:[this._getObjectID(i)],parameters:$.extend(!0,{containerID:i,currentState:s,newState:o},this._getAdditionalParameters(i))}),this._proxy.sendRequest(),this._exchangeIcon(this._containerData[i].button,"open"===o?"chevron-down":"chevron-right"),"open"===o?this._containerData[i].target.show():this._containerData[i].target.hide(),$("#"+i).toggleClass("jsCollapsed"),this._containerData[i].isOpen="open"===o}}),WCF.User={userID:0,username:"",init:function(e,t){this.userID=e,this.username=t}},WCF.Effect={},WCF.Effect.Scroll=Class.extend({scrollTo:function(e,t,i){if(!e.length)return!0;var n=e.getOffsets("offset").top,s=$(document).height(),o=$(window).height();return n>s-o&&(n=s-o)<0&&(n=0),!0===i?$("html,body").scrollTop(n):$("html,body").animate({scrollTop:n},400,function(e,t,i,n,s){return-n*((t=t/s-1)*t*t*t-1)+i}),!1}}),WCF.CloseOverlayHandler={addCallback:function(e,t){require(["Ui/CloseOverlay"],function(i){i.add(e,t)})},removeCallback:function(e){require(["Ui/CloseOverlay"],function(t){t.remove(e)})},forceExecution:function(){require(["Ui/CloseOverlay"],function(e){e.execute()})}},WCF.DOMNodeInsertedHandler={addCallback:function(e,t){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.add("__legacy__",t)})},_executeCallbacks:function(){require(["WoltLabSuite/Core/Dom/Change/Listener"],function(e){e.trigger()})},execute:function(){this._executeCallbacks()}},WCF.DOMNodeRemovedHandler={_callbacks:new WCF.Dictionary,_isExecuting:!1,_isListening:!1,addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.DOMNodeRemovedHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){if(window.MutationObserver){new MutationObserver(function(e){var t=!1;e.forEach(function(e){e.removedNodes.length&&(t=!0)}.bind(this)),t&&this._executeCallbacks({})}.bind(this)).observe(document.body,{childList:!0,subtree:!0})}else $(document).bind("DOMNodeRemoved",$.proxy(this._executeCallbacks,this));this._isListening=!0}},_executeCallbacks:function(e){this._isExecuting||(this._isExecuting=!0,this._callbacks.each(function(t){t.value(e)}),this._isExecuting=!1)}},WCF.Option={},WCF.Option.Handler=Class.extend({init:function(){},_initOptions:function(){},_initOption:function(){},_handleChange:function(){},_change:function(){},_execute:function(){},_enableOption:function(){},_enableOptionElement:function(){},_enableOptions:function(){}}),WCF.PageVisibilityHandler={_callbacks:new WCF.Dictionary,_isListening:!1,_hiddenFieldName:"",addCallback:function(e,t){if(this._bindListener(),this._callbacks.isset(e))return console.debug("[WCF.PageVisibilityHandler] identifier '"+e+"' is already bound to a callback"),!1;this._callbacks.add(e,t)},removeCallback:function(e){this._callbacks.isset(e)&&this._callbacks.remove(e)},_bindListener:function(){if(!this._isListening){var e=null;void 0!==document.hidden?(this._hiddenFieldName="hidden",e="visibilitychange"):void 0!==document.mozHidden?(this._hiddenFieldName="mozHidden",e="mozvisibilitychange"):void 0!==document.msHidden?(this._hiddenFieldName="msHidden",e="msvisibilitychange"):void 0!==document.webkitHidden&&(this._hiddenFieldName="webkitHidden",e="webkitvisibilitychange"),null===e?console.debug("[WCF.PageVisibilityHandler] This browser does not support the page visibility API."):$(document).on(e,$.proxy(this._executeCallbacks,this)),this._isListening=!0}},_executeCallbacks:function(e){if(!this._isExecuting){this._isExecuting=!0;var t=document[this._hiddenFieldName];this._callbacks.each(function(e){e.value(t)}),this._isExecuting=!1}}},WCF.Table={},WCF.Table.EmptyTableHandler=Class.extend({_options:{},_rowClassName:"",init:function(e,t,i){this._rowClassName=t,this._tableContainer=e,this._options=$.extend(!0,{emptyMessage:null,emptyMessageHtml:null,messageType:"info",refreshPage:!1,updatePageNumber:!1,isTable:0!==this._tableContainer.find("table").length},i||{}),WCF.DOMNodeRemovedHandler.addCallback("WCF.Table.EmptyTableHandler."+t,$.proxy(this._remove,this))},_getRowCount:function(){return this._tableContainer.find((this._options.isTable?"table tr.":".tabularList .")+this._rowClassName).length},_handleEmptyTable:function(){if(this._options.emptyMessage)this._tableContainer.replaceWith($("<p />").addClass(this._options.messageType).text(this._options.emptyMessage));else if(this._options.emptyMessageHtml)this._tableContainer.replaceWith($("<p />").addClass(this._options.messageType).html(this._options.emptyMessageHtml));else if(this._options.refreshPage)if(this._options.updatePageNumber){var e=window.location.href.match(/(\?|&)pageNo=(\d+)/g);if(e){var t=e[e.length-1].match(/\d+/g);this._options.updatePageNumber>0?t++:t--,window.location=window.location.href.replace(e[e.length-1],e[e.length-1][0]+"pageNo="+t)}}else window.location.reload();else this._tableContainer.remove()},_remove:function(e){if($.getLength(e)){var t=$(e.target);if(t.hasClass(this._rowClassName))if(this._options.isTable){var i=t.parents("tbody:eq(0)");1==i.children("tr").length&&this._handleEmptyTable()}else 1===this._getRowCount()&&this._handleEmptyTable()}else this._getRowCount()||this._handleEmptyTable()}}),WCF.Search={},WCF.Search.Base=Class.extend({_callback:null,_caretAt:-1,_className:"",_commaSeperated:!1,_delay:0,_excludedSearchValues:[],_itemCount:0,_itemIndex:-1,_lastValue:"",_list:null,_oldSearchString:[],_proxy:null,_searchInput:null,_triggerLength:3,_timer:null,init:function(e,t,i,n,s){return null===t||void 0===t||$.isFunction(t)?(this._callback=t||null,this._caretAt=-1,this._delay=0,this._excludedSearchValues=[],this._lastValue="",i&&(this._excludedSearchValues=i),this._searchInput=$(e),this._searchInput.length?(this._searchInput.keydown($.proxy(this._keyDown,this)).keyup($.proxy(this._keyUp,this)).wrap('<span class="dropdown" />'),$.browser.mozilla&&$.browser.touch&&this._searchInput.on("input",$.proxy(this._keyUp,this)),this._list=$('<ul class="dropdownMenu" />').insertAfter(this._searchInput),this._commaSeperated=!!n,this._oldSearchString=[],this._itemCount=0,this._itemIndex=-1,this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!0===s,success:$.proxy(this._success,this),autoAbortPrevious:!0}),this._searchInput.is("input")&&this._searchInput.attr("autocomplete","off"),this._searchInput.blur($.proxy(this._blur,this)),void WCF.Dropdown.initDropdownFragment(this._searchInput.parent(),this._list)):void console.debug("[WCF.Search.Base] Selector '"+e+"' for search input is invalid, aborting.")):void console.debug("[WCF.Search.Base] The given callback is invalid, aborting.")},_blur:function(){var e=this;new WCF.PeriodicalExecuter(function(t){e._list.is(":visible")&&e._clearList(!1),t.stop()},250)},_keyDown:function(e){if(e.which===$.ui.keyCode.ENTER){var t=this._searchInput.parents(".dropdown");t.data("disableAutoFocus")?-1!==this._itemIndex&&e.preventDefault():(t.data("preventSubmit")||-1!==this._itemIndex)&&e.preventDefault()}},_keyUp:function(e){switch(e.which){case 37:case 39:return;case 38:return void this._selectPreviousItem();case 40:return void this._selectNextItem();case 13:return this._selectElement(e)}var t=this._getSearchString(e);if(""===t)this._clearList(!1);else if(t.length>=this._triggerLength){if(this._lastValue===t)return;this._lastValue=t;var i={data:{excludedSearchValues:this._excludedSearchValues,searchString:t}};if(this._delay){null!==this._timer&&this._timer.stop();var n=this;this._timer=new WCF.PeriodicalExecuter(function(){n._queryServer(i),n._timer.stop(),n._timer=null},this._delay)}else this._queryServer(i)}else this._clearList(!1)},_queryServer:function(e){this._searchInput.parents(".searchBar").addClass("loading"),this._proxy.setOption("data",{actionName:"getSearchResultList",className:this._className,interfaceName:"wcf\\data\\ISearchAction",parameters:this._getParameters(e)}),this._proxy.sendRequest()},setDelay:function(e){this._delay=e},_selectNextItem:function(){
+0!==this._itemCount&&(this._itemIndex++,this._itemIndex===this._itemCount&&(this._itemIndex=0),this._highlightSelectedElement())},_selectPreviousItem:function(){0!==this._itemCount&&(this._itemIndex--,-1===this._itemIndex&&(this._itemIndex=this._itemCount-1),this._highlightSelectedElement())},_highlightSelectedElement:function(){this._list.find("li").removeClass("dropdownNavigationItem"),this._list.find("li:eq("+this._itemIndex+")").addClass("dropdownNavigationItem")},_selectElement:function(e){return 0===this._itemCount||(this._list.find("li.dropdownNavigationItem").trigger("click"),!1)},_getSearchString:function(e){var t=$.trim(this._searchInput.val());if(this._commaSeperated){if((e.keyCode||e.which)==$.ui.keyCode.COMMA)return"";for(var i=t.split(","),n=i.length,s=0;s<n;s++)i[s]=$.trim(i[s]);for(var s=0;s<n;s++){var o=i[s];if(!this._oldSearchString[s]){t=o;break}if(o!=this._oldSearchString[s]){t=o,this._caretAt=s;break}}this._oldSearchString=i}return t},_getParameters:function(e){return e},_success:function(e,t,i){if(this._clearList(!1),this._searchInput.parents(".searchBar").removeClass("loading"),$.getLength(e.returnValues))for(var n in e.returnValues){var s=e.returnValues[n];this._createListItem(s)}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var o=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(o).hasClass("dropdownOpen")||(WCF.Dropdown.toggleDropdown(o,!0),this._openDropdown()),this._itemIndex=-1,WCF.Dropdown.getDropdown(o).data("disableAutoFocus")||this._selectNextItem()},_openDropdown:function(){},_handleEmptyResult:function(){return!1},_createListItem:function(e){var t=$("<li><span>"+WCF.String.escapeHTML(e.label)+"</span></li>").appendTo(this._list);return t.data("objectID",e.objectID).data("label",e.label).click($.proxy(this._executeCallback,this)),this._itemCount++,t},_executeCallback:function(e){var t=!1,i=$(e.currentTarget);if(this._commaSeperated){var n=i.data("label");this._oldSearchString[this._caretAt]=n,this._searchInput.val(this._oldSearchString.join(", ")),$.browser.webkit&&this._searchInput.css({display:"block"});var s=this._searchInput.val().toLowerCase().indexOf(n.toLowerCase())+n.length;this._searchInput.focus().setCaret(s)}else null===this._callback?this._searchInput.val(i.data("label")):t=!0===this._callback(i.data());this._clearList(t)},_clearList:function(e){e&&!this._commaSeperated&&this._searchInput.val(""),WCF.Dropdown.getDropdown(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),WCF.Dropdown.getDropdownMenu(this._searchInput.parents(".dropdown").wcfIdentify()).removeClass("dropdownOpen"),this._list.end().empty(),WCF.CloseOverlayHandler.removeCallback("WCF.Search.Base"),this._itemCount=0,this._itemIndex=-1},addExcludedSearchValue:function(e){WCF.inArray(e,this._excludedSearchValues)||this._excludedSearchValues.push(e)},removeExcludedSearchValue:function(e){var t=$.inArray(e,this._excludedSearchValues);-1!=t&&this._excludedSearchValues.splice(t,1)}}),WCF.Search.User=WCF.Search.Base.extend({_className:"wcf\\data\\user\\UserAction",_includeUserGroups:!1,init:function(e,t,i,n,s){this._includeUserGroups=i,this._super(e,t,n,s)},_getParameters:function(e){return e.data.includeUserGroups=this._includeUserGroups?1:0,e},_createListItem:function(e){var t=this._super(e),i=null;if(e.icon?i=$(e.icon):this._includeUserGroups&&"group"===e.type&&(i=$('<span class="icon icon16 fa-users" />')),i){var n=t.find("span").detach(),s=$("<div />").addClass("box16").appendTo(t);s.append(i),s.append($("<div />").append(n))}return t.data("type",e.type),t}}),WCF.System={},WCF.System.Dependency={},WCF.System.Dependency.Manager={_callbacks:{},_loaded:[],_setupCallbacks:{},register:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Callback for identifier '"+e+"' is invalid, aborting.");WCF.inArray(e,this._loaded)?setTimeout(function(){t()},1):(this._callbacks[e]||(this._callbacks[e]=[]),this._callbacks[e].push(t))},setup:function(e,t){if(!$.isFunction(t))return void console.debug("[WCF.System.Dependency.Manager] Setup callback for identifier '"+e+"' is invalid, aborting.");this._setupCallbacks[e]||(this._setupCallbacks[e]=[]),this._setupCallbacks[e].push(t)},invoke:function(e){if(this._setupCallbacks[e]){for(var t=0,i=this._setupCallbacks[e].length;t<i;t++)this._setupCallbacks[e][t]();delete this._setupCallbacks[e]}if(this._loaded.push(e),this._callbacks[e]){for(var t=0,i=this._callbacks[e].length;t<i;t++)this._callbacks[e][t]();delete this._callbacks[e]}},reset:function(e){var t=this._loaded.indexOf(e);-1!==t&&this._loaded.splice(t,1)}},WCF.System.FlexibleMenu={init:function(){},registerMenu:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.register(e)})},rebuild:function(e){require(["WoltLabSuite/Core/Ui/FlexibleMenu"],function(t){t.rebuild(e)})}},WCF.System.Mobile={},WCF.System.ObjectStore={_objects:{},add:function(e,t){void 0===this._objects[e]&&(this._objects[e]=[]),this._objects[e].push(t)},invoke:function(e,t){if(this._objects[e])for(var i=0;i<this._objects[e].length;i++)t(this._objects[e][i])}},WCF.System.Captcha={_registeredCaptchas:[],addCallback:function(e,t){require(["WoltLabSuite/Core/Controller/Captcha"],function(i){try{i.add(e,t),this._registeredCaptchas.push(e)}catch(e){if(e instanceof TypeError)return void console.debug("[WCF.System.Captcha] Given callback is no function")}}.bind(this))},getData:function(e){var t;if(-1===this._registeredCaptchas.indexOf(e))return t;var i=require("WoltLabSuite/Core/Controller/Captcha");try{t=i.getData(e)}catch(t){console.debug('[WCF.System.Captcha] Unknow captcha id "'+e+'"')}return t},removeCallback:function(e){require(["WoltLabSuite/Core/Controller/Captcha"],function(t){try{t.delete(e),this._registeredCaptchas.splice(this._registeredCaptchas.indexOf(item),1)}catch(e){}}.bind(this))}},WCF.System.Page={},WCF.System.Notification=Class.extend({_cssClassNames:"",_message:"",init:function(e,t){this._cssClassNames=t||"",this._message=e||""},show:function(e,t,i,n){require(["Ui/Notification"],function(t){t.show(i||this._message,e,n||this._cssClassNames)}.bind(this))}}),WCF.System.Confirmation={show:function(e,t,i,n,s){if("object"==typeof n){var o=$("<div />");o.append(n),n=o.html()}require(["Ui/Confirmation"],function(o){o.show({legacyCallback:t,message:e,parameters:i,template:n||"",messageIsHtml:!0===s})})}},WCF.System.DisableScrolling={_depth:0,_oldOverflow:null,disable:function(){$.browser.touch||(0===this._depth&&(this._oldOverflow=$(document.body).css("overflow"),$(document.body).css("overflow","hidden")),this._depth++)},enable:function(){0!==this._depth&&0===--this._depth&&$(document.body).css("overflow",this._oldOverflow)}},WCF.System.DisableZoom={_depth:0,_oldViewportSettings:null,disable:function(){if(0===this._depth){var e=$("meta[name=viewport]");this._oldViewportSettings=e.attr("content"),e.attr("content",this._oldViewportSettings+",maximum-scale=1")}this._depth++},enable:function(){0!==this._depth&&0===--this._depth&&$("meta[name=viewport]").attr("content",this._oldViewportSettings)}},WCF.System.Fullscreen={enterFullscreen:function(e){e.requestFullscreen?e.requestFullscreen():e.msRequestFullscreen?e.msRequestFullscreen():e.mozRequestFullScreen?e.mozRequestFullScreen():e.webkitRequestFullscreen&&e.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)},toggleFullscreen:function(e){null===this.getFullscreenElement()?this.enterFullscreen(e):this.exitFullscreen()},getFullscreenElement:function(){return document.fullscreenElement?document.fullscreenElement:document.mozFullScreenElement?document.mozFullScreenElement:document.webkitFullscreenElement?document.webkitFullscreenElement:document.msFullscreenElement?document.msFullscreenElement:null},exitFullscreen:function(){document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen()},isSupported:function(){return!!(document.documentElement.requestFullscreen||document.documentElement.msRequestFullscreen||document.documentElement.mozRequestFullScreen||document.documentElement.webkitRequestFullscreen)}},WCF.System.PageNavigation={init:function(e,t){require(["WoltLabSuite/Core/Ui/Page/JumpTo"],function(i){for(var n=elBySelAll(e),s=0,o=n.length;s<o;s++)i.init(n[s],t)})}},WCF.System.KeepAlive=Class.extend({init:function(e){new WCF.PeriodicalExecuter(function(e){new WCF.Action.Proxy({autoSend:!0,data:{actionName:"keepAlive",className:"wcf\\data\\session\\SessionAction"},failure:function(){e.stop()},showLoadingOverlay:!1,success:function(e){WCF.System.PushNotification.executeCallbacks(e)},suppressErrors:!0})},1e3*e)}}),WCF.System.PushNotification={_callbacks:{},addCallback:function(e,t){void 0===this._callbacks[e]&&(this._callbacks[e]=[]),this._callbacks[e].push(t)},executeCallbacks:function(e){for(var t in e.returnValues)if(void 0!==this._callbacks[t])for(var i=0;i<this._callbacks[t].length;i++)this._callbacks[t][i](e.returnValues[t])}},WCF.System.Event={addListener:function(e,t,i){return window.__wcf_bc_eventHandler.add(e,t,i)},removeListener:function(e,t,i){return window.__wcf_bc_eventHandler.remove(e,t,i)},removeAllListeners:function(e,t){return window.__wcf_bc_eventHandler.removeAll(e,t)},fireEvent:function(e,t,i){window.__wcf_bc_eventHandler.fire(e,t,i)}},WCF.System.Worker=Class.extend({_aborted:!1,_actionName:"",_callback:{},_className:"",_dialog:{},_proxy:{},_title:"",init:function(){},_success:function(){}}),WCF.InlineEditor=Class.extend({_callbacks:{},_dropdowns:{},_elements:{},_notification:{},_options:{},_proxy:{},_triggerElements:{},_updateData:{},init:function(){},_closeAll:function(){},_setOptions:function(){},registerCallback:function(){},_getTriggerElement:function(){},_show:function(){},_validate:function(){},_validateCallbacks:function(){},_success:function(){},_updateState:function(){},_click:function(){},_execute:function(){},_executeCallback:function(){},_hide:function(){}}),WCF.Upload=Class.extend({_name:"",_buttonSelector:{},_fileListSelector:{},_fileUpload:{},_className:"",_iframe:{},_internalFileID:0,_options:{},_uploadMatrix:{},_supportsAJAXUpload:!0,_overlay:{},init:function(){},_createButton:function(){},_insertButton:function(){},_removeButton:function(){},_upload:function(){},_createUploadMatrix:function(){},_success:function(){},_error:function(){},_progress:function(){},_getParameters:function(){},_initFile:function(){},_showOverlay:function(){},_evaluateResponse:function(){},_getFilename:function(){}}),WCF.Upload.Parallel=WCF.Upload.extend({init:function(){},_upload:function(){},_sendRequest:function(){},_createUploadMatrix:function(){},_success:function(){},_progress:function(){},_showOverlay:function(){},_evaluateResponse:function(){},_name:"",_buttonSelector:{},_fileListSelector:{},_fileUpload:{},_className:"",_iframe:{},_internalFileID:0,_options:{},_uploadMatrix:{},_supportsAJAXUpload:!0,_overlay:{},_createButton:function(){},_insertButton:function(){},_removeButton:function(){},_error:function(){},_getParameters:function(){},_initFile:function(){},_getFilename:function(){}}),WCF.Sortable={},WCF.Sortable.List=Class.extend({_additionalParameters:{},_className:"",_containerID:"",_container:{},_notification:{},_offset:0,_options:{},_proxy:{},_structure:{},init:function(){},_tableRowHelper:function(){},_submit:function(){},_success:function(){}}),WCF.Popover=Class.extend({_activeElementID:"",_identifier:"",_popoverObj:null,init:function(e){var t=!1;require(["Environment"],function(e){"desktop"!==e.platform()&&(t=!0)}.bind(this)),t||(this._activeElementID="",this._identifier=e,require(["WoltLabSuite/Core/Controller/Popover"],function(t){t.init({attributeName:"legacy",className:e,identifier:this._identifier,legacy:!0,loadCallback:this._legacyLoad.bind(this)})}.bind(this)))},_initContainers:function(){},_legacyLoad:function(e,t){this._activeElementID=e,this._popoverObj=t,this._loadContent()},_insertContent:function(e,t){this._popoverObj.setContent(this._identifier,e,t)}}),WCF.EditableItemList=Class.extend({_allowCustomInput:!1,_className:"",_data:{},_form:null,_itemList:null,_objectID:0,_objectTypeID:0,_search:null,_searchInput:null,init:function(e,t){if(this._itemList=$(e),this._searchInput=$(t),this._data={},!this._itemList.length||!this._searchInput.length)return void console.debug("[WCF.EditableItemList] Item list and/or search input do not exist, aborting.");if(this._objectID=this._getObjectID(),this._objectTypeID=this._getObjectTypeID(),this._itemList.find(".jsEditableItem").click($.proxy(this._click,this)),this._itemList.children("ul").length||$("<ul />").appendTo(this._itemList),this._itemList=this._itemList.children("ul"),this._form=this._itemList.parents("form").submit($.proxy(this._submit,this)),this._allowCustomInput){var i=this;this._searchInput.keydown($.proxy(this._keyDown,this)).keypress($.proxy(this._keyPress,this)).on("paste",function(){setTimeout(function(){i._onPaste()},100)})}this._searchInput.parents(".dropdown").data("preventSubmit",!0)},_keyDown:function(e){return null!==e||this._keyPress(null)},_keyPress:function(e){if(null===e||44===e.charCode||e.charCode===$.ui.keyCode.ENTER||$.browser.mozilla&&e.keyCode===$.ui.keyCode.ENTER){if(null!==e&&e.charCode===$.ui.keyCode.ENTER&&this._search&&-1!==this._search._itemIndex)return!1;var t=$.trim(this._searchInput.val());return e&&44===e.charCode&&(t=t.substring(0,this._searchInput.getCaret())),""===t?!0:(this.addItem({objectID:0,label:t}),e&&44===e.charCode?this._searchInput.val($.trim(this._searchInput.val().substr(this._searchInput.getCaret()))):this._searchInput.val(""),null!==e&&e.stopPropagation(),!1)}return!0},_onPaste:function(){var e=$.trim(this._searchInput.val());e=e.split(",");for(var t=0,i=e.length;t<i;t++){var n=$.trim(e[t]);""!==n&&this.addItem({objectID:0,label:n})}this._searchInput.val("")},load:function(e){},_click:function(e){var t=$(e.currentTarget),i=t.data("objectID"),n=t.data("label");return this._search&&this._search.removeExcludedSearchValue(n),this._removeItem(i,n),t.remove(),e.stopPropagation(),!1},_getObjectID:function(){return 0},_getObjectTypeID:function(){return 0},addItem:function(e){return!(!this._data[e.objectID]||0===e.objectID&&this._allowCustomInput)||($('<li class="badge">'+WCF.String.escapeHTML(e.label)+"</li>").data("objectID",e.objectID).data("label",e.label).appendTo(this._itemList).click($.proxy(this._click,this)),this._search&&this._search.addExcludedSearchValue(e.label),this._addItem(e.objectID,e.label),!0)},clearList:function(){this._itemList.children("li").each($.proxy(function(e,t){var i=$(t);this._search&&this._search.removeExcludedSearchValue(i.data("label")),i.remove(),this._removeItem(i.data("objectID"),i.data("label"))},this))},_submit:function(){this._keyDown(null)},_addItem:function(e,t){this._data[e]=t},_removeItem:function(e,t){delete this._data[e]},getSearchInput:function(){return this._searchInput}}),WCF.Language.Chooser=Class.extend({init:function(e,t,i,n,s,o){require(["WoltLabSuite/Core/Language/Chooser"],function(a){a.init(e,t,i,n,s,o)})}}),WCF.Style={},WCF.UserPanel=Class.extend({_container:null,_didLoad:!1,_link:null,_noItems:"",_revertOnEmpty:!0,init:function(e){if(this._container=$("#"+e),this._didLoad=!1,this._revertOnEmpty=!0,1!=this._container.length)return void console.debug("[WCF.UserPanel] Unable to find container identified by '"+e+"', aborting.");this._convert()},_convert:function(){this._container.addClass("dropdown"),this._link=this._container.children("a").remove();var e=$('<a href="'+this._link.attr("href")+'" class="dropdownToggle">'+this._link.html()+"</a>").appendTo(this._container).click($.proxy(this._click,this)),t=$('<ul class="dropdownMenu" />').appendTo(this._container);$('<li class="jsDropdownPlaceholder"><span>'+WCF.Language.get("wcf.global.loading")+"</span></li>").appendTo(t),this._addDefaultItems(t),this._container.dblclick($.proxy(function(){return window.location=this._link.attr("href"),!1},this)),WCF.Dropdown.initDropdown(e,!1)},_addDefaultItems:function(e){},_addDivider:function(e){$('<li class="dropdownDivider" />').appendTo(e)},_click:function(e){e.preventDefault(),this._didLoad||(new WCF.Action.Proxy({autoSend:!0,data:this._getParameters(),success:$.proxy(this._success,this)}),this._didLoad=!0)},_getParameters:function(){return{}},_success:function(e,t,i){var n=WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify());n.children(".jsDropdownPlaceholder").remove(),e.returnValues&&e.returnValues.template?($(""+e.returnValues.template).prependTo(n),this._updateBadge(e.returnValues.totalCount),this._after(n)):($("<li><span>"+WCF.Language.get(this._noItems)+"</span></li>").prependTo(n),this._updateBadge(0))},_updateBadge:function(e){if(e=parseInt(e)||0){var t=this._container.find(".badge");t.length||(t=$('<span class="badge badgeUpdate" />').appendTo(this._container.children(".dropdownToggle")),t.before(" ")),t.html(e)}else this._container.find(".badge").remove()},_after:function(e){}}),jQuery.fn.extend({wcfDialog:function(e){var t=arguments;return require(["Dom/Util","Ui/Dialog"],function(i,n){var s=i.identify(this[0]);if("close"===e)n.close(s);else if("render"===e)n.rebuild(s);else if("option"===e)3===t.length&&("title"===t[1]&&"string"==typeof t[2]?n.setTitle(s,t[2]):0===t[1].indexOf("on")?n.setCallback(s,t[1],t[2]):"closeConfirmMessage"===t[1]&&null===t[2]&&n.setCallback(s,"onBeforeClose",null));else{this[0].parentNode.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&document.body.appendChild(this[0]);var o=1===t.length&&"object"==typeof t[0]?t[0]:{};n.openStatic(s,null,o),o.hasOwnProperty("title")&&n.setTitle(s,o.title)}}.bind(this)),this}}),$.widget("ui.wcfSlideshow",{_buttonList:null,_count:0,_index:0,_itemList:null,_items:null,_timer:null,_width:0,options:{cycle:!0,cycleInterval:5,itemGap:50},_create:function(){this._itemList=this.element.children("ul"),this._items=this._itemList.children("li"),this._count=this._items.length,this._index=0,this._count>1&&this._initSlideshow()},_initSlideshow:function(){var e=$(this._items.get(0)).outerHeight();this._items.addClass("slideshowItem"),this._width=this.element.css("height",e).innerWidth(),this._itemList.addClass("slideshowItemList").css("left",0),this._items.each($.proxy(function(t,i){$(i).show().css({height:e,left:(this._width+this.options.itemGap)*t,width:this._width})},this)),this.element.css({height:e,width:this._width}).hover($.proxy(this._hoverIn,this),$.proxy(this._hoverOut,this)),this._buttonList=$('<ul class="slideshowButtonList" />').appendTo(this.element);for(var t=0;t<this._count;t++){var i=$('<li><a><span class="icon icon16 fa-circle" /></a></li>').data("index",t).click($.proxy(this._click,this)).appendTo(this._buttonList);0==t&&i.find(".icon").addClass("active")}this._resetTimer(),$(window).resize($.proxy(this._resize,this))},rebuildHeight:function(){var e=$(this._items.get(0)).css("height","auto"),t=e.outerHeight();this._items.css("height",t+"px"),this.element.css("height",t+"px")},_resize:function(){this._width=this.element.css("width","auto").innerWidth(),this._items.each($.proxy(function(e,t){$(t).css({left:(this._width+this.options.itemGap)*e,width:this._width})},this)),this._index--,this.moveTo(null)},_hoverIn:function(){null!==this._timer&&this._timer.stop()},_hoverOut:function(){this._resetTimer()},_resetTimer:function(){if(this.options.cycle){null!==this._timer&&this._timer.stop();var e=this;this._timer=new WCF.PeriodicalExecuter(function(){e.moveTo(null)},1e3*this.options.cycleInterval)}},_click:function(e){this.moveTo($(e.currentTarget).data("index")),this._resetTimer()},moveTo:function(e){this._index=null===e?this._index+1:e,this._index==this._count&&(this._index=0),$(this._buttonList.find(".icon").removeClass("active").get(this._index)).addClass("active"),this._itemList.css("left",this._index*(this._width+this.options.itemGap)*-1),this._trigger("moveTo",null,{index:this._index})},getItem:function(e){return this._items[e]?this._items[e]:null}}),jQuery.fn.extend({datepicker:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);switch(e){case"destroy":window.__wcf_bc_datePicker.destroy(t);break;case"getDate":return window.__wcf_bc_datePicker.getDate(t);case"option":if("onClose"===i[0])return i.length>1?this.datepicker("setOption","onClose",i[1]):function(){};console.warn("datepicker('option') supports only 'onClose'.");break;case"setDate":window.__wcf_bc_datePicker.setDate(t,i[0]);break;case"setOption":"onClose"===i[0]?window.__wcf_bc_datePicker.setCloseCallback(t,i[1]):console.warn("datepicker('setOption') supports only 'onClose'.");break;default:console.debug("Unsupported method '"+e+"' for datepicker()")}return this}}),jQuery.fn.extend({wcfTabs:function(e){var t=this[0],i=Array.prototype.slice.call(arguments,1);require(["Dom/Util","WoltLabSuite/Core/Ui/TabMenu"],function(n,s){var o=s.getTabMenu(n.identify(t));null!==o&&o[e].apply(o,i)})}}),$.widget("ui.wcfPages",{_api:null,SHOW_LINKS:11,SHOW_SUB_LINKS:20,options:{activePage:1,maxPage:1},_create:function(){require(["WoltLabSuite/Core/Ui/Pagination"],function(e){this._api=new e(this.element[0],{activePage:this.options.activePage,maxPage:this.options.maxPage,callbackShouldSwitch:function(e){return!1!==this._trigger("shouldSwitch",void 0,{nextPage:e})}.bind(this),callbackSwitch:function(e){this._trigger("switched",void 0,{activePage:e})}.bind(this)})}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this._api=null,this.element[0].innerHTML=""},_setOption:function(e,t){if("activePage"==e&&t!=this.options[e]&&t>0&&t<=this.options.maxPage){var i=this._trigger("shouldSwitch",void 0,{nextPage:t});i||void 0!==i?this._api.switchPage(t):this._trigger("notSwitched",void 0,{activePage:t})}return this}}),WCF.Category={},WCF.Category.NestedList=Class.extend({_categories:{},init:function(){},_updateSelection:function(){}}),WCF.Category.FlexibleCategoryList=Class.extend({_list:{},_categories:{},init:function(){},_buildStructure:function(){},_updateSelection:function(){}}),WCF.Condition={},WCF.Notice={}; })(this);
// WCF.Like.js
-(function (window, undefined) { "use strict";WCF.Like=Class.extend({_allowForOwnContent:!1,_canLike:!1,_containers:{},_containerData:{},_enableDislikes:!0,_isBusy:!1,_likeDetails:{},_proxy:null,_showSummary:!0,init:function(t,e,i,a){this._canLike=t,this._enableDislikes=e,this._isBusy=!1,this._likeDetails={},this._showSummary=i,this._allowForOwnContent=a;var s=this._getContainers();this._initContainers(s),this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)});var n=new Date,o=n.toString().hashCode+n.getUTCMilliseconds();WCF.DOMNodeInsertedHandler.addCallback("WCF.Like"+o,$.proxy(this._domNodeInserted,this))},_domNodeInserted:function(){var t=this._getContainers();this._initContainers(t)},_initContainers:function(containers){var $createdWidgets=!1;containers.each($.proxy(function(index,container){var $container=$(container),$containerID=$container.wcfIdentify();this._containers[$containerID]||(this._containers[$containerID]=$container,this._containerData[$containerID]={likeButton:null,badge:null,dislikeButton:null,likes:$container.data("like-likes"),dislikes:$container.data("like-dislikes"),objectType:$container.data("objectType"),objectID:this._getObjectID($containerID),users:eval($container.data("like-users")),liked:$container.data("like-liked")},this._createWidget($containerID),$createdWidgets=!0)},this)),$createdWidgets&&new WCF.PeriodicalExecuter(function(t){t.stop(),WCF.DOMNodeInsertedHandler.execute()},250)},_getContainers:function(){},_getWidgetContainer:function(t){},_getObjectID:function(t){},_addWidget:function(t,e){var i=this._getWidgetContainer(t);e.appendTo(i)},_buildWidget:function(t,e,i,a,s){var n=$('<aside class="likesWidget"><ul></ul></aside>');this._canLike&&(e.appendTo(n.find("ul")),i.appendTo(n.find("ul"))),a.appendTo(n),this._addWidget(t,n)},_createWidget:function(t){var e=$('<li class="wcfLikeButton"><a href="#" title="'+WCF.Language.get("wcf.like.button.like")+'" class="jsTooltip"><span class="icon icon16 fa-thumbs-o-up" /> <span class="invisible">'+WCF.Language.get("wcf.like.button.like")+"</span></a></li>"),i=$('<li class="wcfDislikeButton"><a href="#" title="'+WCF.Language.get("wcf.like.button.dislike")+'" class="jsTooltip"><span class="icon icon16 fa-thumbs-o-down" /> <span class="invisible">'+WCF.Language.get("wcf.like.button.dislike")+"</span></a></li>");this._enableDislikes||i.hide(),this._allowForOwnContent||WCF.User.userID!=this._containers[t].data("userID")||(e=$(""),i=$(""));var a=$('<a class="badge jsTooltip likesBadge" />').data("containerID",t).click($.proxy(this._showLikeDetails,this)),s=null;this._showSummary&&(s=$('<p class="likesSummary"><span class="pointer" /></p>'),s.children("span").data("containerID",t).click($.proxy(this._showLikeDetails,this))),this._buildWidget(t,e,i,a,s),this._containerData[t].likeButton=e,this._containerData[t].dislikeButton=i,this._containerData[t].badge=a,this._containerData[t].summary=s,e.data("containerID",t).data("type","like").click($.proxy(this._click,this)),i.data("containerID",t).data("type","dislike").click($.proxy(this._click,this)),this._setActiveState(e,i,this._containerData[t].liked),this._updateBadge(t),this._showSummary&&this._updateSummary(t)},_showLikeDetails:function(t,e){var i=null===t?e:$(t.currentTarget).data("containerID");void 0===this._likeDetails[i]&&(this._likeDetails[i]=new WCF.User.List("wcf\\data\\like\\LikeAction",WCF.Language.get("wcf.like.details"),{data:{containerID:i,objectID:this._containerData[i].objectID,objectType:this._containerData[i].objectType}})),this._likeDetails[i].open()},_click:function(t){t.preventDefault();var e=$(t.currentTarget);if(null===e)return void console.debug("[WCF.Like] Unable to find target button, aborting.");this._sendRequest(e.data("containerID"),e.data("type"))},_sendRequest:function(t,e){this._isBusy||(this._isBusy=!0,this._proxy.setOption("data",{actionName:e,className:"wcf\\data\\like\\LikeAction",parameters:{data:{containerID:t,objectID:this._containerData[t].objectID,objectType:this._containerData[t].objectType}}}),this._proxy.sendRequest())},_success:function(t,e,i){var a=t.returnValues.containerID;if(this._containers[a])switch(t.actionName){case"dislike":case"like":this._containerData[a].likes=parseInt(t.returnValues.likes),this._containerData[a].dislikes=parseInt(t.returnValues.dislikes),this._containerData[a].users=t.returnValues.users,$.each(this._containerData[a].users,function(t,e){e.username=WCF.String.escapeHTML(e.username)}),this._updateBadge(a),this._showSummary&&this._updateSummary(a);var s=this._containerData[a].likeButton,n=this._containerData[a].dislikeButton,o=0;t.returnValues.isLiked?o=1:t.returnValues.isDisliked&&(o=-1),this._setActiveState(s,n,o),void 0!==this._likeDetails[a]&&delete this._likeDetails[a],this._isBusy=!1}},_updateBadge:function(t){if(this._containerData[t].likes||this._containerData[t].dislikes){this._containerData[t].badge.show();var e=this._containerData[t].likes-this._containerData[t].dislikes,i=this._containerData[t].badge;i.removeClass("green red"),e>0?(i.text("+"+WCF.String.formatNumeric(e)),i.addClass("green")):e<0?(i.text(WCF.String.formatNumeric(e)),i.addClass("red")):i.text("±0");var a=this._containerData[t].likes,s=this._containerData[t].dislikes;i.attr("data-tooltip",WCF.Language.get("wcf.like.tooltip",{likes:a,dislikes:s}))}else this._containerData[t].badge.hide()},_updateSummary:function(t){if(this._containerData[t].likes){this._containerData[t].summary.show();var e=this._containerData[t].users,i=[];for(var a in e)i.push(e[a].username);var s=this._containerData[t].likes-i.length;this._containerData[t].summary.children("span").html(WCF.Language.get("wcf.like.summary",{users:i,others:s}))}else this._containerData[t].summary.hide()},_setActiveState:function(t,e,i){t.removeClass("active"),e.removeClass("active"),1==i?t.addClass("active"):-1==i&&e.addClass("active")}}); })(this);
+(function (window, undefined) { "use strict";WCF.Like=Class.extend({init:function(){throw new Error("The `WCF.Like` API is obsolete and therefore no longer supported. Please use the current API `WoltLabSuite/Core/Ui/Reaction/Handler` instead.")},_domNodeInserted:function(){},_initContainers:function(){},_getContainers:function(){},_getWidgetContainer:function(){},_getObjectID:function(){},_addWidget:function(){},_buildWidget:function(){},_createWidget:function(){},_showLikeDetails:function(){},_click:function(){},_sendRequest:function(){},_success:function(){},_updateBadge:function(){},_updateSummary:function(){},_setActiveState:function(){}}); })(this);
// WCF.ACL.js
(function (window, undefined) { "use strict";WCF.ACL={},WCF.ACL.List=Class.extend({_categoryName:"",_container:{},_containerElements:{},_objectID:0,_objectTypeID:{},_options:{},_proxy:{},_search:{},_values:{},init:function(){},_reset:function(){},_loadACL:function(){},addObject:function(){},_createListItem:function(){},_removeItem:function(){},_selectFirstEntry:function(){},_success:function(){},_parseData:function(){},_click:function(){},_select:function(){},_change:function(){},_changeAll:function(){},_setupPermissions:function(){},_savePermissions:function(){},submit:function(){},_save:function(){}}); })(this);
(function (window, undefined) { "use strict";WCF.Comment={},WCF.Comment.Handler=Class.extend({_commentButtonList:{},_comments:{},_container:null,_containerID:"",_displayedComments:0,_loadNextComments:null,_loadNextResponses:{},_proxy:null,_responses:{},_responseCache:{},_commentData:{},_guestDialog:null,_permalinkComment:null,_permalinkResponse:null,_scrollTarget:null,init:function(e){if(this._commentButtonList={},this._comments={},this._containerID=e,this._displayedComments=0,this._loadNextComments=null,this._loadNextResponses={},this._permalinkComment=null,this._permalinkResponse=null,this._responseAdd=null,this._responseCache={},this._responseRevert=null,this._responses={},this._scrollTarget=null,this._onResponsesLoaded=null,this._container=$("#"+$.wcfEscapeID(this._containerID)),!this._container.length)return void console.debug("[WCF.Comment.Handler] Unable to find container identified by '"+this._containerID+"'");if(this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._initComments(),this._initResponses(),this._container.data("canAdd")&&(null===elBySel(".commentListAddComment .wysiwygTextarea",this._container[0])?console.error("Missing WYSIWYG implementation, adding comments is not available."):require(["WoltLabSuite/Core/Ui/Comment/Add","WoltLabSuite/Core/Ui/Comment/Response/Add"],function(e,t){new e(elBySel(".jsCommentAdd",this._container[0])),this._responseAdd=new t(elBySel(".jsCommentResponseAdd",this._container[0]),{callbackInsert:function(){null!==this._responseRevert&&(this._responseRevert(),this._responseRevert=null)}.bind(this)})}.bind(this))),require(["WoltLabSuite/Core/Ui/Comment/Edit","WoltLabSuite/Core/Ui/Comment/Response/Edit"],function(e,t){new e(this._container[0]),new t(this._container[0])}.bind(this)),WCF.DOMNodeInsertedHandler.execute(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Comment.Handler",$.proxy(this._domNodeInserted,this)),WCF.System.ObjectStore.add("WCF.Comment.Handler",this),window.addEventListener("hashchange",function(){var e=window.location.hash;if(e&&e.match(/.+\/(comment\d+)/)){var t=RegExp.$1;window.setTimeout(function(){var e=elById(t);e&&e.scrollIntoView({behavior:"smooth"})},100)}}),window.location.hash.match(/^#(?:[^\/]+\/)?comment(\d+)(?:\/response(\d+))?/)){var t=elById("comment"+RegExp.$1);if(t){var n;RegExp.$2?(n=elById("comment"+RegExp.$1+"response"+RegExp.$2),n?this._scrollTo(n,!0):this._loadResponseSegment(t,RegExp.$1,RegExp.$2)):this._scrollTo(t,!0)}else this._loadCommentSegment(RegExp.$1,RegExp.$2)}},_scrollTo:function(e,t){null===this._scrollTarget&&(this._scrollTarget=elCreate("span"),this._scrollTarget.className="commentScrollTarget",document.body.appendChild(this._scrollTarget)),this._scrollTarget.style.setProperty("top",e.getBoundingClientRect().top+window.pageYOffset-49+"px",""),require(["Ui/Scroll"],function(n){n.element(this._scrollTarget,function(){t&&(e.classList.contains("commentHighlightTarget")&&(e.classList.remove("commentHighlightTarget"),e.offsetTop),e.classList.add("commentHighlightTarget"))})}.bind(this))},_loadCommentSegment:function(e,t){this._permalinkComment=elCreate("li"),this._permalinkComment.className="commentPermalinkContainer loading",this._permalinkComment.innerHTML='<span class="icon icon48 fa-spinner"></span>',this._container[0].insertBefore(this._permalinkComment,this._container[0].firstChild),this._proxy.setOption("data",{actionName:"loadComment",className:"wcf\\data\\comment\\CommentAction",objectIDs:[e],parameters:{data:{objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID"),responseID:~~t}}}),this._proxy.sendRequest()},_loadResponseSegment:function(e,t,n){this._permalinkResponse=elCreate("li"),this._permalinkResponse.className="commentResponsePermalinkContainer loading",this._permalinkResponse.innerHTML='<span class="icon icon32 fa-spinner"></span>';var s=elBySel(".commentResponseList",e);s.insertBefore(this._permalinkResponse,s.firstChild),this._proxy.setOption("data",{actionName:"loadResponse",className:"wcf\\data\\comment\\CommentAction",objectIDs:[t],parameters:{data:{objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID"),responseID:~~n}}}),this._proxy.sendRequest()},_handleLoadNextComments:function(){this._displayedComments<this._container.data("comments")?(null===this._loadNextComments&&(this._loadNextComments=$('<li class="commentLoadNext showMore"><button class="small">'+WCF.Language.get("wcf.comment.more")+"</button></li>").appendTo(this._container),this._loadNextComments.children("button").click($.proxy(this._loadComments,this))),this._loadNextComments.children("button").enable()):null!==this._loadNextComments&&this._loadNextComments.remove()},_handleLoadNextResponses:function(e){var t=this._comments[e];if(t.data("displayedResponses",t.find("ul.commentResponseList > li").length),t.data("displayedResponses")<t.data("responses")){if(void 0===this._loadNextResponses[e]){var n=t.data("responses")-t.data("displayedResponses");this._loadNextResponses[e]=$('<li class="jsCommentLoadNextResponses"><a>'+WCF.Language.get("wcf.comment.response.more",{count:n})+"</a></li>").appendTo(this._commentButtonList[e]),this._loadNextResponses[e].children("a").data("commentID",e).click($.proxy(this._loadResponses,this)),this._commentButtonList[e].parent().show()}}else void 0!==this._loadNextResponses[e]&&this._loadNextResponses[e].remove()},_loadComments:function(){this._loadNextComments.children("button").disable(),this._proxy.setOption("data",{actionName:"loadComments",className:"wcf\\data\\comment\\CommentAction",parameters:{data:{objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID"),lastCommentTime:this._container.data("lastCommentTime")}}}),this._proxy.sendRequest()},_loadResponses:function(e){this._loadResponsesExecute($(e.currentTarget).disable().data("commentID"),!1)},_loadResponsesExecute:function(e,t){this._proxy.setOption("data",{actionName:"loadResponses",className:"wcf\\data\\comment\\response\\CommentResponseAction",parameters:{data:{commentID:e,lastResponseTime:this._comments[e].data("lastResponseTime"),loadAllResponses:t?1:0}}}),this._proxy.sendRequest()},_domNodeInserted:function(){this._initComments(),this._initResponses()},_initComments:function(){var e=elBySel('link[rel="canonical"]');e=e?e.href:window.location.toString().replace(/#.+$/,"");var t=this._container[0].closest(".tabMenuContent");t&&(e+="#"+elData(t,"name"));var n=this,s=!1;this._container.find(".jsComment").each(function(t,o){var i=$(o).removeClass("jsComment"),a=i.data("commentID");n._comments[a]=i,i[0].id="comment"+a;var l=i.find("ul.commentResponseList");l.length||(l=i.find(".commentContent"));var r=$('<div class="commentOptionContainer" />').hide().insertAfter(l);n._commentButtonList[a]=$('<ul class="inlineList dotSeparated" />').appendTo(r),n._handleLoadNextResponses(a),n._initComment(a,i),n._initPermalink(i[0],e),n._displayedComments++,s=!0}),s&&this._handleLoadNextComments()},_initComment:function(e,t){if(this._container.data("canAdd")&&this._initAddResponse(e,t),t.data("canEdit")){$('<li><a href="#" class="jsCommentEditButton jsTooltip" title="'+WCF.Language.get("wcf.global.button.edit")+'"><span class="icon icon16 fa-pencil" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.edit")+"</span></a></li>").appendTo(t.find("ul.buttonList:eq(0)"))}if(t.data("canDelete")){$('<li><a href="#" class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.delete")+'"><span class="icon icon16 fa-times" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.delete")+"</span></a></li>").data("commentID",e).appendTo(t.find("ul.buttonList:eq(0)")).click($.proxy(this._delete,this))}var n=elBySel(".jsEnableComment",t[0]);n&&n.addEventListener(WCF_CLICK_EVENT,this._enableComment.bind(this))},_enableComment:function(e){e.preventDefault();var t=e.currentTarget.closest(".comment");this._proxy.setOption("data",{actionName:"enable",className:"wcf\\data\\comment\\CommentAction",objectIDs:[elData(t,"object-id")]}),this._proxy.sendRequest()},_initPermalink:function(e,t){var n=elCreate("a");n.href=t+(-1===t.indexOf("#")?"#":"/")+"comment"+elData(e,"object-id");var s=elBySel(".commentContent:not(.commentResponseContent) .containerHeadline time",e);s.parentNode.insertBefore(n,s),n.appendChild(s)},_initResponses:function(){var e=elBySel('link[rel="canonical"]');e=e?e.href:window.location.toString().replace(/#.+$/,"");var t=this._container[0].closest(".tabMenuContent");t&&(e+="#"+elData(t,"name"));for(var n in this._comments)this._comments.hasOwnProperty(n)&&elBySelAll(".jsCommentResponse",this._comments[n][0],function(t){var s=$(t).removeClass("jsCommentResponse"),o=s.data("responseID");this._responses[o]=s,t.id="comment"+n+"response"+o,this._initResponse(o,s),this._initPermalinkResponse(n,t,o,e);var i=elBySel(".jsEnableResponse",t);i&&i.addEventListener(WCF_CLICK_EVENT,this._enableCommentResponse.bind(this))}.bind(this))},_enableCommentResponse:function(e){e.preventDefault();var t=e.currentTarget.closest(".commentResponse");this._proxy.setOption("data",{actionName:"enableResponse",className:"wcf\\data\\comment\\CommentAction",parameters:{data:{responseID:elData(t,"object-id")}}}),this._proxy.sendRequest()},_initPermalinkResponse:function(e,t,n,s){var o=elCreate("a");o.href=s+(-1===s.indexOf("#")?"#":"/")+"comment"+e+"/response"+n;var i=elBySel(".commentResponseContent .containerHeadline time",t);i.parentNode.insertBefore(o,i),o.appendChild(i)},_initResponse:function(e,t){if(t.data("canEdit")){$('<li><a href="#" class="jsCommentResponseEditButton jsTooltip" title="'+WCF.Language.get("wcf.global.button.edit")+'"><span class="icon icon16 fa-pencil" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.edit")+"</span></a></li>").data("responseID",e).appendTo(t.find("ul.buttonList:eq(0)"))}if(t.data("canDelete")){var n=this;$('<li><a href="#" class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.delete")+'"><span class="icon icon16 fa-times" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.delete")+"</span></a></li>").data("responseID",e).appendTo(t.find("ul.buttonList:eq(0)")).click(function(e){n._delete(e,!0)})}},_initAddResponse:function(e,t){$('<li class="jsCommentShowAddResponse"><a>'+WCF.Language.get("wcf.comment.button.response.add")+"</a></li>").data("commentID",e).click($.proxy(this._showAddResponse,this)).appendTo(this._commentButtonList[e]);this._commentButtonList[e].parent().show()},_showAddResponse:function(e){if(e.preventDefault(),null===this._onResponsesLoaded){if(null===this._responseAdd)return void console.error("Missing response API.");var t=this._responseAdd.getContainer();if(null!==t){null!==this._responseRevert&&(this._responseRevert(),this._responseRevert=null);var n=$(e.currentTarget),s=n.data("commentID");this._onResponsesLoaded=function(){n.hide(),t.parentNode&&t.parentNode.classList.contains("jsCommentResponseAddContainer")&&elRemove(t.parentNode);var e=this._commentButtonList[s][0].closest(".commentOptionContainer");e.parentNode.insertBefore(t,e.nextSibling),"string"==typeof this._responseCache[s]?this._responseAdd.setContent(this._responseCache[s]):this._responseAdd.setContent(""),this._responseRevert=function(){this._responseCache[s]=this._responseAdd.getContent(),elRemove(t),n.show()}.bind(this),this._onResponsesLoaded=null}.bind(this),n.prev().hasClass("jsCommentLoadNextResponses")?(this._loadResponsesExecute(s,!0),n.parent().children(".button").disable()):this._onResponsesLoaded()}}},_delete:function(e,t){e.preventDefault(),WCF.System.Confirmation.show(WCF.Language.get("wcf.comment.delete.confirmMessage"),$.proxy(function(n){if("confirm"===n){var s={objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID")};!0!==t?s.commentID=$(e.currentTarget).data("commentID"):s.responseID=$(e.currentTarget).data("responseID"),this._proxy.setOption("data",{actionName:"remove",className:"wcf\\data\\comment\\CommentAction",parameters:{data:s}}),this._proxy.sendRequest()}},this))},_success:function(e,t,n){switch(e.actionName){case"enable":this._enable(e);break;case"enableResponse":this._enableResponse(e);break;case"loadComment":this._insertComment(e);break;case"loadComments":this._insertComments(e);break;case"loadResponse":this._insertResponse(e);break;case"loadResponses":this._insertResponses(e);break;case"remove":this._remove(e)}WCF.DOMNodeInsertedHandler.execute()},_enable:function(e){if(e.returnValues.commentID){var t=elBySel('.comment[data-object-id="'+e.returnValues.commentID+'"]',this._container[0]);if(t){elData(t,"is-disabled",0);var n=elBySel(".jsIconDisabled",t);n&&elRemove(n);var s=elBySel(".jsEnableComment",t);s&&elRemove(s.parentNode)}}},_enableResponse:function(e){if(e.returnValues.responseID){var t=elBySel('.commentResponse[data-object-id="'+e.returnValues.responseID+'"]',this._container[0]);if(t){elData(t,"is-disabled",0);var n=elBySel(".jsIconDisabled",t);n&&elRemove(n);var s=elBySel(".jsEnableResponse",t);s&&elRemove(s.parentNode)}}},_insertComment:function(e){if(""===e.returnValues.template)return void elRemove(this._permalinkComment);$(e.returnValues.template).insertBefore(this._permalinkComment);var t=this._permalinkComment.previousElementSibling;if(t.classList.add("commentPermalinkContainer"),elRemove(this._permalinkComment),this._permalinkComment=t,e.returnValues.response){this._permalinkResponse=elCreate("li"),this._permalinkResponse.className="commentResponsePermalinkContainer loading",this._permalinkResponse.innerHTML='<span class="icon icon32 fa-spinner"></span>';var n=elBySel(".commentResponseList",t);n.insertBefore(this._permalinkResponse,n.firstChild),this._insertResponse({returnValues:{template:e.returnValues.response}})}t.offsetTop,t.classList.add("commentHighlightTarget")},_insertResponse:function(e){if(""===e.returnValues.template)return void elRemove(this._permalinkResponse);$(e.returnValues.template).insertBefore(this._permalinkResponse);var t=this._permalinkResponse.previousElementSibling;t.classList.add("commentResponsePermalinkContainer"),elRemove(this._permalinkResponse),this._permalinkResponse=t,t.offsetTop,t.classList.add("commentHighlightTarget")},_insertComments:function(e){if($(e.returnValues.template).insertBefore(this._loadNextComments),this._container.data("lastCommentTime",e.returnValues.lastCommentTime),this._permalinkComment){var t=elData(this._permalinkComment,"object-id");null!==elBySel('.comment[data-object-id="'+t+'"]:not(.commentPermalinkContainer)',this._container[0])&&(elRemove(this._permalinkComment),this._permalinkComment=null)}this._initComments()},_insertResponses:function(e){var t=this._comments[e.returnValues.commentID];if($(e.returnValues.template).appendTo(t.find("ul.commentResponseList")),t.data("lastResponseTime",e.returnValues.lastResponseTime),this._handleLoadNextResponses(e.returnValues.commentID),this._permalinkResponse){var n=elData(this._permalinkResponse,"object-id");null!==elBySel('.commentResponse[data-object-id="'+n+'"]:not(.commentPermalinkContainer)',this._container[0])&&(elRemove(this._permalinkResponse),this._permalinkResponse=null)}null!==this._onResponsesLoaded&&this._onResponsesLoaded()},_remove:function(e){if(e.returnValues.commentID)this._comments[e.returnValues.commentID].remove(),delete this._comments[e.returnValues.commentID];else{var t=this._responses[e.returnValues.responseID],n=this._comments[t.parents("li.comment:eq(0)").data("commentID")];n.data("responses",parseInt(n.data("responses"))-1);var s=t.parent();t.remove(),s.children().length||s.empty(),delete this._responses[e.returnValues.responseID]}},_prepareEdit:function(){console.warn("This method is no longer supported.")},_keyUp:function(){console.warn("This method is no longer supported.")},_save:function(){console.warn("This method is no longer supported.")},_failure:function(){console.warn("This method is no longer supported.")},_edit:function(){console.warn("This method is no longer supported.")},_update:function(){console.warn("This method is no longer supported.")},_createGuestDialog:function(){console.warn("This method is no longer supported.")},_keyDown:function(){console.warn("This method is no longer supported.")},_submit:function(){console.warn("This method is no longer supported.")},_keyUpEdit:function(){console.warn("This method is no longer supported.")},_saveEdit:function(){console.warn("This method is no longer supported.")},_cancelEdit:function(){console.warn("This method is no longer supported.")}}),WCF.Comment.Response={}; })(this);
// WCF.ImageViewer.js
-(function (window, undefined) { "use strict";WCF.ImageViewer=Class.extend({_triggerElement:null,init:function(){this._triggerElement=$('<span class="wcfImageViewerTriggerElement" />').data("disableSlideshow",!0).hide().appendTo(document.body),this._triggerElement.wcfImageViewer({enableSlideshow:0,imageSelector:".jsImageViewerEnabled",staticViewer:!0}),WCF.DOMNodeInsertedHandler.addCallback("WCF.ImageViewer",$.proxy(this._domNodeInserted,this)),WCF.DOMNodeInsertedHandler.execute()},_domNodeInserted:function(){this._initImageSizeCheck(),this._rebuildImageViewer()},_rebuildImageViewer:function(){var i=$("a.jsImageViewer");i.length&&i.removeClass("jsImageViewer").addClass("jsImageViewerEnabled").click($.proxy(this._click,this))},_click:function(i){i.ctrlKey||(i.preventDefault(),i.stopPropagation(),$(i.currentTarget).closest(".popover").length||this._triggerElement.wcfImageViewer("open",null,$(i.currentTarget).wcfIdentify()))},_initImageSizeCheck:function(){$(".jsResizeImage").each($.proxy(function(i,e){e.complete&&this._checkImageSize({currentTarget:e})},this)),$(".jsResizeImage").on("load",$.proxy(this._checkImageSize,this))},_checkImageSize:function(i){var e=$(i.currentTarget);if(!e.is(":visible"))return void e.off("load");if(e.removeClass("jsResizeImage"),!e.closest(".messageSignature").length){var t=new Image;t.src=e.attr("src");e.closest("div.messageText, div.messageTextPreview").width()<t.width?e.parents("a").length||(e.wrap('<a href="'+e.attr("src")+'" class="jsImageViewerEnabled embeddedImageLink" />'),e.parent().click($.proxy(this._click,this)),"right"==e.css("float")?e.parent().addClass("messageFloatObjectRight"):"left"==e.css("float")&&e.parent().addClass("messageFloatObjectLeft"),e[0].style.removeProperty("float"),e[0].style.removeProperty("margin")):e.removeClass("embeddedAttachmentLink")}}}),$.widget("ui.wcfImageViewer",{_active:-1,_activeImage:null,_container:null,_didInit:!1,_disableSlideshow:!1,_eventNamespace:"",_images:[],_isMobile:!1,_isOpen:!1,_items:-1,_maxDimensions:{height:0,width:0},_proxy:null,_slideshowEnabled:!1,_thumbnailContainerWidth:0,_thumbnailMarginRight:0,_thumbnailOffset:0,_thumbnailWidth:0,_timer:null,_ui:{buttonNext:null,buttonPrevious:null,header:null,image:null,imageContainer:null,imageList:null,slideshow:{container:null,enlarge:null,next:null,previous:null,toggle:null}},options:{shiftBy:5,enableSlideshow:1,speed:5,className:"",imageSelector:"",staticViewer:!1},_create:function(){this._active=-1,this._activeImage=null,this._container=null,this._didInit=!1,this._disableSlideshow=this.element.data("disableSlideshow"),this._eventNamespace=this.element.wcfIdentify(),this._images=[],this._isMobile=!1,this._isOpen=!1,this._items=-1,this._maxDimensions={height:document.documentElement.clientHeight,width:document.documentElement.clientWidth},this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._slideshowEnabled=!1,this._thumbnailContainerWidth=0,this._thumbnailMarginRight=0,this._thumbnailOffset=0,this._thumbnaiLWidth=0,this._timer=null,this._ui={},this.element.click($.proxy(this.open,this)),window.addEventListener("popstate",function(i){if(null!=i.state&&"imageViewer"===i.state.name&&i.state.container===this._eventNamespace)return this.open(i),void this.showImage(i.state.image);this.close(i)}.bind(this))},open:function(i,e){if(i&&i.preventDefault(),this._isOpen)return!1;if(i&&"popstate"===i.type||window.history.pushState({name:"imageViewer"},"",""),this.options.staticViewer){var t=this._getStaticImages();this._initUI(),this._createThumbnails(t,!0),this._render(!0,void 0,e),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable(),$.browser.touch&&setTimeout($.proxy(function(){this._isMobile&&!this._container.hasClass("maximized")&&this._toggleView()},this),500)}else 0===this._images.length?this._loadNextImages(!0):(this._render(!1,this.element.data("targetImageID")),this._items>1&&this._slideshowEnabled&&this.startSlideshow(),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable());return this._bindListener(),document.documentElement.classList.add("pageOverlayActive"),!0},close:function(i){return i&&i.preventDefault(),i&&"popstate"===i.type?!!this._isOpen&&(this._container.removeClass("open"),null!==this._timer&&this._timer.stop(),this._unbindListener(),this._isOpen=!1,WCF.System.DisableScrolling.enable(),WCF.System.DisableZoom.enable(),document.documentElement.classList.remove("pageOverlayActive"),!0):void window.history.back()},startSlideshow:function(){return!this._disableSlideshow&&!this._slideshowEnabled&&(null===this._timer?this._timer=new WCF.PeriodicalExecuter($.proxy(function(){var i=this._active+1;i==this._items&&(i=0),this.showImage(i)},this),1e3*this.options.speed):this._timer.resume(),this._slideshowEnabled=!0,this._ui.slideshow.toggle.children("span").removeClass("fa-play").addClass("fa-pause"),!0)},stopSlideshow:function(i){return!!this._slideshowEnabled&&(this._timer.stop(),i&&this._ui.slideshow.toggle.children("span").removeClass("fa-pause").addClass("fa-play"),this._slideshowEnabled=!1,!0)},_bindListener:function(){$(document).on("keydown."+this._eventNamespace,$.proxy(this._keyDown,this)),$(window).on("resize."+this._eventNamespace,$.proxy(this._renderImage,this))},_unbindListener:function(){$(document).off("keydown."+this._eventNamespace),$(window).off("resize."+this._eventNamespace)},_keyDown:function(i){switch(i.which){case $.ui.keyCode.ESCAPE:this.close();break;case $.ui.keyCode.LEFT:this._previousImage();break;case $.ui.keyCode.RIGHT:this._nextImage();break;case $.ui.keyCode.UP:this._container.hasClass("maximized")||this._toggleView();break;case $.ui.keyCode.DOWN:this._container.hasClass("maximized")&&this._toggleView();break;case $.ui.keyCode.ENTER:var e=this._ui.header.find("h1 > a");1==e.length?window.location=e.prop("href"):this._ui.slideshow.full.trigger("click");break;case 80:this._ui.slideshow.toggle.trigger("click");break;default:return!0}return!1},_render:function(i,e,t){this._container.addClass("open");var s=null;if(i&&(s=this._ui.imageList.children("li:eq(0)"),this._thumbnailMarginRight=parseInt(s.css("marginRight").replace(/px$/,""))||0,this._thumbnailWidth=s.outerWidth(!0),this._thumbnailContainerWidth=this._ui.imageList.parent().innerWidth(),this._items>1&&this.options.enableSlideshow&&!e&&!t&&this.startSlideshow()),e)this._ui.imageList.children("li").each($.proxy(function(i,t){var s=$(t);if(s.data("objectID")==e)return s.trigger("click"),this.moveToImage(s.data("index")),!1},this));else if(t){var a=0;$(this.options.imageSelector).each(function(i,e){if($(e).wcfIdentify()==t)return a=i,!1});var n=this._ui.imageList.children("li:eq("+a+")");if(-1!==this._active){var h=!1;this._active!=n.data("index")&&(h=!0),this._ui.images[this._activeImage].prop("src")!=this._images[this._active].image.url&&(h=!0),h&&(this._active=-1)}n.trigger("click"),this.moveToImage(n.data("index"))}else null!==s&&s.trigger("click");this._toggleButtons(),this._preload()},_preload:function(){if(this._images.length<this._items){this._images.length*this._thumbnailWidth-this._thumbnailOffset<this._thumbnailContainerWidth&&this._loadNextImages(!1)}},_showImage:function(i){this.showImage($(i.currentTarget).data("index"),!0)},showImage:function(i,e){if(this._active==i)return!1;this.stopSlideshow(e||!1),-1!=this._active&&this._images[this._active].listItem.removeClass("active"),this._active=i,window.history.replaceState({name:"imageViewer",container:this._eventNamespace,image:this._active},"","");var t=this._images[i];this._ui.imageList.children("li").removeClass("active"),t.listItem.addClass("active");var s=this._ui.imageContainer.getDimensions("inner"),a=this._activeImage?0:1;null!==this._activeImage&&this._ui.images[this._activeImage].removeClass("active"),this._activeImage=a;var n=this._active;if(this._ui.imageContainer.addClass("loading"),this._ui.images[a].off("load").prop("src","data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="),this._ui.images[a].on("load",$.proxy(function(){this._imageOnLoad(n,a)},this)),this._renderImage(a,t,s),!this.options.staticViewer){this._ui.header.find("> div > a").prop("href",t.user.link).prop("title",t.user.username).children("img").prop("src",t.user.avatarURL)}var h=WCF.String.escapeHTML(t.image.title);if(t.image.link&&(h='<a href="'+t.image.link+'">'+h+"</a>"),this._ui.header.find("h1").html(h),!this.options.staticViewer){var o=t.series&&t.series.title?WCF.String.escapeHTML(t.series.title):"";t.series.link&&(o='<a href="'+t.series.link+'">'+o+"</a>"),this._ui.header.find("h2").html(o)}return this._ui.header.find("h3").text(WCF.Language.get("wcf.imageViewer.seriesIndex").replace(/{x}/,t.listItem.data("index")+1).replace(/{y}/,this._items)),this._ui.slideshow.full.data("link",t.image.fullURL?t.image.fullURL:t.image.url),this.moveToImage(t.listItem.data("index")),this._toggleButtons(),!0},_imageOnLoad:function(i,e){i==this._active&&(this._ui.imageContainer.removeClass("loading"),this._ui.images[e].addClass("active"),this.options.staticViewer&&this._renderImage(e,null),this.startSlideshow())},_renderImage:function(i,e,t){var s=!0;e||(i=this._activeImage,e=this._images[this._active],t={height:$(window).height()-(this._container.hasClass("maximized")||this._container.hasClass("wcfImageViewerMobile")?0:200),width:this._ui.imageContainer.innerWidth()},s=!1),t.height-=22,t.width-=20;var a=this._ui.images[i];if(a.prop("src")!==e.image.url&&a.prop("src",e.image.url),s&&a[0].complete&&a.trigger("load"),this.options.staticViewer&&!e.image.height&&a[0].complete)if($.browser.mozilla||$.browser.safari){var n=new Image;n.src=e.image.url,e.image.height=n.height||a[0].naturalHeight,e.image.width=n.width||a[0].naturalWidth}else a.css({height:"auto",width:"auto"}),e.image.height=a[0].height,e.image.width=a[0].width;var h=e.image.height,o=e.image.width,l=0;h>t.height&&(l=t.height/h,h=t.height,o=Math.floor(o*l)),o>t.width&&(l=t.width/o,o=t.width,h=Math.floor(h*l));var r=Math.floor((t.width-o)/2);this._ui.images[i].css({height:h+"px",left:r+10+"px",marginTop:-1*Math.round(h/2)+"px",width:o+"px"})},_initUI:function(){if(this._didInit)return!1;this._didInit=!0,this._container=$('<div class="wcfImageViewer'+(this.options.staticViewer?" wcfImageViewerStatic":"")+'" />').appendTo(document.body);var i=$("<div><img /><img /></div>").appendTo(this._container),e=$('<footer><span class="wcfImageViewerButtonPrevious icon fa-angle-double-left" /><div><ul /></div><span class="wcfImageViewerButtonNext icon fa-angle-double-right" /></footer>').appendTo(this._container),t=$("<ul />").appendTo(i),s=$('<li class="wcfImageViewerSlideshowButtonPrevious"><span class="icon icon48 fa-angle-left" /></li>').appendTo(t),a=$('<li class="wcfImageViewerSlideshowButtonToggle pointer"><span class="icon icon48 fa-play" /></li>').appendTo(t),n=$('<li class="wcfImageViewerSlideshowButtonNext"><span class="icon icon48 fa-angle-right" /></li>').appendTo(t),h=$('<li class="wcfImageViewerSlideshowButtonEnlarge pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.enlarge")+'"><span class="icon icon48 fa-expand" /></li>').appendTo(t),o=$('<li class="wcfImageViewerSlideshowButtonFull pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.full")+'"><span class="icon icon48 fa-external-link" /></li>').appendTo(t);return this._ui={buttonNext:e.children("span.wcfImageViewerButtonNext"),buttonPrevious:e.children("span.wcfImageViewerButtonPrevious"),header:$("<header><div"+(this.options.staticViewer?">":' class="box64"><a class="jsTooltip"><img /></a>')+"<div><h1 /><h2 /><h3 /></div></div></header>").appendTo(this._container),imageContainer:i,images:[i.children("img:eq(0)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")}),i.children("img:eq(1)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")})],imageList:e.find("> div > ul"),slideshow:{container:t,enlarge:h,full:o,next:n,previous:s,toggle:a}},this._ui.buttonNext.click($.proxy(this._next,this)),this._ui.buttonPrevious.click($.proxy(this._previous,this)),n.click($.proxy(this._nextImage,this)),s.click($.proxy(this._previousImage,this)),h.click($.proxy(this._toggleView,this)),a.click($.proxy(function(){this._items<2||(this._slideshowEnabled?this.stopSlideshow(!0):(this._disableSlideshow=!1,this.startSlideshow()))},this)),o.click(function(i){window.location=$(i.currentTarget).data("link")}),$('<span class="wcfImageViewerButtonClose icon icon48 fa-times pointer jsTooltip" title="'+WCF.Language.get("wcf.global.button.close")+'" />').appendTo(this._ui.header).click($.proxy(this.close,this)),$.browser.mobile||i.click(function(e){e.target===i[0]&&this.close()}.bind(this)),WCF.DOMNodeInsertedHandler.execute(),enquire.register("(max-width: 767px)",{match:$.proxy(this._enableMobileView,this),unmatch:$.proxy(this._disableMobileView,this)}),!0},_enableMobileView:function(){this._container.addClass("wcfImageViewerMobile");var i=this;this._ui.imageContainer.swipe({swipeLeft:function(e){i._container.hasClass("maximized")&&i._nextImage(e)},swipeRight:function(e){i._container.hasClass("maximized")&&i._previousImage(e)},tap:function(e,t){switch(t.tagName){case"DIV":case"IMG":i._toggleView()}}}),this._isMobile=!0},_disableMobileView:function(){this._container.removeClass("wcfImageViewerMobile"),this._ui.imageContainer.swipe("destroy"),this._isMobile=!1},_toggleView:function(){this._ui.images[this._activeImage].addClass("animateTransformation"),this._container.toggleClass("maximized"),this._ui.slideshow.enlarge.toggleClass("active").children("span").toggleClass("fa-expand").toggleClass("fa-compress"),this._renderImage(null,void 0,null)},_next:function(i,e){if(this._ui.buttonNext.hasClass("pointer")){void 0==e&&this.stopSlideshow(!0);var t=Math.max(this._items*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight,0);this._thumbnailOffset=Math.min(this._thumbnailOffset+this._thumbnailWidth*(e||this.options.shiftBy),t),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)}this._preload(),this._toggleButtons()},_previous:function(i,e){this._ui.buttonPrevious.hasClass("pointer")&&(void 0==e&&this.stopSlideshow(!0),this._thumbnailOffset=Math.max(this._thumbnailOffset-this._thumbnailWidth*(e||this.options.shiftBy),0),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)),this._toggleButtons()},_nextImage:function(i){this._ui.slideshow.next.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active+1),i&&(i.preventDefault(),i.stopPropagation()))},_previousImage:function(i){this._ui.slideshow.previous.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active-1),i&&(i.preventDefault(),i.stopPropagation()))},moveToImage:function(i){var e=(i-3)*this._thumbnailWidth,t=e+5*this._thumbnailWidth,s=this._thumbnailOffset,a=this._thumbnailOffset+this._thumbnailContainerWidth,n=!1;if((e<s||t>a)&&(n=!0),n){var h=0;if(e<s){for(;e<s;)h++,s-=this._thumbnailWidth;this._previous(null,h)}else{for(;t>a;)h++,a+=this._thumbnailWidth;this._next(null,h)}}},_toggleButtons:function(){this._thumbnailOffset>0?this._ui.buttonPrevious.addClass("pointer"):this._ui.buttonPrevious.removeClass("pointer");var i=this._images.length*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight;this._thumbnailOffset>=i?this._ui.buttonNext.removeClass("pointer"):this._ui.buttonNext.addClass("pointer"),this._active>0?this._ui.slideshow.previous.addClass("pointer"):this._ui.slideshow.previous.removeClass("pointer"),this._active+1<this._images.length?this._ui.slideshow.next.addClass("pointer"):this._ui.slideshow.next.removeClass("pointer"),this._items<2?this._ui.slideshow.toggle.removeClass("pointer"):this._ui.slideshow.toggle.addClass("pointer")},_createThumbnails:function(i){this.options.staticViewer&&(this._images=[],this._ui.imageList.empty());for(var e=0,t=i.length;e<t;e++){var s=i[e],a=$('<li class="loading pointer"><img src="'+s.thumbnail.url+'" /></li>').appendTo(this._ui.imageList);a.data("index",this._images.length).data("objectID",s.objectID).click($.proxy(this._showImage,this));var n=a.children("img");if(n.get(0).complete)a.removeClass("loading"),this.options.staticViewer&&this._fixThumbnailDimensions(n);else{var h=this;n.on("load",function(){var i=$(this);i.parent().removeClass("loading"),h.options.staticViewer&&h._fixThumbnailDimensions(i)})}s.listItem=a,this._images.push(s)}},_fixThumbnailDimensions:function(i){var e=new Image;e.src=i.prop("src");var t=e.height,s=e.width;if(t==s)t=s=80;else if(t<s){var a=80/s;s=80,t*=a}else{var a=80/t;t=80,s*=a}i.css({height:t+"px",width:s+"px"})},_loadNextImages:function(i){this._proxy.setOption("data",{actionName:"loadNextImages",className:this.options.className,interfaceName:"wcf\\data\\IImageViewerAction",objectIDs:[this.element.data("objectID")],parameters:{maximumHeight:this._maxDimensions.height,maximumWidth:this._maxDimensions.width,offset:this._images.length,targetImageID:i&&this.element.data("targetImageID")?this.element.data("targetImageID"):0}}),this._proxy.setOption("showLoadingOverlay",!1),this._proxy.sendRequest()},_getStaticImages:function(){var i=[];return $(this.options.imageSelector).each(function(e,t){var s=$(t),a=s.find("> img, .attachmentThumbnailImage > img").first();a.length||(a=s.parentsUntil(".formAttachmentList").last().find(".attachmentTinyThumbnail")),i.push({image:{fullURL:a.data("source")?a.data("source").replace(/\\\//g,"/"):s.prop("href"),link:"",title:s.prop("title"),url:s.prop("href")},series:null,thumbnail:{url:a.prop("src")},user:null})}),this._items=i.length,i},_success:function(i,e,t){i.returnValues.items&&(this._items=i.returnValues.items);var s=this._initUI();this._createThumbnails(i.returnValues.images);var a=i.returnValues.targetImageID?i.returnValues.targetImageID:0;this._render(s,a),this._isOpen||(this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable())}}); })(this);
+(function (window, undefined) { "use strict";WCF.ImageViewer=Class.extend({_triggerElement:null,init:function(){this._triggerElement=$('<span class="wcfImageViewerTriggerElement" />').data("disableSlideshow",!0).hide().appendTo(document.body),this._triggerElement.wcfImageViewer({enableSlideshow:0,imageSelector:".jsImageViewerEnabled",staticViewer:!0}),WCF.DOMNodeInsertedHandler.addCallback("WCF.ImageViewer",$.proxy(this._domNodeInserted,this)),WCF.DOMNodeInsertedHandler.execute()},_domNodeInserted:function(){this._initImageSizeCheck(),this._rebuildImageViewer()},_rebuildImageViewer:function(){var i=$("a.jsImageViewer");i.length&&i.removeClass("jsImageViewer").addClass("jsImageViewerEnabled").click($.proxy(this._click,this))},_click:function(i){i.ctrlKey||(i.preventDefault(),i.stopPropagation(),$(i.currentTarget).closest(".popover").length||this._triggerElement.wcfImageViewer("open",null,$(i.currentTarget).wcfIdentify()))},_initImageSizeCheck:function(){$(".jsResizeImage").each($.proxy(function(i,e){e.complete&&this._checkImageSize({currentTarget:e})},this)),$(".jsResizeImage").on("load",$.proxy(this._checkImageSize,this))},_checkImageSize:function(i){var e=$(i.currentTarget);if(!e.is(":visible"))return void e.off("load");if(e.removeClass("jsResizeImage"),!e.closest(".messageSignature").length){var t=new Image;t.src=e.attr("src");e.closest("div.messageText, div.messageTextPreview").width()<t.width?e.parents("a").length||(e.wrap('<a href="'+e.attr("src")+'" class="jsImageViewerEnabled embeddedImageLink" />'),e.parent().click($.proxy(this._click,this)),"right"==e.css("float")?e.parent().addClass("messageFloatObjectRight"):"left"==e.css("float")&&e.parent().addClass("messageFloatObjectLeft"),e[0].style.removeProperty("float"),e[0].style.removeProperty("margin")):e.removeClass("embeddedAttachmentLink")}}}),$.widget("ui.wcfImageViewer",{_active:-1,_activeImage:null,_container:null,_didInit:!1,_disableSlideshow:!1,_eventNamespace:"",_images:[],_isMobile:!1,_isOpen:!1,_items:-1,_maxDimensions:{height:0,width:0},_proxy:null,_slideshowEnabled:!1,_thumbnailContainerWidth:0,_thumbnailMarginRight:0,_thumbnailOffset:0,_thumbnailWidth:0,_timer:null,_ui:{buttonNext:null,buttonPrevious:null,header:null,image:null,imageContainer:null,imageList:null,slideshow:{container:null,enlarge:null,next:null,previous:null,toggle:null}},options:{shiftBy:5,enableSlideshow:1,speed:5,className:"",imageSelector:"",staticViewer:!1},_create:function(){this._active=-1,this._activeImage=null,this._container=null,this._didInit=!1,this._disableSlideshow=this.element.data("disableSlideshow"),this._eventNamespace=this.element.wcfIdentify(),this._images=[],this._isMobile=!1,this._isOpen=!1,this._items=-1,this._maxDimensions={height:document.documentElement.clientHeight,width:document.documentElement.clientWidth},this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)}),this._slideshowEnabled=!1,this._thumbnailContainerWidth=0,this._thumbnailMarginRight=0,this._thumbnailOffset=0,this._thumbnaiLWidth=0,this._timer=null,this._ui={},this.element.click($.proxy(this.open,this)),window.addEventListener("popstate",function(i){if(null!=i.state&&"imageViewer"===i.state.name&&i.state.container===this._eventNamespace)return this.open(i),void this.showImage(i.state.image);this.close(i)}.bind(this))},open:function(i,e){if(i&&i.preventDefault(),this._isOpen)return!1;if(i&&"popstate"===i.type||window.history.pushState({name:"imageViewer"},"",""),this.options.staticViewer){var t=this._getStaticImages();this._initUI(),this._createThumbnails(t,!0),this._render(!0,void 0,e),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable(),$.browser.touch&&setTimeout($.proxy(function(){this._isMobile&&!this._container.hasClass("maximized")&&this._toggleView()},this),500)}else 0===this._images.length?this._loadNextImages(!0):(this._render(!1,this.element.data("targetImageID")),this._items>1&&this._slideshowEnabled&&this.startSlideshow(),this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable());return this._bindListener(),require(["Ui/Screen"],function(i){i.pageOverlayOpen()}),!0},close:function(i){return i&&i.preventDefault(),i&&"popstate"===i.type?!!this._isOpen&&(this._container.removeClass("open"),null!==this._timer&&this._timer.stop(),this._unbindListener(),this._isOpen=!1,WCF.System.DisableScrolling.enable(),WCF.System.DisableZoom.enable(),require(["Ui/Screen"],function(i){i.pageOverlayClose()}),!0):void window.history.back()},startSlideshow:function(){return!this._disableSlideshow&&!this._slideshowEnabled&&(null===this._timer?this._timer=new WCF.PeriodicalExecuter($.proxy(function(){var i=this._active+1;i==this._items&&(i=0),this.showImage(i)},this),1e3*this.options.speed):this._timer.resume(),this._slideshowEnabled=!0,this._ui.slideshow.toggle.children("span").removeClass("fa-play").addClass("fa-pause"),!0)},stopSlideshow:function(i){return!!this._slideshowEnabled&&(this._timer.stop(),i&&this._ui.slideshow.toggle.children("span").removeClass("fa-pause").addClass("fa-play"),this._slideshowEnabled=!1,!0)},_bindListener:function(){$(document).on("keydown."+this._eventNamespace,$.proxy(this._keyDown,this)),$(window).on("resize."+this._eventNamespace,$.proxy(this._renderImage,this))},_unbindListener:function(){$(document).off("keydown."+this._eventNamespace),$(window).off("resize."+this._eventNamespace)},_keyDown:function(i){switch(i.which){case $.ui.keyCode.ESCAPE:this.close();break;case $.ui.keyCode.LEFT:this._previousImage();break;case $.ui.keyCode.RIGHT:this._nextImage();break;case $.ui.keyCode.UP:this._container.hasClass("maximized")||this._toggleView();break;case $.ui.keyCode.DOWN:this._container.hasClass("maximized")&&this._toggleView();break;case $.ui.keyCode.ENTER:var e=this._ui.header.find("h1 > a");1==e.length?window.location=e.prop("href"):this._ui.slideshow.full.trigger("click");break;case 80:this._ui.slideshow.toggle.trigger("click");break;default:return!0}return!1},_render:function(i,e,t){this._container.addClass("open");var s=null;if(i&&(s=this._ui.imageList.children("li:eq(0)"),this._thumbnailMarginRight=parseInt(s.css("marginRight").replace(/px$/,""))||0,this._thumbnailWidth=s.outerWidth(!0),this._thumbnailContainerWidth=this._ui.imageList.parent().innerWidth(),this._items>1&&this.options.enableSlideshow&&!e&&!t&&this.startSlideshow()),e)this._ui.imageList.children("li").each($.proxy(function(i,t){var s=$(t);if(s.data("objectID")==e)return s.trigger("click"),this.moveToImage(s.data("index")),!1},this));else if(t){var a=0;$(this.options.imageSelector).each(function(i,e){if($(e).wcfIdentify()==t)return a=i,!1});var n=this._ui.imageList.children("li:eq("+a+")");if(-1!==this._active){var h=!1;this._active!=n.data("index")&&(h=!0),this._ui.images[this._activeImage].prop("src")!=this._images[this._active].image.url&&(h=!0),h&&(this._active=-1)}n.trigger("click"),this.moveToImage(n.data("index"))}else null!==s&&s.trigger("click");this._toggleButtons(),this._preload()},_preload:function(){if(this._images.length<this._items){this._images.length*this._thumbnailWidth-this._thumbnailOffset<this._thumbnailContainerWidth&&this._loadNextImages(!1)}},_showImage:function(i){this.showImage($(i.currentTarget).data("index"),!0)},showImage:function(i,e){if(this._active==i)return!1;this.stopSlideshow(e||!1),-1!=this._active&&this._images[this._active].listItem.removeClass("active"),this._active=i,window.history.replaceState({name:"imageViewer",container:this._eventNamespace,image:this._active},"","");var t=this._images[i];this._ui.imageList.children("li").removeClass("active"),t.listItem.addClass("active");var s=this._ui.imageContainer.getDimensions("inner"),a=this._activeImage?0:1;null!==this._activeImage&&this._ui.images[this._activeImage].removeClass("active"),this._activeImage=a;var n=this._active;if(this._ui.imageContainer.addClass("loading"),this._ui.images[a].off("load").prop("src","data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="),this._ui.images[a].on("load",$.proxy(function(){this._imageOnLoad(n,a)},this)),this._renderImage(a,t,s),!this.options.staticViewer){this._ui.header.find("> div > a").prop("href",t.user.link).prop("title",t.user.username).children("img").prop("src",t.user.avatarURL)}var h=WCF.String.escapeHTML(t.image.title);if(t.image.link&&(h='<a href="'+t.image.link+'">'+h+"</a>"),this._ui.header.find("h1").html(h),!this.options.staticViewer){var o=t.series&&t.series.title?WCF.String.escapeHTML(t.series.title):"";t.series.link&&(o='<a href="'+t.series.link+'">'+o+"</a>"),this._ui.header.find("h2").html(o)}return this._ui.header.find("h3").text(WCF.Language.get("wcf.imageViewer.seriesIndex").replace(/{x}/,t.listItem.data("index")+1).replace(/{y}/,this._items)),this._ui.slideshow.full.data("link",t.image.fullURL?t.image.fullURL:t.image.url),this.moveToImage(t.listItem.data("index")),this._toggleButtons(),!0},_imageOnLoad:function(i,e){i==this._active&&(this._ui.imageContainer.removeClass("loading"),this._ui.images[e].addClass("active"),this.options.staticViewer&&this._renderImage(e,null),this.startSlideshow())},_renderImage:function(i,e,t){var s=!0;e||(i=this._activeImage,e=this._images[this._active],t={height:$(window).height()-(this._container.hasClass("maximized")||this._container.hasClass("wcfImageViewerMobile")?0:200),width:this._ui.imageContainer.innerWidth()},s=!1),t.height-=22,t.width-=20;var a=this._ui.images[i];if(a.prop("src")!==e.image.url&&a.prop("src",e.image.url),s&&a[0].complete&&a.trigger("load"),this.options.staticViewer&&!e.image.height&&a[0].complete)if($.browser.mozilla||$.browser.safari){var n=new Image;n.src=e.image.url,e.image.height=n.height||a[0].naturalHeight,e.image.width=n.width||a[0].naturalWidth}else a.css({height:"auto",width:"auto"}),e.image.height=a[0].height,e.image.width=a[0].width;var h=e.image.height,o=e.image.width,l=0;h>t.height&&(l=t.height/h,h=t.height,o=Math.floor(o*l)),o>t.width&&(l=t.width/o,o=t.width,h=Math.floor(h*l));var r=Math.floor((t.width-o)/2);this._ui.images[i].css({height:h+"px",left:r+10+"px",marginTop:-1*Math.round(h/2)+"px",width:o+"px"})},_initUI:function(){if(this._didInit)return!1;this._didInit=!0,this._container=$('<div class="wcfImageViewer'+(this.options.staticViewer?" wcfImageViewerStatic":"")+'" />').appendTo(document.body);var i=$("<div><img /><img /></div>").appendTo(this._container),e=$('<footer><span class="wcfImageViewerButtonPrevious icon fa-angle-double-left" /><div><ul /></div><span class="wcfImageViewerButtonNext icon fa-angle-double-right" /></footer>').appendTo(this._container),t=$("<ul />").appendTo(i),s=$('<li class="wcfImageViewerSlideshowButtonPrevious"><span class="icon icon48 fa-angle-left" /></li>').appendTo(t),a=$('<li class="wcfImageViewerSlideshowButtonToggle pointer"><span class="icon icon48 fa-play" /></li>').appendTo(t),n=$('<li class="wcfImageViewerSlideshowButtonNext"><span class="icon icon48 fa-angle-right" /></li>').appendTo(t),h=$('<li class="wcfImageViewerSlideshowButtonEnlarge pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.enlarge")+'"><span class="icon icon48 fa-expand" /></li>').appendTo(t),o=$('<li class="wcfImageViewerSlideshowButtonFull pointer jsTooltip" title="'+WCF.Language.get("wcf.imageViewer.button.full")+'"><span class="icon icon48 fa-external-link" /></li>').appendTo(t);return this._ui={buttonNext:e.children("span.wcfImageViewerButtonNext"),buttonPrevious:e.children("span.wcfImageViewerButtonPrevious"),header:$("<header><div"+(this.options.staticViewer?">":' class="box64"><a class="jsTooltip"><img /></a>')+"<div><h1 /><h2 /><h3 /></div></div></header>").appendTo(this._container),imageContainer:i,images:[i.children("img:eq(0)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")}),i.children("img:eq(1)").on("webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd",function(){$(this).removeClass("animateTransformation")})],imageList:e.find("> div > ul"),slideshow:{container:t,enlarge:h,full:o,next:n,previous:s,toggle:a}},this._ui.buttonNext.click($.proxy(this._next,this)),this._ui.buttonPrevious.click($.proxy(this._previous,this)),n.click($.proxy(this._nextImage,this)),s.click($.proxy(this._previousImage,this)),h.click($.proxy(this._toggleView,this)),a.click($.proxy(function(){this._items<2||(this._slideshowEnabled?this.stopSlideshow(!0):(this._disableSlideshow=!1,this.startSlideshow()))},this)),o.click(function(i){window.location=$(i.currentTarget).data("link")}),$('<span class="wcfImageViewerButtonClose icon icon48 fa-times pointer jsTooltip" title="'+WCF.Language.get("wcf.global.button.close")+'" />').appendTo(this._ui.header).click($.proxy(this.close,this)),$.browser.mobile||i.click(function(e){e.target===i[0]&&this.close()}.bind(this)),WCF.DOMNodeInsertedHandler.execute(),require(["Ui/Screen"],function(i){i.on("screen-sm-down",{match:$.proxy(this._enableMobileView,this),unmatch:$.proxy(this._disableMobileView,this)})}.bind(this)),!0},_enableMobileView:function(){this._container.addClass("wcfImageViewerMobile");var i=this;this._ui.imageContainer.swipe({swipeLeft:function(e){i._container.hasClass("maximized")&&i._nextImage(e)},swipeRight:function(e){i._container.hasClass("maximized")&&i._previousImage(e)},tap:function(e,t){switch(t.tagName){case"DIV":case"IMG":i._toggleView()}}}),this._isMobile=!0},_disableMobileView:function(){this._container.removeClass("wcfImageViewerMobile"),this._ui.imageContainer.swipe("destroy"),this._isMobile=!1},_toggleView:function(){this._ui.images[this._activeImage].addClass("animateTransformation"),this._container.toggleClass("maximized"),this._ui.slideshow.enlarge.toggleClass("active").children("span").toggleClass("fa-expand").toggleClass("fa-compress"),this._renderImage(null,void 0,null)},_next:function(i,e){if(this._ui.buttonNext.hasClass("pointer")){void 0==e&&this.stopSlideshow(!0);var t=Math.max(this._items*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight,0);this._thumbnailOffset=Math.min(this._thumbnailOffset+this._thumbnailWidth*(e||this.options.shiftBy),t),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)}this._preload(),this._toggleButtons()},_previous:function(i,e){this._ui.buttonPrevious.hasClass("pointer")&&(void 0==e&&this.stopSlideshow(!0),this._thumbnailOffset=Math.max(this._thumbnailOffset-this._thumbnailWidth*(e||this.options.shiftBy),0),this._ui.imageList.css("marginLeft",-1*this._thumbnailOffset)),this._toggleButtons()},_nextImage:function(i){this._ui.slideshow.next.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active+1),i&&(i.preventDefault(),i.stopPropagation()))},_previousImage:function(i){this._ui.slideshow.previous.hasClass("pointer")&&(this._disableSlideshow=!0,this.stopSlideshow(!0),this.showImage(this._active-1),i&&(i.preventDefault(),i.stopPropagation()))},moveToImage:function(i){var e=(i-3)*this._thumbnailWidth,t=e+5*this._thumbnailWidth,s=this._thumbnailOffset,a=this._thumbnailOffset+this._thumbnailContainerWidth,n=!1;if((e<s||t>a)&&(n=!0),n){var h=0;if(e<s){for(;e<s;)h++,s-=this._thumbnailWidth;this._previous(null,h)}else{for(;t>a;)h++,a+=this._thumbnailWidth;this._next(null,h)}}},_toggleButtons:function(){this._thumbnailOffset>0?this._ui.buttonPrevious.addClass("pointer"):this._ui.buttonPrevious.removeClass("pointer");var i=this._images.length*this._thumbnailWidth-this._thumbnailContainerWidth-this._thumbnailMarginRight;this._thumbnailOffset>=i?this._ui.buttonNext.removeClass("pointer"):this._ui.buttonNext.addClass("pointer"),this._active>0?this._ui.slideshow.previous.addClass("pointer"):this._ui.slideshow.previous.removeClass("pointer"),this._active+1<this._images.length?this._ui.slideshow.next.addClass("pointer"):this._ui.slideshow.next.removeClass("pointer"),this._items<2?this._ui.slideshow.toggle.removeClass("pointer"):this._ui.slideshow.toggle.addClass("pointer")},_createThumbnails:function(i){this.options.staticViewer&&(this._images=[],this._ui.imageList.empty());for(var e=0,t=i.length;e<t;e++){var s=i[e],a=$('<li class="loading pointer"><img src="'+s.thumbnail.url+'" /></li>').appendTo(this._ui.imageList);a.data("index",this._images.length).data("objectID",s.objectID).click($.proxy(this._showImage,this));var n=a.children("img");if(n.get(0).complete)a.removeClass("loading"),this.options.staticViewer&&this._fixThumbnailDimensions(n);else{var h=this;n.on("load",function(){var i=$(this);i.parent().removeClass("loading"),h.options.staticViewer&&h._fixThumbnailDimensions(i)})}s.listItem=a,this._images.push(s)}},_fixThumbnailDimensions:function(i){var e=new Image;e.src=i.prop("src");var t=e.height,s=e.width;if(t==s)t=s=80;else if(t<s){var a=80/s;s=80,t*=a}else{var a=80/t;t=80,s*=a}i.css({height:t+"px",width:s+"px"})},_loadNextImages:function(i){this._proxy.setOption("data",{actionName:"loadNextImages",className:this.options.className,interfaceName:"wcf\\data\\IImageViewerAction",objectIDs:[this.element.data("objectID")],parameters:{maximumHeight:this._maxDimensions.height,maximumWidth:this._maxDimensions.width,offset:this._images.length,targetImageID:i&&this.element.data("targetImageID")?this.element.data("targetImageID"):0}}),this._proxy.setOption("showLoadingOverlay",!1),this._proxy.sendRequest()},_getStaticImages:function(){var i=[];return $(this.options.imageSelector).each(function(e,t){var s=$(t),a=s.find("> img, .attachmentThumbnailImage > img").first();a.length||(a=s.parentsUntil(".formAttachmentList").last().find(".attachmentTinyThumbnail")),i.push({image:{fullURL:a.data("source")?a.data("source").replace(/\\\//g,"/"):s.prop("href"),link:"",title:s.prop("title"),url:s.prop("href")},series:null,thumbnail:{url:a.prop("src")},user:null})}),this._items=i.length,i},_success:function(i,e,t){i.returnValues.items&&(this._items=i.returnValues.items);var s=this._initUI();this._createThumbnails(i.returnValues.images);var a=i.returnValues.targetImageID?i.returnValues.targetImageID:0;this._render(s,a),this._isOpen||(this._isOpen=!0,WCF.System.DisableScrolling.disable(),WCF.System.DisableZoom.disable())}}); })(this);
// WCF.Label.js
-(function (window, undefined) { "use strict";WCF.Label={},WCF.Label.ACPList=Class.extend({_labelInput:{},_labelList:{},init:function(){},_keyPressed:function(){}}),WCF.Label.ACPList.Connect=Class.extend({init:function(){},_click:function(){}}),WCF.Label.Chooser=Class.extend({_container:null,_groups:{},_showWithoutSelection:!1,init:function(t,i,e,n){if(this._container=null,this._groups={},this._showWithoutSelection=!0===n,this._initContainers(i),$.getLength(t))for(var a in t){var o=this._groups[a];o&&WCF.Dropdown.getDropdownMenu(o.wcfIdentify()).find("> ul > li:not(.dropdownDivider)").each($.proxy(function(i,e){var n=$(e),o=n.data("labelID")||0;o&&t[a]==o&&this._selectLabel(n,!0)},this))}for(var l in this._containers){var s=this._containers[l];void 0===s.data("labelID")&&s.data("labelID",0)}this._container=$(i),e?$(e).click($.proxy(this._submit,this)):this._container.is("form")&&this._container.submit($.proxy(this._submit,this))},_initContainers:function(t){function i(t){t.addEventListener("wheel",function(t){t.preventDefault()},{passive:!1})}$(t).find(".labelChooser").each($.proxy(function(t,e){var n=$(e),a=n.data("groupID");if(!this._groups[a]){var o=n.wcfIdentify(),l=WCF.Dropdown.getDropdownMenu(o);null===l&&(WCF.Dropdown.initDropdown(n.find(".dropdownToggle")),l=WCF.Dropdown.getDropdownMenu(o));var s=l;if("div"==l.getTagName()&&l.children(".scrollableDropdownMenu").length&&(s=$("<ul />").appendTo(l),l=l.children(".scrollableDropdownMenu")),this._groups[a]=n,l.children("li").data("groupID",a).click($.proxy(this._click,this)),n.data("forceSelection")&&!this._showWithoutSelection||$('<li class="dropdownDivider" />').appendTo(s),this._showWithoutSelection){i($('<li data-label-id="-1"><span><span class="badge label">'+WCF.Language.get("wcf.label.withoutSelection")+"</span></span></li>").data("groupID",a).appendTo(s).click($.proxy(this._click,this))[0])}if(!n.data("forceSelection")){var r=$('<li data-label-id="0"><span><span class="badge label">'+WCF.Language.get("wcf.label.none")+"</span></span></li>").data("groupID",a).appendTo(s);r.click($.proxy(this._click,this)),i(r[0])}}},this))},_click:function(t){this._selectLabel($(t.currentTarget),!1)},_selectLabel:function(t,i){var e=this._groups[t.data("groupID")];i&&void 0!==e.data("labelID")||(t.data("labelID")?e.data("labelID",t.data("labelID")):e.data("labelID",0),t=t.find("span > span"),e.find(".dropdownToggle > span").removeClass().addClass(t.attr("class")).text(t.text()))},_submit:function(){var t=this._container.find(".formSubmit");t.find('input[type="hidden"]').each(function(t,i){var e=$(i);0===e.attr("name").indexOf("labelIDs[")&&e.remove()});for(var i in this._groups){var e=this._groups[i];e.data("labelID")&&$('<input type="hidden" name="labelIDs['+i+']" value="'+e.data("labelID")+'" />').appendTo(t)}},destroy:function(){for(var t in this._groups)WCF.Dropdown.destroy(this._groups[t].wcfIdentify())}}),WCF.Label.ArticleLabelChooser=WCF.Label.Chooser.extend({_labelGroupsToCategories:{},init:function(){},_updateLabelGroups:function(){},_submit:function(){}}); })(this);
+(function (window, undefined) { "use strict";WCF.Label={},WCF.Label.ACPList=Class.extend({_labelInput:{},_labelList:{},init:function(){},_keyPressed:function(){}}),WCF.Label.ACPList.Connect=Class.extend({init:function(){},_click:function(){}}),WCF.Label.Chooser=Class.extend({_container:null,_groups:{},_showWithoutSelection:!1,init:function(t,e,i,n){if(this._container=null,this._groups={},this._showWithoutSelection=!0===n,this._initContainers(e),$.getLength(t))for(var a in t){var o=this._groups[a];o&&WCF.Dropdown.getDropdownMenu(o.wcfIdentify()).find("> ul > li:not(.dropdownDivider)").each($.proxy(function(e,i){var n=$(i),o=n.data("labelID")||0;o&&t[a]==o&&this._selectLabel(n,!0)},this))}for(var s in this._containers){var l=this._containers[s];void 0===l.data("labelID")&&l.data("labelID",0)}this._container=$(e),i?$(i).click($.proxy(this._submit,this)):this._container.is("form")&&this._container.submit($.proxy(this._submit,this))},_initContainers:function(t){function e(t){t.addEventListener("wheel",function(t){t.preventDefault()},{passive:!1})}$(t).find(".labelChooser").each($.proxy(function(t,i){var n=$(i),a=n.data("groupID");if(!this._groups[a]){var o=n.wcfIdentify(),s=WCF.Dropdown.getDropdownMenu(o);null===s&&(WCF.Dropdown.initDropdown(n.find(".dropdownToggle")),s=WCF.Dropdown.getDropdownMenu(o));var l=s;if("div"==s.getTagName()&&s.children(".scrollableDropdownMenu").length&&(l=$("<ul />").appendTo(s),s=s.children(".scrollableDropdownMenu")),this._groups[a]=n,s.children("li").data("groupID",a).click($.proxy(this._click,this)),n.data("forceSelection")&&!this._showWithoutSelection||$('<li class="dropdownDivider" />').appendTo(l),this._showWithoutSelection){e($('<li data-label-id="-1"><span><span class="badge label">'+WCF.Language.get("wcf.label.withoutSelection")+"</span></span></li>").data("groupID",a).appendTo(l).click($.proxy(this._click,this))[0])}if(!n.data("forceSelection")){var r=$('<li data-label-id="0"><span><span class="badge label">'+WCF.Language.get("wcf.label.none")+"</span></span></li>").data("groupID",a).appendTo(l);r.click($.proxy(this._click,this)),e(r[0])}}},this))},_click:function(t){this._selectLabel($(t.currentTarget),!1)},_selectLabel:function(t,e){var i=this._groups[t.data("groupID")];e&&void 0!==i.data("labelID")||(t.data("labelID")?i.data("labelID",t.data("labelID")):i.data("labelID",0),t=t.find("span > span"),i.find(".dropdownToggle > span").removeClass().addClass(t.attr("class")).text(t.text()),!e&&this._container[0]&&"FORM"===this._container[0].nodeName&&null===elBySel('input:not([type="hidden"]):not([type="submit"]):not([type="reset"]), select, textarea',this._container[0])&&setTimeout(function(){this._container.trigger("submit")}.bind(this),100))},_submit:function(){var t=this._container.find(".formSubmit");t.find('input[type="hidden"]').each(function(t,e){var i=$(e);0===i.attr("name").indexOf("labelIDs[")&&i.remove()});for(var e in this._groups){var i=this._groups[e];i.data("labelID")&&$('<input type="hidden" name="labelIDs['+e+']" value="'+i.data("labelID")+'" />').appendTo(t)}},destroy:function(){for(var t in this._groups)WCF.Dropdown.destroy(this._groups[t].wcfIdentify())}}),WCF.Label.ArticleLabelChooser=WCF.Label.Chooser.extend({_labelGroupsToCategories:{},init:function(){},_updateLabelGroups:function(){},_submit:function(){}}); })(this);
// WCF.Location.js
-(function (window, undefined) { "use strict";function gm_authFailure(){WCF.System.Event.fireEvent("com.woltlab.wcf.googleMaps","authenticationFailure")}WCF.Location={},WCF.Location.Util={getLocation:function(t,e){var o=WCF.Location.GoogleMaps.Settings.get("accessUserLocation");navigator.geolocation&&null!==o&&o?navigator.geolocation.getCurrentPosition(function(e){t(e.coords.latitude,e.coords.longitude)},function(){t(void 0,void 0)},{timeout:e||5e3}):t(void 0,void 0)}},WCF.Location.GoogleMaps={},WCF.Location.GoogleMaps.Settings={_settings:{},get:function(t){return void 0===t?this._settings:void 0!==this._settings[t]?this._settings[t]:null},set:function(t,e){if($.isPlainObject(t))for(var o in t)this._settings[o]=t[o];else this._settings[t]=e}},WCF.Location.GoogleMaps.Map=Class.extend({_map:null,_markers:[],init:function(t,e){this._mapContainer=$("#"+t),this._mapOptions=$.extend(!0,this._getDefaultMapOptions(),e),this._map=new google.maps.Map(this._mapContainer[0],this._mapOptions),this._markers=[],this._mapContainer.parents(".sidebar").length&&enquire.register("(max-width: 767px)",{setup:$.proxy(this._addSidebarMapListener,this),deferSetup:!0}),this.refresh()},_addInfoWindowEventListener:function(t,e){google.maps.event.addListener(t,"click",$.proxy(function(){e.open(this._map,t)},this))},_addSidebarMapListener:function(){$(".content > .mobileSidebarToggleButton").click($.proxy(this.refresh,this))},_getDefaultMapOptions:function(){var t={};switch(t.center=new google.maps.LatLng(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),t.disableDoubleClickZoom=WCF.Location.GoogleMaps.Settings.get("disableDoubleClickZoom"),t.draggable=WCF.Location.GoogleMaps.Settings.get("draggable"),WCF.Location.GoogleMaps.Settings.get("mapType")){case"map":t.mapTypeId=google.maps.MapTypeId.ROADMAP;break;case"satellite":t.mapTypeId=google.maps.MapTypeId.SATELLITE;break;case"physical":t.mapTypeId=google.maps.MapTypeId.TERRAIN;break;case"hybrid":default:t.mapTypeId=google.maps.MapTypeId.HYBRID}if(t.mapTypeControl="off"!=WCF.Location.GoogleMaps.Settings.get("mapTypeControl"),t.mapTypeControl)switch(WCF.Location.GoogleMaps.Settings.get("mapTypeControl")){case"dropdown":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DROPDOWN_MENU};break;case"horizontalBar":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.HORIZONTAL_BAR};break;default:t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DEFAULT}}return t.scaleControl=WCF.Location.GoogleMaps.Settings.get("scaleControl"),t.scrollwheel=WCF.Location.GoogleMaps.Settings.get("scrollwheel"),t.zoom=WCF.Location.GoogleMaps.Settings.get("zoom"),t},addDraggableMarker:function(t,e){var o=new google.maps.Marker({clickable:!1,draggable:!0,map:this._map,position:new google.maps.LatLng(t,e),zIndex:1});return this._markers.push(o),o},addMarker:function(t,e,o,s,i){var a=new google.maps.Marker({map:this._map,position:new google.maps.LatLng(t,e),title:o});if(s&&a.setIcon(s),i){var n=new google.maps.InfoWindow({content:i});this._addInfoWindowEventListener(a,n),a.infoWindow=n}return this._markers.push(a),a},getMarkers:function(){return this._markers},getMap:function(){return this._map},refresh:function(){var t=this._map.getCenter();google.maps.event.trigger(this._map,"resize"),this._map.setCenter(t)},refreshBounds:function(){var t=null,e=null,o=null,s=null;for(var i in this._markers){var a=this._markers[i],n=a.getPosition().lat(),r=a.getPosition().lng();null===t?(t=e=n,o=s=r):(t>n?t=n:e<n&&(e=n),o>n?o=n:s<r&&(s=r))}this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(t,o),new google.maps.LatLng(e,s)))},removeMarkers:function(){for(var t in this._markers)this._markers[t].setMap(null);this._markers=[]},setBounds:function(t,e){this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(e.latitude,e.longitude),new google.maps.LatLng(t.latitude,t.longitude)))},setCenter:function(t,e){this._map.setCenter(new google.maps.LatLng(t,e))}}),WCF.Location.GoogleMaps.LargeMap=WCF.Location.GoogleMaps.Map.extend({_actionClassName:null,_additionalParameters:{},_locationSearch:null,_locationSearchInputSelector:null,_markerClusterer:null,_objectIDs:[],_previousNorthEast:null,_previousSouthWest:null,_stringifyExcludedObjectIds:!1,init:function(t,e,o,s,i){this._stringifyExcludedObjectIds=!1,e&&e.stringifyExcludedObjectIds&&(this._stringifyExcludedObjectIds=e.stringifyExcludedObjectIds,delete e.stringifyExcludedObjectIds),this._super(t,e),this._actionClassName=o,this._locationSearchInputSelector=s||"",this._additionalParameters=i||{},this._objectIDs=[],this._locationSearchInputSelector&&(this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(s,$.proxy(this._centerMap,this))),this._markerClusterer=new MarkerClusterer(this._map,this._markers,{maxZoom:17,imagePath:WCF.Location.GoogleMaps.Settings.get("markerClustererImagePath")+"m"}),this._markerSpiderfier=new OverlappingMarkerSpiderfier(this._map,{keepSpiderfied:!0,markersWontHide:!0,markersWontMove:!0}),this._markerSpiderfier.addListener("click",$.proxy(function(t){t.infoWindow&&t.infoWindow.open(this._map,t)},this)),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._previousNorthEast=null,this._previousSouthWest=null,google.maps.event.addListener(this._map,"idle",$.proxy(this._loadMarkers,this))},_addInfoWindowEventListener:function(t,e){},_centerMap:function(t){this.setCenter(t.location.lat(),t.location.lng()),$(this._locationSearchInputSelector).val(t.label)},_loadMarkers:function(){var t=this._map.getBounds().getNorthEast(),e=this._map.getBounds().getSouthWest();return!(this._previousNorthEast&&this._previousNorthEast.lat()>=t.lat()&&this._previousNorthEast.lng()>=t.lng()&&this._previousSouthWest.lat()<=e.lat()&&this._previousSouthWest.lng()<=e.lng())&&(this._previousNorthEast=t,this._previousSouthWest=e,this._proxy.setOption("data",{actionName:"getMapMarkers",className:this._actionClassName,parameters:$.extend(this._additionalParameters,{excludedObjectIDs:this._stringifyExcludedObjectIds?JSON.stringify(this._objectIDs):this._objectIDs,eastLongitude:t.lng(),northLatitude:t.lat(),southLatitude:e.lat(),westLongitude:e.lng()})}),this._proxy.sendRequest(),!0)},_success:function(t,e,o){if(t.returnValues&&t.returnValues.markers)for(var s in t.returnValues.markers){var i=t.returnValues.markers[s];this.addMarker(i.latitude,i.longitude,i.title,null,i.infoWindow),i.objectID?this._objectIDs.push(i.objectID):i.objectIDs&&(this._objectIDs=this._objectIDs.concat(i.objectIDs))}},addMarker:function(t,e,o,s,i){var a=this._super(t,e,o,s,i);return this._markerClusterer.addMarker(a),this._markerSpiderfier.addMarker(a),a}}),WCF.Location.GoogleMaps.SuggestionMap=WCF.Location.GoogleMaps.LargeMap.extend({_locationSuggestionsButton:null,_suggestionSelectionCallback:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i);var a=$('<div class="gmnoprint googleMapsCustomControlContainer"><div class="gm-style-mtc"><div class="googleMapsCustomControl">'+WCF.Language.get("wcf.map.showLocationSuggestions")+"</div></div></div>");this._locationSuggestionsButton=a.find(".googleMapsCustomControl").click($.proxy(this._toggleLocationSuggestions,this)),this._map.controls[google.maps.ControlPosition.TOP_RIGHT].push(a.get(0))},_loadMarkers:function(){this._locationSuggestionsButton.hasClass("active")&&(this._super()||(this._loadSuggestions=!1))},_success:function(t,e,o){var s=this._markers.length;this._super(t,e,o),this._loadSuggestions&&s==this._markers.length&&(this._loadSuggestions=!1,new WCF.System.Notification(WCF.Language.get("wcf.map.noLocationSuggestions"),"info").show())},_toggleLocationSuggestions:function(){var t=!this._locationSuggestionsButton.hasClass("active");t&&(this._loadSuggestions=!0),this.showSuggestions(t)},addMarker:function(t,e,o,s,i){var a=$(i),n=$('<a class="googleMapsUseLocationSuggestionLink" />').text(WCF.Language.get("wcf.map.useLocationSuggestion")).click(this._suggestionSelectionCallback);a.append($("<p />").append(n));var r=this._super(t,e,o,"//mt.google.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png",a.get(0));return n.data("marker",r),r},setSuggestionSelectionCallback:function(t){this._suggestionSelectionCallback=t},showSuggestions:function(t){void 0===t&&(t=!0),this._locationSuggestionsButton.toggleClass("active",t);for(var e=[],o=0,s=this._markers.length;o<s;o++){var i=this._markers[o];i.draggable||(i.setVisible(t),t&&e.push(i))}this._markerClusterer.clearMarkers(),t&&this._markerClusterer.addMarkers(e),this._loadMarkers()}}),WCF.Location.GoogleMaps.LocationSearch=WCF.Search.Base.extend({_geocoder:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i),this.setDelay(500),this._geocoder=new google.maps.Geocoder},_createListItem:function(t){var e=$("<li><span>"+WCF.String.escapeHTML(t.formatted_address)+"</span></li>").appendTo(this._list);return e.data("location",t.geometry.location).data("label",t.formatted_address).click($.proxy(this._executeCallback,this)),this._itemCount++,e},_keyUp:function(t){switch(t.which){case $.ui.keyCode.LEFT:case $.ui.keyCode.RIGHT:return;case $.ui.keyCode.UP:return void this._selectPreviousItem();case $.ui.keyCode.DOWN:return void this._selectNextItem();case $.ui.keyCode.ENTER:return this._selectElement(t)}var e=this._getSearchString(t);""===e?this._clearList(!0):e.length>=this._triggerLength?this._delay?(null!==this._timer&&this._timer.stop(),this._timer=new WCF.PeriodicalExecuter($.proxy(function(){this._geocoder.geocode({address:e},$.proxy(this._success,this)),this._timer.stop(),this._timer=null},this),this._delay)):this._geocoder.geocode({address:e},$.proxy(this._success,this)):this._clearList(!1)},_success:function(t,e){if(this._clearList(!1),e==google.maps.GeocoderStatus.OK){if($.getLength(t)){var o=0;for(var s in t)if(this._createListItem(t[s]),10==++o)break}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var i=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(i).hasClass("dropdownOpen")||WCF.Dropdown.toggleDropdown(i),this._itemIndex=-1,WCF.Dropdown.getDropdown(i).data("disableAutoFocus")||this._selectNextItem()}}}),WCF.Location.GoogleMaps.LocationInput=Class.extend({_locationSearch:null,_map:null,_marker:null,init:function(t,e,o,s,i,a){this._searchInput=o,a?(this._map=new WCF.Location.GoogleMaps.SuggestionMap(t,e,a),this._map.setSuggestionSelectionCallback($.proxy(this._useSuggestion,this))):this._map=new WCF.Location.GoogleMaps.Map(t,e),this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(o,$.proxy(this._setMarkerByLocation,this)),s&&i?this._marker=this._map.addDraggableMarker(s,i):(this._marker=this._map.addDraggableMarker(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),WCF.Location.Util.getLocation($.proxy(function(t,e){void 0!==t&&void 0!==e&&(WCF.Location.GoogleMaps.Util.moveMarker(this._marker,t,e),WCF.Location.GoogleMaps.Util.focusMarker(this._marker))},this))),this._marker.addListener("dragend",$.proxy(this._updateLocation,this))},_useSuggestion:function(t){var e=$(t.currentTarget).data("marker");this._marker.setPosition(e.getPosition()),this._updateLocation(),this._map.showSuggestions(!1)},_updateLocation:function(){WCF.Location.GoogleMaps.Util.reverseGeocoding($.proxy(function(t){null!==t&&$(this._searchInput).val(t)},this),this._marker)},_setMarkerByLocation:function(t){this._marker.setPosition(t.location),WCF.Location.GoogleMaps.Util.focusMarker(this._marker),$(this._searchInput).val(t.label)},getMap:function(){return this._map},getMarker:function(){return this._marker}}),WCF.Location.GoogleMaps.Util={_geocoder:null,focusMarker:function(t){t.getMap().setCenter(t.getPosition())},getMarkerPosition:function(t){return{latitude:t.getPosition().lat(),longitude:t.getPosition().lng()}},moveMarker:function(t,e,o,s){t.setPosition(new google.maps.LatLng(e,o)),s&&google.maps.event.trigger(t,"dragend")},reverseGeocoding:function(t,e,o,s,i){e&&(o=e.getPosition().lat(),s=e.getPosition().lng()),null===this._geocoder&&(this._geocoder=new google.maps.Geocoder);var a=new google.maps.LatLng(o,s);this._geocoder.geocode({latLng:a},function(e,o){t(o==google.maps.GeocoderStatus.OK?i?e:e[0].formatted_address:null)})}}; })(this);
+(function (window, undefined) { "use strict";function gm_authFailure(){WCF.System.Event.fireEvent("com.woltlab.wcf.googleMaps","authenticationFailure")}WCF.Location={},WCF.Location.Util={getLocation:function(t,e){var o=WCF.Location.GoogleMaps.Settings.get("accessUserLocation");navigator.geolocation&&null!==o&&o?navigator.geolocation.getCurrentPosition(function(e){t(e.coords.latitude,e.coords.longitude)},function(){t(void 0,void 0)},{timeout:e||5e3}):t(void 0,void 0)}},WCF.Location.GoogleMaps={},WCF.Location.GoogleMaps.Settings={_settings:{},get:function(t){return void 0===t?this._settings:void 0!==this._settings[t]?this._settings[t]:null},set:function(t,e){if($.isPlainObject(t))for(var o in t)this._settings[o]=t[o];else this._settings[t]=e}},WCF.Location.GoogleMaps.Map=Class.extend({_map:null,_markers:[],init:function(t,e){this._mapContainer=$("#"+t),this._mapOptions=$.extend(!0,this._getDefaultMapOptions(),e),this._map=new google.maps.Map(this._mapContainer[0],this._mapOptions),this._markers=[],this._mapContainer.parents(".sidebar").length&&require(["Ui/Screen"],function(t){t.on("screen-sm-down",{setup:$.proxy(this._addSidebarMapListener,this)})}.bind(this)),this.refresh()},_addInfoWindowEventListener:function(t,e){google.maps.event.addListener(t,"click",$.proxy(function(){e.open(this._map,t)},this))},_addSidebarMapListener:function(){$(".content > .mobileSidebarToggleButton").click($.proxy(this.refresh,this))},_getDefaultMapOptions:function(){var t={};switch(t.center=new google.maps.LatLng(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),t.disableDoubleClickZoom=WCF.Location.GoogleMaps.Settings.get("disableDoubleClickZoom"),t.draggable=WCF.Location.GoogleMaps.Settings.get("draggable"),WCF.Location.GoogleMaps.Settings.get("mapType")){case"map":t.mapTypeId=google.maps.MapTypeId.ROADMAP;break;case"satellite":t.mapTypeId=google.maps.MapTypeId.SATELLITE;break;case"physical":t.mapTypeId=google.maps.MapTypeId.TERRAIN;break;case"hybrid":default:t.mapTypeId=google.maps.MapTypeId.HYBRID}if(t.mapTypeControl="off"!=WCF.Location.GoogleMaps.Settings.get("mapTypeControl"),t.mapTypeControl)switch(WCF.Location.GoogleMaps.Settings.get("mapTypeControl")){case"dropdown":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DROPDOWN_MENU};break;case"horizontalBar":t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.HORIZONTAL_BAR};break;default:t.mapTypeControlOptions={style:google.maps.MapTypeControlStyle.DEFAULT}}return t.scaleControl=WCF.Location.GoogleMaps.Settings.get("scaleControl"),t.scrollwheel=WCF.Location.GoogleMaps.Settings.get("scrollwheel"),t.zoom=WCF.Location.GoogleMaps.Settings.get("zoom"),t},addDraggableMarker:function(t,e){var o=new google.maps.Marker({clickable:!1,draggable:!0,map:this._map,position:new google.maps.LatLng(t,e),zIndex:1});return this._markers.push(o),o},addMarker:function(t,e,o,s,i){var a=new google.maps.Marker({map:this._map,position:new google.maps.LatLng(t,e),title:o});if(s&&a.setIcon(s),i){var n=new google.maps.InfoWindow({content:i});this._addInfoWindowEventListener(a,n),a.infoWindow=n}return this._markers.push(a),a},getMarkers:function(){return this._markers},getMap:function(){return this._map},refresh:function(){var t=this._map.getCenter();google.maps.event.trigger(this._map,"resize"),this._map.setCenter(t)},refreshBounds:function(){var t=null,e=null,o=null,s=null;for(var i in this._markers){var a=this._markers[i],n=a.getPosition().lat(),r=a.getPosition().lng();null===t?(t=e=n,o=s=r):(t>n?t=n:e<n&&(e=n),o>n?o=n:s<r&&(s=r))}this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(t,o),new google.maps.LatLng(e,s)))},removeMarkers:function(){for(var t in this._markers)this._markers[t].setMap(null);this._markers=[]},setBounds:function(t,e){this._map.fitBounds(new google.maps.LatLngBounds(new google.maps.LatLng(e.latitude,e.longitude),new google.maps.LatLng(t.latitude,t.longitude)))},setCenter:function(t,e){this._map.setCenter(new google.maps.LatLng(t,e))}}),WCF.Location.GoogleMaps.LargeMap=WCF.Location.GoogleMaps.Map.extend({_actionClassName:null,_additionalParameters:{},_locationSearch:null,_locationSearchInputSelector:null,_markerClusterer:null,_objectIDs:[],_previousNorthEast:null,_previousSouthWest:null,_stringifyExcludedObjectIds:!1,init:function(t,e,o,s,i){this._stringifyExcludedObjectIds=!1,e&&e.stringifyExcludedObjectIds&&(this._stringifyExcludedObjectIds=e.stringifyExcludedObjectIds,delete e.stringifyExcludedObjectIds),this._super(t,e),this._actionClassName=o,this._locationSearchInputSelector=s||"",this._additionalParameters=i||{},this._objectIDs=[],this._locationSearchInputSelector&&(this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(s,$.proxy(this._centerMap,this))),this._markerClusterer=new MarkerClusterer(this._map,this._markers,{maxZoom:17,imagePath:WCF.Location.GoogleMaps.Settings.get("markerClustererImagePath")+"m"}),this._markerSpiderfier=new OverlappingMarkerSpiderfier(this._map,{keepSpiderfied:!0,markersWontHide:!0,markersWontMove:!0}),this._markerSpiderfier.addListener("click",$.proxy(function(t){t.infoWindow&&t.infoWindow.open(this._map,t)},this)),this._proxy=new WCF.Action.Proxy({showLoadingOverlay:!1,success:$.proxy(this._success,this)}),this._previousNorthEast=null,this._previousSouthWest=null,google.maps.event.addListener(this._map,"idle",$.proxy(this._loadMarkers,this))},_addInfoWindowEventListener:function(t,e){},_centerMap:function(t){this.setCenter(t.location.lat(),t.location.lng()),$(this._locationSearchInputSelector).val(t.label)},_loadMarkers:function(){var t=this._map.getBounds().getNorthEast(),e=this._map.getBounds().getSouthWest();return!(this._previousNorthEast&&this._previousNorthEast.lat()>=t.lat()&&this._previousNorthEast.lng()>=t.lng()&&this._previousSouthWest.lat()<=e.lat()&&this._previousSouthWest.lng()<=e.lng())&&(this._previousNorthEast=t,this._previousSouthWest=e,this._proxy.setOption("data",{actionName:"getMapMarkers",className:this._actionClassName,parameters:$.extend(this._additionalParameters,{excludedObjectIDs:this._stringifyExcludedObjectIds?JSON.stringify(this._objectIDs):this._objectIDs,eastLongitude:t.lng(),northLatitude:t.lat(),southLatitude:e.lat(),westLongitude:e.lng()})}),this._proxy.sendRequest(),!0)},_success:function(t,e,o){if(t.returnValues&&t.returnValues.markers)for(var s in t.returnValues.markers){var i=t.returnValues.markers[s];this.addMarker(i.latitude,i.longitude,i.title,null,i.infoWindow),i.objectID?this._objectIDs.push(i.objectID):i.objectIDs&&(this._objectIDs=this._objectIDs.concat(i.objectIDs))}},addMarker:function(t,e,o,s,i){var a=this._super(t,e,o,s,i);return this._markerClusterer.addMarker(a),this._markerSpiderfier.addMarker(a),a}}),WCF.Location.GoogleMaps.SuggestionMap=WCF.Location.GoogleMaps.LargeMap.extend({_locationSuggestionsButton:null,_suggestionSelectionCallback:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i);var a=$('<div class="gmnoprint googleMapsCustomControlContainer"><div class="gm-style-mtc"><div class="googleMapsCustomControl">'+WCF.Language.get("wcf.map.showLocationSuggestions")+"</div></div></div>");this._locationSuggestionsButton=a.find(".googleMapsCustomControl").click($.proxy(this._toggleLocationSuggestions,this)),this._map.controls[google.maps.ControlPosition.TOP_RIGHT].push(a.get(0))},_loadMarkers:function(){this._locationSuggestionsButton.hasClass("active")&&(this._super()||(this._loadSuggestions=!1))},_success:function(t,e,o){var s=this._markers.length;this._super(t,e,o),this._loadSuggestions&&s==this._markers.length&&(this._loadSuggestions=!1,new WCF.System.Notification(WCF.Language.get("wcf.map.noLocationSuggestions"),"info").show())},_toggleLocationSuggestions:function(){var t=!this._locationSuggestionsButton.hasClass("active");t&&(this._loadSuggestions=!0),this.showSuggestions(t)},addMarker:function(t,e,o,s,i){var a=$(i),n=$('<a class="googleMapsUseLocationSuggestionLink" />').text(WCF.Language.get("wcf.map.useLocationSuggestion")).click(this._suggestionSelectionCallback);a.append($("<p />").append(n));var r=this._super(t,e,o,"//mt.google.com/vt/icon/name=icons/spotlight/spotlight-waypoint-a.png",a.get(0));return n.data("marker",r),r},setSuggestionSelectionCallback:function(t){this._suggestionSelectionCallback=t},showSuggestions:function(t){void 0===t&&(t=!0),this._locationSuggestionsButton.toggleClass("active",t);for(var e=[],o=0,s=this._markers.length;o<s;o++){var i=this._markers[o];i.draggable||(i.setVisible(t),t&&e.push(i))}this._markerClusterer.clearMarkers(),t&&this._markerClusterer.addMarkers(e),this._loadMarkers()}}),WCF.Location.GoogleMaps.LocationSearch=WCF.Search.Base.extend({_geocoder:null,init:function(t,e,o,s,i){this._super(t,e,o,s,i),this.setDelay(500),this._geocoder=new google.maps.Geocoder},_createListItem:function(t){var e=$("<li><span>"+WCF.String.escapeHTML(t.formatted_address)+"</span></li>").appendTo(this._list);return e.data("location",t.geometry.location).data("label",t.formatted_address).click($.proxy(this._executeCallback,this)),this._itemCount++,e},_keyUp:function(t){switch(t.which){case $.ui.keyCode.LEFT:case $.ui.keyCode.RIGHT:return;case $.ui.keyCode.UP:return void this._selectPreviousItem();case $.ui.keyCode.DOWN:return void this._selectNextItem();case $.ui.keyCode.ENTER:return this._selectElement(t)}var e=this._getSearchString(t);""===e?this._clearList(!0):e.length>=this._triggerLength?this._delay?(null!==this._timer&&this._timer.stop(),this._timer=new WCF.PeriodicalExecuter($.proxy(function(){this._geocoder.geocode({address:e},$.proxy(this._success,this)),this._timer.stop(),this._timer=null},this),this._delay)):this._geocoder.geocode({address:e},$.proxy(this._success,this)):this._clearList(!1)},_success:function(t,e){if(this._clearList(!1),e==google.maps.GeocoderStatus.OK){if($.getLength(t)){var o=0;for(var s in t)if(this._createListItem(t[s]),10==++o)break}else if(!this._handleEmptyResult())return;WCF.CloseOverlayHandler.addCallback("WCF.Search.Base",$.proxy(function(){this._clearList()},this));var i=this._searchInput.parents(".dropdown").wcfIdentify();WCF.Dropdown.getDropdownMenu(i).hasClass("dropdownOpen")||WCF.Dropdown.toggleDropdown(i),this._itemIndex=-1,WCF.Dropdown.getDropdown(i).data("disableAutoFocus")||this._selectNextItem()}}}),WCF.Location.GoogleMaps.LocationInput=Class.extend({_locationSearch:null,_map:null,_marker:null,init:function(t,e,o,s,i,a){this._searchInput=o,a?(this._map=new WCF.Location.GoogleMaps.SuggestionMap(t,e,a),this._map.setSuggestionSelectionCallback($.proxy(this._useSuggestion,this))):this._map=new WCF.Location.GoogleMaps.Map(t,e),this._locationSearch=new WCF.Location.GoogleMaps.LocationSearch(o,$.proxy(this._setMarkerByLocation,this)),s&&i?this._marker=this._map.addDraggableMarker(s,i):(this._marker=this._map.addDraggableMarker(WCF.Location.GoogleMaps.Settings.get("defaultLatitude"),WCF.Location.GoogleMaps.Settings.get("defaultLongitude")),WCF.Location.Util.getLocation($.proxy(function(t,e){void 0!==t&&void 0!==e&&(WCF.Location.GoogleMaps.Util.moveMarker(this._marker,t,e),WCF.Location.GoogleMaps.Util.focusMarker(this._marker))},this))),this._marker.addListener("dragend",$.proxy(this._updateLocation,this))},_useSuggestion:function(t){var e=$(t.currentTarget).data("marker");this._marker.setPosition(e.getPosition()),this._updateLocation(),this._map.showSuggestions(!1)},_updateLocation:function(){WCF.Location.GoogleMaps.Util.reverseGeocoding($.proxy(function(t){null!==t&&$(this._searchInput).val(t)},this),this._marker)},_setMarkerByLocation:function(t){this._marker.setPosition(t.location),WCF.Location.GoogleMaps.Util.focusMarker(this._marker),$(this._searchInput).val(t.label)},getMap:function(){return this._map},getMarker:function(){return this._marker}}),WCF.Location.GoogleMaps.Util={_geocoder:null,focusMarker:function(t){t.getMap().setCenter(t.getPosition())},getMarkerPosition:function(t){return{latitude:t.getPosition().lat(),longitude:t.getPosition().lng()}},moveMarker:function(t,e,o,s){t.setPosition(new google.maps.LatLng(e,o)),s&&google.maps.event.trigger(t,"dragend")},reverseGeocoding:function(t,e,o,s,i){e&&(o=e.getPosition().lat(),s=e.getPosition().lng()),null===this._geocoder&&(this._geocoder=new google.maps.Geocoder);var a=new google.maps.LatLng(o,s);this._geocoder.geocode({latLng:a},function(e,o){t(o==google.maps.GeocoderStatus.OK?i?e:e[0].formatted_address:null)})}}; })(this);
// WCF.Message.js
-(function (window, undefined) { "use strict";WCF.Message={},WCF.Message.BBCode={},WCF.Message.BBCode.CodeViewer=Class.extend({_dialog:null,init:function(){this._dialog=null,this._initCodeBoxes(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.BBCode.CodeViewer",$.proxy(this._initCodeBoxes,this)),WCF.DOMNodeInsertedHandler.execute()},_initCodeBoxes:function(){$(".codeBox:not(.jsCodeViewer)").each($.proxy(function(e,t){var n=$(t).addClass("jsCodeViewer");$('<span class="codeBoxPlainSource icon icon24 fa-files-o pointer jsTooltip" title="'+WCF.Language.get("wcf.message.bbcode.code.copy")+'" />').appendTo(n.find(".codeBoxHeader")).click($.proxy(this._click,this))},this))},_click:function(e){var t="";$(e.currentTarget).parents("div").next("ol").children("li").each(function(e,n){t&&(t+="\n"),t+=$(n).text().replace(/\n+$/,"").replace(/\u200B/g,"").replace(/\xa0/," ")}),null===this._dialog?(this._dialog=$('<div><textarea cols="60" rows="12" readonly></textarea></div>').hide().appendTo(document.body),this._dialog.children("textarea").val(t),this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.bbcode.code.copy")})):(this._dialog.children("textarea").val(t),this._dialog.wcfDialog("open"));var n=this._dialog.children("textarea")[0];"rtl"===document.documentElement.dir&&(n.dir="ltr",n.style.setProperty("text-align","left",""));var i=function(){n.selectionStart=0,n.selectionEnd=n.value.length};n.addEventListener("mouseup",i),window.setTimeout(i,10)}}),WCF.Message.EditHistory=Class.extend({_oldIDInputs:{},_newIDInputs:{},_containerSelector:"",_buttonSelector:"",init:function(){},_initInputs:function(){},_initElements:function(){},_click:function(){},_sendRequest:function(){},_success:function(){}}),WCF.Message.FormGuard=Class.extend({init:function(){var e=$("form.jsFormGuard").removeClass("jsFormGuard").submit(function(){$(this).find(".formSubmit input[type=submit]").disable()});$(window).on("unload",function(){e.find(".formSubmit input[type=submit]").enable()})}}),WCF.Message.Preview=Class.extend({_className:"",_messageFieldID:"",_messageField:{},_proxy:{},_previewButton:{},_previewButtonLabel:"",init:function(){},_click:function(){},_getParameters:function(){},_getMessage:function(){},_success:function(){},_handleResponse:function(){},_failure:function(){}}),WCF.Message.DefaultPreview=WCF.Message.Preview.extend({_dialog:{},_options:{},init:function(){},_handleResponse:function(){},_getParameters:function(){},_dialogSetup:function(){},_className:"",_messageFieldID:"",_messageField:{},_proxy:{},_previewButton:{},_previewButtonLabel:"",_click:function(){},_getMessage:function(){},_success:function(){},_failure:function(){}}),WCF.Message.Multilingualism=Class.extend({_availableLanguages:{},_languageID:0,_languageInput:{},init:function(){},_click:function(){},_disable:function(){},_updateLabel:function(){},_submit:function(){}}),WCF.Message.SmileyCategories=Class.extend({_cache:{},_proxy:{},_wysiwygSelector:"",init:function(){},_click:function(){},_success:function(){}}),WCF.Message.Smilies=Class.extend({_editorId:"",init:function(){},_smileyClick:function(){}}),WCF.Message.InlineEditor=Class.extend({_container:{},_containerID:0,_dropdowns:{},_messageContainerSelector:"",_messageEditorIDPrefix:"",init:function(){},_click:function(){},_initDropdownMenu:function(){},_callbackDropdownInit:function(){},_getClassName:function(){}}),WCF.Message.Submit={_buttons:{},registerButton:function(){},execute:function(){}},WCF.Message.Quote={},WCF.Message.Quote.Handler=Class.extend({_activeContainerID:"",_className:"",_containers:{},_containerSelector:"",_copyQuote:{},_message:"",_messageBodySelector:"",_objectID:0,_objectType:"",_proxy:{},_quoteManager:{},init:function(){},_initContainers:function(){},_mouseDown:function(){},_getNodeText:function(){},_mouseUp:function(){},_normalize:function(){},_getBoundingRectangle:function(){},_initCopyQuote:function(){},_getSelectedText:function(){},_saveFullQuote:function(){},_saveQuote:function(){},_saveAndInsertQuote:function(){},_success:function(){},updateFullQuoteObjectIDs:function(){}}),WCF.Message.Quote.Manager=Class.extend({_buttons:{},_count:0,_dialog:{},_editorId:"",_editorIdAlternative:"",_form:{},_handlers:{},_hasTemplate:!1,_insertQuotes:!0,_proxy:{},_removeOnSubmit:{},_supportPaste:!1,init:function(){},setAlternativeEditor:function(){},clearAlternativeEditor:function(){},register:function(){},updateCount:function(){},insertQuotes:function(){},_toggleShowQuotes:function(){},_click:function(){},renderDialog:function(){},_changeButtons:function(){},_change:function(){},_insertSelected:function(){},_insertQuote:function(){},_removeSelected:function(){},_submit:function(){},getQuotesMarkedForRemoval:function(){},markQuotesForRemoval:function(){},removeMarkedQuotes:function(){},countQuotes:function(){},_success:function(){},supportPaste:function(){}}),WCF.Message.Share={},WCF.Message.Share.Content=Class.extend({_cache:{},_dialog:null,init:function(){this._cache={},this._dialog=null,this._initLinks(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.Share.Content",$.proxy(this._initLinks,this))},_initLinks:function(){$("a.jsButtonShare").removeClass("jsButtonShare").click($.proxy(this._click,this))},_click:function(e){e.preventDefault();var t=$(e.currentTarget),n=t.prop("href"),i=t.data("linkTitle")?t.data("linkTitle"):n,a=n.hashCode();if(void 0===this._cache[a]){var o=!1;null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),o=!0):this._dialog.empty();var s=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalink">'+WCF.Language.get("wcf.message.share.permalink")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalink" class="long" readonly />').attr("value",n).appendTo(s);var s=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkBBCode">'+WCF.Language.get("wcf.message.share.permalink.bbcode")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkBBCode" class="long" readonly />').attr("value","[url='"+n+"']"+i+"[/url]").appendTo(s);var s=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkHTML">'+WCF.Language.get("wcf.message.share.permalink.html")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkHTML" class="long" readonly />').attr("value",'<a href="'+n+'">'+WCF.String.escapeHTML(i)+"</a>").appendTo(s),this._cache[a]=this._dialog.html(),o?this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.share")}):this._dialog.wcfDialog("open")}else this._dialog.html(this._cache[a]).wcfDialog("open");this._enableSelection()},_enableSelection:function(){var e=this._dialog.find("input").click(function(){$(this).select()});navigator.userAgent.match(/iP(ad|hone|od)/)&&e.keydown(function(){return!1}).removeAttr("readonly").click(function(){this.setSelectionRange(0,9999)})}}),WCF.Message.Share.Page=Class.extend({init:function(){require(["WoltLabSuite/Core/Ui/Message/Share"],function(e){e.init()})}}),WCF.Message.UserMention=Class.extend({init:function(){throw new Error("Support for mentions in Redactor are now enabled by adding the attribute 'data-support-mention=\"true\"' to the textarea element.")}}),$.widget("wcf.messageTabMenu",{_tabs:[],_tabsByName:{},options:{collapsible:!0},_create:function(){var e=this.element.find("> nav"),t=e.find("> ul > li:not(.jsFlexibleMenuDropdown)"),n=this.element.find("> div, > fieldset");if(t.length!=n.length)return void console.debug("[wcf.messageTabMenu] Amount of tabs does not equal amount of tab containers, aborting.");var i=this.element.data("preselect");n.each(function(e,n){if(null!==elBySel(".innerError",n))return i=$(t[e]).data("name"),!1}),"true"===i&&(i=!0),this._tabs=[],this._tabsByName={};for(var a=0;a<t.length;a++){var o=$(t[a]),s=$(n[a]),l=o.data("name");if(void 0===l){var c=o.children("a").prop("href");void 0!==c&&c.match(/#([a-zA-Z_-]+)$/)&&(l=RegExp.$1),void 0===l&&(l=o.wcfIdentify(),console.debug("[wcf.messageTabMenu] Missing name attribute, assuming generic ID '"+l+"'"))}this._tabs.push({container:s,name:l,tab:o}),this._tabsByName[l]=a;var r=o.children("a").data("index",a).on("mousedown",this._showTab.bind(this));(i===l||!0===i&&0===a)&&r.trigger("mousedown")}!0===i&&this._tabs.length&&!window.matchMedia("(max-width: 544px)").matches&&this._tabs[0].tab.children("a").trigger("click");var u=this.element.data("collapsible");void 0!==u&&(this.options.collapsible=u);var d=elData(this.element[0],"wysiwyg-container-id");d&&WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+d,function(){for(var e=0,t=this._tabs.length;e<t;e++)this._tabs[e].container.removeClass("active"),this._tabs[e].tab.removeClass("active")}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this.element.remove()},_showTab:function(e,t,n){var i=null===e?t:$(e.currentTarget).data("index");n=!this.options.collapsible||!0===n;for(var a=null,o=0;o<this._tabs.length;o++){var s=this._tabs[o];if(o==i){if(!s.tab.hasClass("active")){s.tab.addClass("active"),s.container.addClass("active"),a=s;var l=s.container[0];if(null===elBySel(".messageTabMenuContent.active",l)&&null!==elBySel(".messageTabMenuContent",l)){var c=elBySel("nav > ul > li[data-name] > a",l);null!==c&&$(c).trigger("mousedown")}continue}if(!0===n)continue}s.tab.removeClass("active"),s.container.removeClass("active")}null!==e&&(e.preventDefault(),e.stopPropagation()),null!==a&&this._trigger("show",{},{activeTab:a}),$(window).trigger("resize")},showTab:function(e,t){if($.isNumeric(e)||void 0!==this._tabsByName[e]&&(e=this._tabsByName[e]),void 0===this._tabs[e])return void console.debug("[wcf.messageTabMenu] Cannot locate tab identified by '"+e+"'");this._showTab(null,e,t)},getTab:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].tab:null},getContainer:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].container:null}}); })(this);
+(function (window, undefined) { "use strict";WCF.Message={},WCF.Message.BBCode={},WCF.Message.BBCode.CodeViewer=Class.extend({init:function(){}}),WCF.Message.EditHistory=Class.extend({_oldIDInputs:{},_newIDInputs:{},_containerSelector:"",_buttonSelector:"",init:function(){},_initInputs:function(){},_initElements:function(){},_click:function(){},_sendRequest:function(){},_success:function(){}}),WCF.Message.FormGuard=Class.extend({init:function(){var e=$("form.jsFormGuard").removeClass("jsFormGuard").submit(function(){$(this).find(".formSubmit input[type=submit]").disable()});$(window).on("unload",function(){e.find(".formSubmit input[type=submit]").enable()})}}),WCF.Message.Preview=Class.extend({_className:"",_messageFieldID:"",_messageField:{},_proxy:{},_previewButton:{},_previewButtonLabel:"",init:function(){},_click:function(){},_getParameters:function(){},_getMessage:function(){},_success:function(){},_handleResponse:function(){},_failure:function(){}}),WCF.Message.DefaultPreview=WCF.Message.Preview.extend({_dialog:{},_options:{},init:function(){},_handleResponse:function(){},_getParameters:function(){},_dialogSetup:function(){},_className:"",_messageFieldID:"",_messageField:{},_proxy:{},_previewButton:{},_previewButtonLabel:"",_click:function(){},_getMessage:function(){},_success:function(){},_failure:function(){}}),WCF.Message.Multilingualism=Class.extend({_availableLanguages:{},_languageID:0,_languageInput:{},init:function(){},_click:function(){},_disable:function(){},_updateLabel:function(){},_submit:function(){}}),WCF.Message.SmileyCategories=Class.extend({_cache:{},_proxy:{},_wysiwygSelector:"",init:function(){},_click:function(){},_success:function(){}}),WCF.Message.Smilies=Class.extend({_editorId:"",init:function(){},_smileyClick:function(){}}),WCF.Message.InlineEditor=Class.extend({_container:{},_containerID:0,_dropdowns:{},_messageContainerSelector:"",_messageEditorIDPrefix:"",init:function(){},_click:function(){},_initDropdownMenu:function(){},_callbackDropdownInit:function(){},_getClassName:function(){}}),WCF.Message.Submit={_buttons:{},registerButton:function(){},execute:function(){}},WCF.Message.Quote={},WCF.Message.Quote.Handler=Class.extend({_activeContainerID:"",_className:"",_containers:{},_containerSelector:"",_copyQuote:{},_message:"",_messageBodySelector:"",_objectID:0,_objectType:"",_proxy:{},_quoteManager:{},init:function(){},_initContainers:function(){},_mouseDown:function(){},_getNodeText:function(){},_mouseUp:function(){},_normalize:function(){},_getBoundingRectangle:function(){},_initCopyQuote:function(){},_getSelectedText:function(){},_saveFullQuote:function(){},_saveQuote:function(){},_saveAndInsertQuote:function(){},_success:function(){},updateFullQuoteObjectIDs:function(){}}),WCF.Message.Quote.Manager=Class.extend({_buttons:{},_count:0,_dialog:{},_editorId:"",_editorIdAlternative:"",_form:{},_handlers:{},_hasTemplate:!1,_insertQuotes:!0,_proxy:{},_removeOnSubmit:{},_supportPaste:!1,init:function(){},setAlternativeEditor:function(){},clearAlternativeEditor:function(){},register:function(){},updateCount:function(){},insertQuotes:function(){},_toggleShowQuotes:function(){},_click:function(){},renderDialog:function(){},_changeButtons:function(){},_change:function(){},_insertSelected:function(){},_insertQuote:function(){},_removeSelected:function(){},_submit:function(){},getQuotesMarkedForRemoval:function(){},markQuotesForRemoval:function(){},removeMarkedQuotes:function(){},countQuotes:function(){},_success:function(){},supportPaste:function(){}}),WCF.Message.Share={},WCF.Message.Share.Content=Class.extend({_cache:{},_dialog:null,_shareButtonsTemplate:"",init:function(e){this._shareButtonsTemplate=e||"",this._cache={},this._dialog=null,this._initLinks(),WCF.DOMNodeInsertedHandler.addCallback("WCF.Message.Share.Content",$.proxy(this._initLinks,this))},_initLinks:function(){$("a.jsButtonShare").removeClass("jsButtonShare").click($.proxy(this._click,this))},_click:function(e){e.preventDefault();var t=$(e.currentTarget),n=t.prop("href"),i=t.data("linkTitle")?t.data("linkTitle"):n,a=n.hashCode();if(void 0===this._cache[a]){var s=!1;null===this._dialog?(this._dialog=$("<div />").hide().appendTo(document.body),s=!0):this._dialog.empty();var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalink">'+WCF.Language.get("wcf.message.share.permalink")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalink" class="long" readonly />').attr("value",n).appendTo(o);var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkBBCode">'+WCF.Language.get("wcf.message.share.permalink.bbcode")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkBBCode" class="long" readonly />').attr("value","[url='"+n+"']"+i+"[/url]").appendTo(o);var o=$('<section class="section"><h2 class="sectionTitle"><label for="__sharePermalinkHTML">'+WCF.Language.get("wcf.message.share.permalink.html")+"</label></h2></section>").appendTo(this._dialog);$('<input type="text" id="__sharePermalinkHTML" class="long" readonly />').attr("value",'<a href="'+n+'">'+WCF.String.escapeHTML(i)+"</a>").appendTo(o),""!==this._shareButtonsTemplate&&(o=$('<section class="section"><h2 class="sectionTitle">'+WCF.Language.get("wcf.message.share")+"</h2>"+this._shareButtonsTemplate+"</section>").appendTo(this._dialog),elData(o.children(".jsMessageShareButtons")[0],"url",WCF.String.escapeHTML(n))),this._cache[a]=this._dialog.html(),s?this._dialog.wcfDialog({title:WCF.Language.get("wcf.message.share")}):this._dialog.wcfDialog("open")}else this._dialog.html(this._cache[a]).wcfDialog("open");this._enableSelection()},_enableSelection:function(){var e=this._dialog.find("input").click(function(){$(this).select()});navigator.userAgent.match(/iP(ad|hone|od)/)&&e.keydown(function(){return!1}).removeAttr("readonly").click(function(){this.setSelectionRange(0,9999)})}}),WCF.Message.Share.Page=Class.extend({init:function(){require(["WoltLabSuite/Core/Ui/Message/Share"],function(e){e.init()})}}),WCF.Message.UserMention=Class.extend({init:function(){throw new Error("Support for mentions in Redactor are now enabled by adding the attribute 'data-support-mention=\"true\"' to the textarea element.")}}),$.widget("wcf.messageTabMenu",{_tabs:[],_tabsByName:{},options:{collapsible:!0},_create:function(){var e=this.element.find("> nav"),t=e.find("> ul > li:not(.jsFlexibleMenuDropdown)"),n=this.element.find("> div, > fieldset");if(t.length!=n.length)return void console.debug("[wcf.messageTabMenu] Amount of tabs does not equal amount of tab containers, aborting.");var i=this.element.data("preselect");n.each(function(e,n){if(null!==elBySel(".innerError",n))return i=$(t[e]).data("name"),!1}),"true"===i&&(i=!0),this._tabs=[],this._tabsByName={};for(var a=0;a<t.length;a++){var s=$(t[a]),o=$(n[a]),l=s.data("name");if(void 0===l){var c=s.children("a").prop("href");void 0!==c&&c.match(/#([a-zA-Z_-]+)$/)&&(l=RegExp.$1),void 0===l&&(l=s.wcfIdentify())}this._tabs.push({container:o,name:l,tab:s}),this._tabsByName[l]=a;var r=s.children("a").data("index",a).on("mousedown",this._showTab.bind(this));r.attr("role","button").attr("tabindex","0").attr("aria-haspopup",!0).attr("aria-expanded",!1).attr("aria-controls",o[0].id),r.on("keydown",function(e){13!==e.which&&32!==e.which||(e.preventDefault(),this._showTab(e))}.bind(this)),(i===l||!0===i&&0===a)&&r.trigger("mousedown")}!0===i&&this._tabs.length&&!window.matchMedia("(max-width: 544px)").matches&&this._tabs[0].tab.children("a").trigger("click");var u=this.element.data("collapsible");void 0!==u&&(this.options.collapsible=u);var d=elData(this.element[0],"wysiwyg-container-id");d&&WCF.System.Event.addListener("com.woltlab.wcf.redactor2","reset_"+d,function(){for(var e=0,t=this._tabs.length;e<t;e++)this._tabs[e].container.removeClass("active"),this._tabs[e].tab.removeClass("active")}.bind(this))},destroy:function(){$.Widget.prototype.destroy.apply(this,arguments),this.element.remove()},_showTab:function(e,t,n){var i=null===e?t:$(e.currentTarget).data("index");n=!this.options.collapsible||!0===n;for(var a=null,s=0;s<this._tabs.length;s++){var o=this._tabs[s];if(s==i){if(!o.tab.hasClass("active")){o.tab.addClass("active"),o.container.addClass("active"),a=o,o.tab.children("a").attr("aria-expanded",!0);var l=o.container[0];if(null===elBySel(".messageTabMenuContent.active",l)&&null!==elBySel(".messageTabMenuContent",l)){var c=elBySel("nav > ul > li[data-name] > a",l);null!==c&&$(c).trigger("mousedown")}continue}if(!0===n)continue}o.tab.removeClass("active"),o.container.removeClass("active"),o.tab.children("a").attr("aria-expanded",!1)}null!==e&&(e.preventDefault(),e.stopPropagation()),null!==a&&this._trigger("show",{},{activeTab:a}),$(window).trigger("resize")},showTab:function(e,t){if($.isNumeric(e)||void 0!==this._tabsByName[e]&&(e=this._tabsByName[e]),void 0===this._tabs[e])return void console.debug("[wcf.messageTabMenu] Cannot locate tab identified by '"+e+"'");this._showTab(null,e,t)},getTab:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].tab:null},getContainer:function(e){return void 0!==this._tabsByName[e]?this._tabs[this._tabsByName[e]].container:null}}); })(this);
// WCF.Poll.js
(function (window, undefined) { "use strict";WCF.Poll={},WCF.Poll.Management=Class.extend({_container:{},_count:0,_editorId:"",_maxOptions:0,init:function(){},_createOptionList:function(){},_createOption:function(){},_keyDown:function(){},_addOption:function(){},_removeOption:function(){},_submit:function(){},_reset:function(){},_validate:function(){}}),WCF.Poll.Manager=Class.extend({_cache:{},_canViewParticipants:{},_canViewResult:{},_canVote:{},_inputElements:{},_participants:{},_polls:{},_proxy:null,init:function(t){var e=$(t);if(!e.length)return void console.debug("[WCF.Poll.Manager] Given selector '"+t+"' does not match, aborting.");this._cache={},this._canViewParticipants={},this._canViewResult={},this._inputElements={},this._participants={},this._polls={},this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this),url:"index.php?poll/&t="+SECURITY_TOKEN});var i=this;e.each(function(t,e){var o=$(e),n=o.data("pollID");void 0===i._polls[n]&&(i._cache[n]={result:"",vote:""},i._polls[n]=o,i._canViewParticipants[n]=!!o.data("canViewParticipants"),i._canViewResult[n]=!!o.data("canViewResult"),i._canVote[n]=!!o.data("canVote"),i._bindListeners(n),o.data("inVote")&&i._prepareVote(n),i._toggleButtons(n))})},_bindListeners:function(t){this._polls[t].find(".jsButtonPollShowParticipants").data("pollID",t).click($.proxy(this._showParticipants,this)),this._polls[t].find(".jsButtonPollShowResult").data("pollID",t).click($.proxy(this._showResult,this)),this._polls[t].find(".jsButtonPollShowVote").data("pollID",t).click($.proxy(this._showVote,this)),this._polls[t].find(".jsButtonPollVote").data("pollID",t).click($.proxy(this._vote,this))},_showResult:function(t,e){var i=null===t?e:$(t.currentTarget).data("pollID");this._canViewResult[i]&&this._polls[i].data("inVote")&&(this._cache[i].result?(this._polls[i].find(".pollInnerContainer").html(this._cache[i].result),this._polls[i].data("inVote",!1),this._toggleButtons(i)):(this._proxy.setOption("data",{actionName:"getResult",pollID:i}),this._proxy.sendRequest()))},_showParticipants:function(t){var e=$(t.currentTarget).data("pollID");this._participants[e]||(this._participants[e]=new WCF.User.List("wcf\\data\\poll\\PollAction",this._polls[e].data("question"),{pollID:e})),this._participants[e].open()},_showVote:function(t,e){var i=null===t?e:$(t.currentTarget).data("pollID");this._canVote[i]&&(this._polls[i].data("inVote")||(this._cache[i].vote?(this._polls[i].find(".pollInnerContainer").html(this._cache[i].vote),this._polls[i].data("inVote",!0),this._prepareVote(i),this._toggleButtons(i)):(this._proxy.setOption("data",{actionName:"getVote",pollID:i}),this._proxy.sendRequest())))},_success:function(t,e,i){if(t&&t.actionName){var o=t.pollID;switch(t.resultTemplate&&(this._cache[o].result=t.resultTemplate),t.voteTemplate&&(this._cache[o].vote=t.voteTemplate),t.actionName){case"getResult":this._showResult(null,o);break;case"getVote":this._showVote(null,o);break;case"vote":this._canViewResult[o]=!0,this._canVote[o]=!!t.canVote,this._polls[o].data("isPublic")&&(this._canViewParticipants[o]=!0),this._showResult(null,o)}}},_prepareVote:function(t){this._polls[t].find(".jsButtonPollVote").disable();var e=this._polls[t].find(".pollInnerContainer > .jsPollVote"),i=this;this._inputElements[t]=e.find("input").change(function(){i._handleVoteButton(t)}),this._handleVoteButton(t);var o=e.data("maxVotes");this._inputElements[t].filter("[type=checkbox]").length&&(this._inputElements[t].change(function(){i._enforceMaxVotes(t,o)}),this._enforceMaxVotes(t,o))},_enforceMaxVotes:function(t,e){var i=this._inputElements[t];i.filter(":checked").length==e?i.filter(":not(:checked)").disable():i.enable()},_handleVoteButton:function(t){var e=this._inputElements[t],i=this._polls[t].find(".jsButtonPollVote");e.filter(":checked").length?i.enable():i.disable()},_toggleButtons:function(t){var e=this._polls[t].children(".formSubmit");e.find(".jsButtonPollShowParticipants, .jsButtonPollShowResult, .jsButtonPollShowVote, .jsButtonPollVote").hide();var i=!0;this._polls[t].data("inVote")?(i=!1,e.find(".jsButtonPollVote").show(),this._canViewResult[t]&&e.find(".jsButtonPollShowResult").show()):(this._canVote[t]&&(i=!1,e.find(".jsButtonPollShowVote").show()),this._canViewParticipants[t]&&(i=!1,e.find(".jsButtonPollShowParticipants").show())),i&&e.hide()},_vote:function(t){var e=$(t.currentTarget).data("pollID");if(this._canVote[e]){var i=[];this._inputElements[e].each(function(t,e){var o=$(e);o.is(":checked")&&i.push(o.data("optionID"))}),i.length&&(this._proxy.setOption("data",{actionName:"vote",optionIDs:i,pollID:e}),this._proxy.sendRequest())}}}); })(this);
// promise.min.js
-!function(e){function n(){}function t(e,n){return function(){e.apply(n,arguments)}}function o(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],s(e,this)}function i(e,n){for(;3===e._state;)e=e._value;return 0===e._state?void e._deferreds.push(n):(e._handled=!0,void o._immediateFn(function(){var t=1===e._state?n.onFulfilled:n.onRejected;if(null===t)return void(1===e._state?r:u)(n.promise,e._value);var o;try{o=t(e._value)}catch(i){return void u(n.promise,i)}r(n.promise,o)}))}function r(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var i=n.then;if(n instanceof o)return e._state=3,e._value=n,void f(e);if("function"==typeof i)return void s(t(i,n),e)}e._state=1,e._value=n,f(e)}catch(r){u(e,r)}}function u(e,n){e._state=2,e._value=n,f(e)}function f(e){2===e._state&&0===e._deferreds.length&&o._immediateFn(function(){e._handled||o._unhandledRejectionFn(e._value)});for(var n=0,t=e._deferreds.length;n<t;n++)i(e,e._deferreds[n]);e._deferreds=null}function c(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}function s(e,n){var t=!1;try{e(function(e){t||(t=!0,r(n,e))},function(e){t||(t=!0,u(n,e))})}catch(o){if(t)return;t=!0,u(n,o)}}var a=setTimeout;o.prototype["catch"]=function(e){return this.then(null,e)},o.prototype.then=function(e,t){var o=new this.constructor(n);return i(this,new c(e,t,o)),o},o.all=function(e){var n=Array.prototype.slice.call(e);return new o(function(e,t){function o(r,u){try{if(u&&("object"==typeof u||"function"==typeof u)){var f=u.then;if("function"==typeof f)return void f.call(u,function(e){o(r,e)},t)}n[r]=u,0===--i&&e(n)}catch(c){t(c)}}if(0===n.length)return e([]);for(var i=n.length,r=0;r<n.length;r++)o(r,n[r])})},o.resolve=function(e){return e&&"object"==typeof e&&e.constructor===o?e:new o(function(n){n(e)})},o.reject=function(e){return new o(function(n,t){t(e)})},o.race=function(e){return new o(function(n,t){for(var o=0,i=e.length;o<i;o++)e[o].then(n,t)})},o._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){a(e,0)},o._unhandledRejectionFn=function(e){"undefined"!=typeof console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)},o._setImmediateFn=function(e){o._immediateFn=e},o._setUnhandledRejectionFn=function(e){o._unhandledRejectionFn=e},"undefined"!=typeof module&&module.exports?module.exports=o:e.Promise||(e.Promise=o)}(this);
+!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(n){var t=this.constructor;return this.then(function(e){return t.resolve(n()).then(function(){return e})},function(e){return t.resolve(n()).then(function(){return t.reject(e)})})}var n=setTimeout;function o(){}function f(e){if(!(this instanceof f))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=undefined,this._deferreds=[],l(e,this)}function r(o,r){for(;3===o._state;)o=o._value;0!==o._state?(o._handled=!0,f._immediateFn(function(){var e=1===o._state?r.onFulfilled:r.onRejected;if(null!==e){var n;try{n=e(o._value)}catch(t){return void u(r.promise,t)}i(r.promise,n)}else(1===o._state?i:u)(r.promise,o._value)})):o._deferreds.push(r)}function i(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var t=n.then;if(n instanceof f)return e._state=3,e._value=n,void c(e);if("function"==typeof t)return void l((o=t,r=n,function(){o.apply(r,arguments)}),e)}e._state=1,e._value=n,c(e)}catch(i){u(e,i)}var o,r}function u(e,n){e._state=2,e._value=n,c(e)}function c(e){2===e._state&&0===e._deferreds.length&&f._immediateFn(function(){e._handled||f._unhandledRejectionFn(e._value)});for(var n=0,t=e._deferreds.length;n<t;n++)r(e,e._deferreds[n]);e._deferreds=null}function a(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}function l(e,n){var t=!1;try{e(function(e){t||(t=!0,i(n,e))},function(e){t||(t=!0,u(n,e))})}catch(o){if(t)return;t=!0,u(n,o)}}f.prototype["catch"]=function(e){return this.then(null,e)},f.prototype.then=function(e,n){var t=new this.constructor(o);return r(this,new a(e,n,t)),t},f.prototype["finally"]=e,f.all=function(n){return new f(function(r,i){if(!n||"undefined"==typeof n.length)throw new TypeError("Promise.all accepts an array");var f=Array.prototype.slice.call(n);if(0===f.length)return r([]);var u=f.length;function c(n,e){try{if(e&&("object"==typeof e||"function"==typeof e)){var t=e.then;if("function"==typeof t)return void t.call(e,function(e){c(n,e)},i)}f[n]=e,0==--u&&r(f)}catch(o){i(o)}}for(var e=0;e<f.length;e++)c(e,f[e])})},f.resolve=function(n){return n&&"object"==typeof n&&n.constructor===f?n:new f(function(e){e(n)})},f.reject=function(t){return new f(function(e,n){n(t)})},f.race=function(r){return new f(function(e,n){for(var t=0,o=r.length;t<o;t++)r[t].then(e,n)})},f._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){n(e,0)},f._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var t=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw Error("unable to locate global object")}();"Promise"in t?t.Promise.prototype["finally"]||(t.Promise.prototype["finally"]=e):t.Promise=f});
+
// WoltLabSuite.Core.min.js
-var requirejs,require,define;!function(global,Promise,undef){function commentReplace(e,t){return t||""}function hasProp(e,t){return hasOwn.call(e,t)}function getOwn(e,t){return e&&hasProp(e,t)&&e[t]}function obj(){return Object.create(null)}function eachProp(e,t){var i;for(i in e)if(hasProp(e,i)&&t(e[i],i))break}function mixin(e,t,i,n){return t&&eachProp(t,function(t,a){!i&&hasProp(e,a)||(!n||"object"!=typeof t||!t||Array.isArray(t)||"function"==typeof t||t instanceof RegExp?e[a]=t:(e[a]||(e[a]={}),mixin(e[a],t,i,n)))}),e}function getGlobal(e){if(!e)return e;var t=global;return e.split(".").forEach(function(e){t=t[e]}),t}function newContext(e){function t(e){var t,i,n=e.length;for(t=0;t<n;t++)if("."===(i=e[t]))e.splice(t,1),t-=1;else if(".."===i){if(0===t||1===t&&".."===e[2]||".."===e[t-1])continue;t>0&&(e.splice(t-1,2),t-=2)}}function i(e,i,n){var a,o,r,s,l,c,d,u,h,p,f=i&&i.split("/"),m=f,g=k.map,v=g&&g["*"];if(e&&(e=e.split("/"),c=e.length-1,k.nodeIdCompat&&jsSuffixRegExp.test(e[c])&&(e[c]=e[c].replace(jsSuffixRegExp,"")),"."===e[0].charAt(0)&&f&&(m=f.slice(0,f.length-1),e=m.concat(e)),t(e),e=e.join("/")),n&&g&&(f||v)){o=e.split("/");e:for(r=o.length;r>0;r-=1){if(l=o.slice(0,r).join("/"),f)for(s=f.length;s>0;s-=1)if((a=getOwn(g,f.slice(0,s).join("/")))&&(a=getOwn(a,l))){d=a,u=r;break e}!h&&v&&getOwn(v,l)&&(h=getOwn(v,l),p=r)}!d&&h&&(d=h,u=p),d&&(o.splice(0,u,d),e=o.join("/"))}return getOwn(k.pkgs,e)||e}function n(e){function t(){var t;return e.init&&(t=e.init.apply(global,arguments)),t||e.exports&&getGlobal(e.exports)}return t}function a(e){var t,i,n,a;for(t=0;t<queue.length;t+=1){if("string"!=typeof queue[t][0]){if(!e)break;queue[t].unshift(e),e=undef}n=queue.shift(),i=n[0],t-=1,i in T||i in N||(i in A?C.apply(undef,n):N[i]=n)}e&&(a=getOwn(k.shim,e)||{},C(e,a.deps||[],a.exportsFn))}function o(e,t){var n=function(i,o,r,s){var l,c;if(t&&a(),"string"==typeof i){if(S[i])return S[i](e);if(!((l=E(i,e,!0).id)in T))throw new Error("Not loaded: "+l);return T[l]}return i&&!Array.isArray(i)&&(c=i,i=undef,Array.isArray(o)&&(i=o,o=r,r=s),t)?n.config(c)(i,o,r):(o=o||function(){return slice.call(arguments,0)},V.then(function(){return a(),C(undef,i||[],o,r,e)}))};return n.isBrowser="undefined"!=typeof document&&"undefined"!=typeof navigator,n.nameToUrl=function(e,t,i){var a,o,r,s,l,c,d,u=getOwn(k.pkgs,e);if(u&&(e=u),d=getOwn(F,e))return n.nameToUrl(d,t,i);if(urlRegExp.test(e))l=e+(t||"");else{for(a=k.paths,o=e.split("/"),r=o.length;r>0;r-=1)if(s=o.slice(0,r).join("/"),c=getOwn(a,s)){Array.isArray(c)&&(c=c[0]),o.splice(0,r,c);break}l=o.join("/"),l+=t||(/^data\:|^blob\:|\?/.test(l)||i?"":".js"),l=("/"===l.charAt(0)||l.match(/^[\w\+\.\-]+:/)?"":k.baseUrl)+l}return k.urlArgs&&!/^blob\:/.test(l)?l+k.urlArgs(e,l):l},n.toUrl=function(t){var a,o=t.lastIndexOf("."),r=t.split("/")[0],s="."===r||".."===r;return-1!==o&&(!s||o>1)&&(a=t.substring(o,t.length),t=t.substring(0,o)),n.nameToUrl(i(t,e),a,!0)},n.defined=function(t){return E(t,e,!0).id in T},n.specified=function(t){return(t=E(t,e,!0).id)in T||t in A},n}function r(e,t,i){e&&(T[e]=i,requirejs.onResourceLoad&&requirejs.onResourceLoad(I,t.map,t.deps)),t.finished=!0,t.resolve(i)}function s(e,t){e.finished=!0,e.rejected=!0,e.reject(t)}function l(e){return function(t){return i(t,e,!0)}}function c(e){e.factoryCalled=!0;var t,i=e.map.id;try{t=I.execCb(i,e.factory,e.values,T[i])}catch(t){return s(e,t)}i?t===undef&&(e.cjsModule?t=e.cjsModule.exports:e.usingExports&&(t=T[i])):B.splice(B.indexOf(e),1),r(i,e,t)}function d(e,t){this.rejected||this.depDefined[t]||(this.depDefined[t]=!0,this.depCount+=1,this.values[t]=e,this.depending||this.depCount!==this.depMax||c(this))}function u(e,t){var i={};return i.promise=new Promise(function(t,n){i.resolve=t,i.reject=function(t){e||B.splice(B.indexOf(i),1),n(t)}}),i.map=e?t||E(e):{},i.depCount=0,i.depMax=0,i.values=[],i.depDefined=[],i.depFinished=d,i.map.pr&&(i.deps=[E(i.map.pr)]),i}function h(e,t){var i;return e?(i=e in A&&A[e])||(i=A[e]=u(e,t)):(i=u(),B.push(i)),i}function p(e,t){return function(i){e.rejected||(i.dynaId||(i.dynaId="id"+(W+=1),i.requireModules=[t]),s(e,i))}}function f(e,t,i,n){i.depMax+=1,L(e,t).then(function(e){i.depFinished(e,n)},p(i,e.id)).catch(p(i,i.map.id))}function m(e){function t(t){i||r(e,h(e),t)}var i;return t.error=function(t){h(e).reject(t)},t.fromText=function(t,n){var o=h(e),r=E(E(e).n),l=r.id;i=!0,o.factory=function(e,t){return t},n&&(t=n),hasProp(k.config,e)&&(k.config[l]=k.config[e]);try{y.exec(t)}catch(e){s(o,new Error("fromText eval for "+l+" failed: "+e))}a(l),o.deps=[r],f(r,null,o,o.deps.length)},t}function g(e,t,i){e.load(t.n,o(i),m(t.id),k)}function v(e){var t,i=e?e.indexOf("!"):-1;return i>-1&&(t=e.substring(0,i),e=e.substring(i+1,e.length)),[t,e]}function _(e,t,i){var n=e.map.id;t[n]=!0,!e.finished&&e.deps&&e.deps.forEach(function(n){var a=n.id,o=!hasProp(S,a)&&h(a,n);!o||o.finished||i[a]||(hasProp(t,a)?e.deps.forEach(function(t,i){t.id===a&&e.depFinished(T[a],i)}):_(o,t,i))}),i[n]=!0}function b(e){var t,i,n,a=[],o=1e3*k.waitSeconds,r=o&&O+o<(new Date).getTime();if(0===P&&(e?e.finished||_(e,{},{}):B.length&&B.forEach(function(e){_(e,{},{})})),r){for(i in A)n=A[i],n.finished||a.push(n.map.id);t=new Error("Timeout for modules: "+a),t.requireModules=a,y.onError(t)}else(P||B.length)&&(D||(D=!0,setTimeout(function(){D=!1,b()},70)))}function w(e){return setTimeout(function(){e.dynaId&&R[e.dynaId]||(R[e.dynaId]=!0,y.onError(e))}),e}var y,C,E,L,S,D,x,I,T=obj(),N=obj(),k={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},M=obj(),B=[],A=obj(),U=obj(),j=obj(),P=0,O=(new Date).getTime(),W=0,R=obj(),H=obj(),F=obj(),V=Promise.resolve();return x="function"==typeof importScripts?function(e){var t=e.url;H[t]||(H[t]=!0,h(e.id),importScripts(t),a(e.id))}:function(e){var t,i=e.id,n=e.url;H[n]||(H[n]=!0,t=document.createElement("script"),t.setAttribute("data-requiremodule",i),t.type=k.scriptType||"text/javascript",t.charset="utf-8",t.async=!0,P+=1,t.addEventListener("load",function(){P-=1,a(i)},!1),t.addEventListener("error",function(){P-=1;var e,n=getOwn(k.paths,i);if(n&&Array.isArray(n)&&n.length>1){t.parentNode.removeChild(t),n.shift();var a=h(i);a.map=E(i),a.map.url=y.nameToUrl(i),x(a.map)}else e=new Error("Load failed: "+i+": "+t.src),e.requireModules=[i],h(i).reject(e)},!1),t.src=n,10===document.documentMode?asap.then(function(){document.head.appendChild(t)}):document.head.appendChild(t))},L=function(e,t){var i,n,a=e.id,o=k.shim[a];if(a in N)i=N[a],delete N[a],C.apply(undef,i);else if(!(a in A))if(e.pr){if(!(n=getOwn(F,a)))return L(E(e.pr)).then(function(i){var n=e.prn?e:E(a,t,!0),o=n.id,r=getOwn(k.shim,o);return o in j||(j[o]=!0,r&&r.deps?y(r.deps,function(){g(i,n,t)}):g(i,n,t)),h(o).promise});e.url=y.nameToUrl(n),x(e)}else o&&o.deps?y(o.deps,function(){x(e)}):x(e);return h(a).promise},E=function(e,t,n){if("string"!=typeof e)return e;var a,o,r,s,c,d,u=e+" & "+(t||"")+" & "+!!n;return r=v(e),s=r[0],e=r[1],!s&&u in M?M[u]:(s&&(s=i(s,t,n),a=s in T&&T[s]),s?a&&a.normalize?(e=a.normalize(e,l(t)),d=!0):e=-1===e.indexOf("!")?i(e,t,n):e:(e=i(e,t,n),r=v(e),s=r[0],e=r[1],o=y.nameToUrl(e)),c={id:s?s+"!"+e:e,n:e,pr:s,url:o,prn:s&&d},s||(M[u]=c),c)},S={require:function(e){return o(e)},exports:function(e){var t=T[e];return void 0!==t?t:T[e]={}},module:function(e){return{id:e,uri:"",exports:S.exports(e),config:function(){return getOwn(k.config,e)||{}}}}},C=function(e,t,i,n,a){if(e){if(e in U)return;U[e]=!0}var o=h(e);return t&&!Array.isArray(t)&&(i=t,t=[]),t=t?slice.call(t,0):null,n||(hasProp(k,"defaultErrback")?k.defaultErrback&&(n=k.defaultErrback):n=w),n&&o.promise.catch(n),a=a||e,"function"==typeof i?(!t.length&&i.length&&(i.toString().replace(commentRegExp,commentReplace).replace(cjsRequireRegExp,function(e,i){t.push(i)}),t=(1===i.length?["require"]:["require","exports","module"]).concat(t)),o.factory=i,o.deps=t,o.depending=!0,t.forEach(function(i,n){var r;t[n]=r=E(i,a,!0),i=r.id,"require"===i?o.values[n]=S.require(e):"exports"===i?(o.values[n]=S.exports(e),o.usingExports=!0):"module"===i?o.values[n]=o.cjsModule=S.module(e):void 0===i?o.values[n]=void 0:f(r,a,o,n)}),o.depending=!1,o.depCount===o.depMax&&c(o)):e&&r(e,o,i),O=(new Date).getTime(),e||b(o),o.promise},y=o(null,!0),y.config=function(t){if(t.context&&t.context!==e){var i=getOwn(contexts,t.context);return i?i.req.config(t):newContext(t.context).config(t)}if(M=obj(),t.baseUrl&&"/"!==t.baseUrl.charAt(t.baseUrl.length-1)&&(t.baseUrl+="/"),"string"==typeof t.urlArgs){var a=t.urlArgs;t.urlArgs=function(e,t){return(-1===t.indexOf("?")?"?":"&")+a}}var o=k.shim,r={paths:!0,bundles:!0,config:!0,map:!0};return eachProp(t,function(e,t){r[t]?(k[t]||(k[t]={}),mixin(k[t],e,!0,!0)):k[t]=e}),t.bundles&&eachProp(t.bundles,function(e,t){e.forEach(function(e){e!==t&&(F[e]=t)})}),t.shim&&(eachProp(t.shim,function(e,t){Array.isArray(e)&&(e={deps:e}),!e.exports&&!e.init||e.exportsFn||(e.exportsFn=n(e)),o[t]=e}),k.shim=o),t.packages&&t.packages.forEach(function(e){var t,i;e="string"==typeof e?{name:e}:e,i=e.name,t=e.location,t&&(k.paths[i]=e.location),k.pkgs[i]=e.name+"/"+(e.main||"main").replace(currDirRegExp,"").replace(jsSuffixRegExp,"")}),(t.deps||t.callback)&&y(t.deps,t.callback),y},y.onError=function(e){throw e},I={id:e,defined:T,waiting:N,config:k,deferreds:A,req:y,execCb:function(e,t,i,n){return t.apply(n,i)}},contexts[e]=I,y}if(!Promise)throw new Error("No Promise implementation available");var topReq,dataMain,src,subPath,bootstrapConfig=requirejs||require,hasOwn=Object.prototype.hasOwnProperty,contexts={},queue=[],currDirRegExp=/^\.\//,urlRegExp=/^\/|\:|\?|\.js$/,commentRegExp=/\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,cjsRequireRegExp=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,jsSuffixRegExp=/\.js$/,slice=Array.prototype.slice;if("function"!=typeof requirejs){var asap=Promise.resolve(void 0);requirejs=topReq=newContext("_"),"function"!=typeof require&&(require=topReq),topReq.exec=function(text){return eval(text)},topReq.contexts=contexts,define=function(){queue.push(slice.call(arguments,0))},define.amd={jQuery:!0},bootstrapConfig&&topReq.config(bootstrapConfig),topReq.isBrowser&&!contexts._.config.skipDataMain&&(dataMain=document.querySelectorAll("script[data-main]")[0],(dataMain=dataMain&&dataMain.getAttribute("data-main"))&&(dataMain=dataMain.replace(jsSuffixRegExp,""),bootstrapConfig&&bootstrapConfig.baseUrl||-1!==dataMain.indexOf("!")||(src=dataMain.split("/"),dataMain=src.pop(),subPath=src.length?src.join("/")+"/":"./",topReq.config({baseUrl:subPath})),topReq([dataMain])))}}(this,"undefined"!=typeof Promise?Promise:void 0),define("requireLib",function(){}),requirejs.config({paths:{enquire:"3rdParty/enquire",favico:"3rdParty/favico","perfect-scrollbar":"3rdParty/perfect-scrollbar"},shim:{enquire:{exports:"enquire"},favico:{exports:"Favico"},"perfect-scrollbar":{exports:"PerfectScrollbar"}},map:{"*":{Ajax:"WoltLabSuite/Core/Ajax",AjaxJsonp:"WoltLabSuite/Core/Ajax/Jsonp",AjaxRequest:"WoltLabSuite/Core/Ajax/Request",CallbackList:"WoltLabSuite/Core/CallbackList",ColorUtil:"WoltLabSuite/Core/ColorUtil",Core:"WoltLabSuite/Core/Core",DateUtil:"WoltLabSuite/Core/Date/Util",Devtools:"WoltLabSuite/Core/Devtools",Dictionary:"WoltLabSuite/Core/Dictionary","Dom/ChangeListener":"WoltLabSuite/Core/Dom/Change/Listener","Dom/Traverse":"WoltLabSuite/Core/Dom/Traverse","Dom/Util":"WoltLabSuite/Core/Dom/Util",Environment:"WoltLabSuite/Core/Environment",EventHandler:"WoltLabSuite/Core/Event/Handler",EventKey:"WoltLabSuite/Core/Event/Key",Language:"WoltLabSuite/Core/Language",List:"WoltLabSuite/Core/List",ObjectMap:"WoltLabSuite/Core/ObjectMap",Permission:"WoltLabSuite/Core/Permission",StringUtil:"WoltLabSuite/Core/StringUtil","Ui/Alignment":"WoltLabSuite/Core/Ui/Alignment","Ui/CloseOverlay":"WoltLabSuite/Core/Ui/CloseOverlay","Ui/Confirmation":"WoltLabSuite/Core/Ui/Confirmation","Ui/Dialog":"WoltLabSuite/Core/Ui/Dialog","Ui/Notification":"WoltLabSuite/Core/Ui/Notification","Ui/ReusableDropdown":"WoltLabSuite/Core/Ui/Dropdown/Reusable","Ui/Screen":"WoltLabSuite/Core/Ui/Screen","Ui/Scroll":"WoltLabSuite/Core/Ui/Scroll","Ui/SimpleDropdown":"WoltLabSuite/Core/Ui/Dropdown/Simple","Ui/TabMenu":"WoltLabSuite/Core/Ui/TabMenu",Upload:"WoltLabSuite/Core/Upload",User:"WoltLabSuite/Core/User"}},waitSeconds:0}),define("jquery",[],function(){return window.jQuery}),define("require.config",function(){}),function(e,t){e.elAttr=function(e,t,i){if(void 0===i)return e.getAttribute(t)||"";e.setAttribute(t,i)},e.elAttrBool=function(e,t){var i=elAttr(e,t);return"1"===i||"true"===i},e.elByClass=function(e,i){return(i||t).getElementsByClassName(e)},e.elById=function(e){return t.getElementById(e)},e.elBySel=function(e,i){return(i||t).querySelector(e)},e.elBySelAll=function(e,i,n){var a=(i||t).querySelectorAll(e);return"function"==typeof n&&Array.prototype.forEach.call(a,n),a},e.elByTag=function(e,i){return(i||t).getElementsByTagName(e)},e.elCreate=function(e){return t.createElement(e)},e.elClosest=function(e,t){if(!(e instanceof Node))throw new TypeError("Provided element is not a Node.");return e.nodeType===Node.TEXT_NODE&&null===(e=e.parentNode)?null:("string"!=typeof t&&(t=""),0===t.length?e:e.closest(t))},e.elData=function(e,t,i){if(t="data-"+t,void 0===i)return e.getAttribute(t)||"";e.setAttribute(t,i)},e.elDataBool=function(e,t){var i=elData(e,t);return"1"===i||"true"===i},e.elHide=function(e){e.style.setProperty("display","none","")},e.elInnerError=function(e,t,i){var n=e.parentNode;if(null===n)throw new Error("Only elements that have a parent element or document are valid.");if("string"!=typeof t){if(void 0!==t&&null!==t&&!1!==t)throw new TypeError("The error message must be a string; `false`, `null` or `undefined` can be used as a substitute for an empty string.");t=""}var a=e.nextElementSibling;return null!==a&&"SMALL"===a.nodeName&&a.classList.contains("innerError")||(""===t?a=null:(a=elCreate("small"),a.className="innerError",n.insertBefore(a,e.nextSibling))),""===t?null!==a&&(n.removeChild(a),a=null):a[i?"innerHTML":"textContent"]=t,a},e.elRemove=function(e){e.parentNode.removeChild(e)},e.elShow=function(e){e.style.removeProperty("display")},e.elToggle=function(e){"none"===e.style.getPropertyValue("display")?elShow(e):elHide(e)},e.forEach=function(e,t){for(var i=0,n=e.length;i<n;i++)t(e[i],i)},e.objOwns=function(e,t){return e.hasOwnProperty(t)};"touchstart"in t.documentElement||"ontouchstart"in e||navigator.MaxTouchPoints>0||navigator.msMaxTouchPoints;Object.defineProperty(e,"WCF_CLICK_EVENT",{value:"click"}),function(){function t(){e.history.state&&e.history.state.name&&"initial"!==e.history.state.name?(e.history.replaceState({name:"skip",depth:++i},""),e.history.back(),setTimeout(t,1)):e.history.replaceState({name:"initial"},"")}var i=0;t(),e.addEventListener("popstate",function(t){t.state&&t.state.name&&"skip"===t.state.name&&e.history.go(t.state.depth)})}(),e.String.prototype.hashCode=function(){var e,t=0;if(this.length)for(var i=0,n=this.length;i<n;i++)e=this.charCodeAt(i),t=(t<<5)-t+e,t&=t;return t}}(window,document),define("wcf.globalHelper",function(){}),define("WoltLabSuite/Core/Core",[],function(){"use strict";var e=function(e){return"object"==typeof e&&(Array.isArray(e)||n.isPlainObject(e))?t(e):e},t=function(t){if(!t)return null;if(Array.isArray(t))return t.slice();var i={};for(var n in t)t.hasOwnProperty(n)&&void 0!==t[n]&&(i[n]=e(t[n]));return i},i="wsc"+window.WCF_PATH.hashCode()+"-",n={clone:function(t){return e(t)},convertLegacyUrl:function(e){return e.replace(/^index\.php\/(.*?)\/\?/,function(e,t){var i=t.split(/([A-Z][a-z0-9]+)/);t="";for(var n=0,a=i.length;n<a;n++){var o=i[n].trim();o.length&&(t.length&&(t+="-"),t+=o.toLowerCase())}return"index.php?"+t+"/&"})},extend:function(e){e=e||{};for(var t=this.clone(e),i=1,n=arguments.length;i<n;i++){var a=arguments[i];if(a)for(var o in a)objOwns(a,o)&&(Array.isArray(a[o])||"object"!=typeof a[o]?t[o]=a[o]:this.isPlainObject(a[o])?t[o]=this.extend(e[o],a[o]):t[o]=a[o])}return t},inherit:function(e,t,i){if(void 0===e||null===e)throw new TypeError("The constructor must not be undefined or null.");if(void 0===t||null===t)throw new TypeError("The super constructor must not be undefined or null.");if(void 0===t.prototype)throw new TypeError("The super constructor must have a prototype.");e._super=t,e.prototype=n.extend(Object.create(t.prototype,{constructor:{configurable:!0,enumerable:!1,value:e,writable:!0}}),i||{})},isPlainObject:function(e){return"object"==typeof e&&null!==e&&!e.nodeType&&Object.getPrototypeOf(e)===Object.prototype},getType:function(e){return Object.prototype.toString.call(e).replace(/^\[object (.+)\]$/,"$1")},getUuid:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)})},serialize:function(e,t){var i=[];for(var n in e)if(objOwns(e,n)){var a=t?t+"["+n+"]":n,o=e[n];"object"==typeof o?i.push(this.serialize(o,a)):i.push(encodeURIComponent(a)+"="+encodeURIComponent(o))}return i.join("&")},triggerEvent:function(e,t){var i;try{i=new Event(t,{bubbles:!0,cancelable:!0})}catch(e){i=document.createEvent("Event"),i.initEvent(t,!0,!0)}e.dispatchEvent(i)},getStoragePrefix:function(){return i}};return n}),define("WoltLabSuite/Core/Dictionary",["Core"],function(e){"use strict";function t(){this._dictionary=i?new Map:{}}var i=objOwns(window,"Map")&&"function"==typeof window.Map;return t.prototype={set:function(e,t){if("number"==typeof e&&(e=e.toString()),"string"!=typeof e)throw new TypeError("Only strings can be used as keys, rejected '"+e+"' ("+typeof e+").");i?this._dictionary.set(e,t):this._dictionary[e]=t},delete:function(e){"number"==typeof e&&(e=e.toString()),i?this._dictionary.delete(e):this._dictionary[e]=void 0},has:function(e){return"number"==typeof e&&(e=e.toString()),i?this._dictionary.has(e):objOwns(this._dictionary,e)&&void 0!==this._dictionary[e]},get:function(e){if("number"==typeof e&&(e=e.toString()),this.has(e))return i?this._dictionary.get(e):this._dictionary[e]},forEach:function(e){if("function"!=typeof e)throw new TypeError("forEach() expects a callback as first parameter.");if(i)this._dictionary.forEach(e);else for(var t=Object.keys(this._dictionary),n=0,a=t.length;n<a;n++)e(this._dictionary[t[n]],t[n])},merge:function(){for(var e=0,i=arguments.length;e<i;e++){var n=arguments[e];if(!(n instanceof t))throw new TypeError("Expected an object of type Dictionary, but argument "+e+" is not.");n.forEach(function(e,t){this.set(t,e)}.bind(this))}},toObject:function(){if(!i)return e.clone(this._dictionary);var t={};return this._dictionary.forEach(function(e,i){t[i]=e}),t}},t.fromObject=function(e){var i=new t;for(var n in e)objOwns(e,n)&&i.set(n,e[n]);return i},Object.defineProperty(t.prototype,"size",{enumerable:!1,configurable:!0,get:function(){return i?this._dictionary.size:Object.keys(this._dictionary).length}}),t}),define("WoltLabSuite/Core/Template.grammar",["require"],function(e){var t=function(e,t,i,n){for(i=i||{},n=e.length;n--;i[e[n]]=t);return i},i=[2,37],n=[5,9,11,12,13,18,19,21,22,23,25,26,27,28,30,31,32,33,35,37,39],a=[1,24],o=[1,25],r=[1,31],s=[1,29],l=[1,30],c=[1,26],d=[1,27],u=[1,33],h=[11,12,15,40,41,45,47,49,50,52],p=[9,11,12,13,18,19,21,23,26,28,30,31,32,33,35,37],f=[11,12,15,40,41,44,45,46,47,49,50,52],m=[18,35,37],g=[12,15],v={trace:function(){},yy:{},symbols_:{error:2,TEMPLATE:3,CHUNK_STAR:4,EOF:5,CHUNK_STAR_repetition0:6,CHUNK:7,PLAIN_ANY:8,T_LITERAL:9,COMMAND:10,T_ANY:11,T_WS:12,"{if":13,COMMAND_PARAMETERS:14,"}":15,COMMAND_repetition0:16,COMMAND_option0:17,"{/if}":18,"{include":19,COMMAND_PARAMETER_LIST:20,"{implode":21,"{/implode}":22,"{foreach":23,COMMAND_option1:24,"{/foreach}":25,"{lang}":26,"{/lang}":27,"{":28,VARIABLE:29,"{#":30,"{@":31,"{ldelim}":32,"{rdelim}":33,ELSE:34,"{else}":35,ELSE_IF:36,"{elseif":37,FOREACH_ELSE:38,"{foreachelse}":39,T_VARIABLE:40,T_VARIABLE_NAME:41,VARIABLE_repetition0:42,VARIABLE_SUFFIX:43,"[":44,"]":45,".":46,"(":47,VARIABLE_SUFFIX_option0:48,")":49,"=":50,COMMAND_PARAMETER_VALUE:51,T_QUOTED_STRING:52,COMMAND_PARAMETERS_repetition_plus0:53,COMMAND_PARAMETER:54,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",9:"T_LITERAL",11:"T_ANY",12:"T_WS",13:"{if",15:"}",18:"{/if}",19:"{include",21:"{implode",22:"{/implode}",23:"{foreach",25:"{/foreach}",26:"{lang}",27:"{/lang}",28:"{",30:"{#",31:"{@",32:"{ldelim}",33:"{rdelim}",35:"{else}",37:"{elseif",39:"{foreachelse}",40:"T_VARIABLE",41:"T_VARIABLE_NAME",44:"[",45:"]",46:".",47:"(",49:")",50:"=",52:"T_QUOTED_STRING"},productions_:[0,[3,2],[4,1],[7,1],[7,1],[7,1],[8,1],[8,1],[10,7],[10,3],[10,5],[10,6],[10,3],[10,3],[10,3],[10,3],[10,1],[10,1],[34,2],[36,4],[38,2],[29,3],[43,3],[43,2],[43,3],[20,5],[20,3],[51,1],[51,1],[14,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,3],[6,0],[6,2],[16,0],[16,2],[17,0],[17,1],[24,0],[24,1],[42,0],[42,2],[48,0],[48,1],[53,1],[53,2]],performAction:function(e,t,i,n,a,o,r){var s=o.length-1;switch(a){case 1:return o[s-1]+";";case 2:var l=o[s].reduce(function(e,t){return t.encode&&!e[1]?e[0]+=" + '"+t.value:t.encode&&e[1]?e[0]+=t.value:!t.encode&&e[1]?e[0]+="' + "+t.value:t.encode||e[1]||(e[0]+=" + "+t.value),e[1]=t.encode,e},["''",!1]);l[1]&&(l[0]+="'"),this.$=l[0];break;case 3:case 4:this.$={encode:!0,value:o[s].replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/g,"\\n")};break;case 5:this.$={encode:!1,value:o[s]};break;case 8:this.$="(function() { if ("+o[s-5]+") { return "+o[s-3]+"; } "+o[s-2].join(" ")+" "+(o[s-1]||"")+" return ''; })()";break;case 9:if(!o[s-1].file)throw new Error("Missing parameter file");this.$=o[s-1].file+".fetch(v)";break;case 10:if(!o[s-3].from)throw new Error("Missing parameter from");if(!o[s-3].item)throw new Error("Missing parameter item");o[s-3].glue||(o[s-3].glue="', '"),this.$="(function() { return "+o[s-3].from+".map(function(item) { v["+o[s-3].item+"] = item; return "+o[s-1]+"; }).join("+o[s-3].glue+"); })()";break;case 11:if(!o[s-4].from)throw new Error("Missing parameter from");if(!o[s-4].item)throw new Error("Missing parameter item");this.$="(function() {var looped = false, result = '';if ("+o[s-4].from+" instanceof Array) {for (var i = 0; i < "+o[s-4].from+".length; i++) { looped = true;v["+o[s-4].key+"] = i;v["+o[s-4].item+"] = "+o[s-4].from+"[i];result += "+o[s-2]+";}} else {for (var key in "+o[s-4].from+") {if (!"+o[s-4].from+".hasOwnProperty(key)) continue;looped = true;v["+o[s-4].key+"] = key;v["+o[s-4].item+"] = "+o[s-4].from+"[key];result += "+o[s-2]+";}}return (looped ? result : "+(o[s-1]||"''")+"); })()";break;case 12:this.$="Language.get("+o[s-1]+", v)";break;case 13:this.$="StringUtil.escapeHTML("+o[s-1]+")";break;case 14:this.$="StringUtil.formatNumeric("+o[s-1]+")";break;case 15:this.$=o[s-1];break;case 16:this.$="'{'";break;case 17:this.$="'}'";break;case 18:this.$="else { return "+o[s]+"; }";break;case 19:this.$="else if ("+o[s-2]+") { return "+o[s]+"; }";break;case 20:this.$=o[s];break;case 21:this.$="v['"+o[s-1]+"']"+o[s].join("");break;case 22:this.$=o[s-2]+o[s-1]+o[s];break;case 23:this.$="['"+o[s]+"']";break;case 24:case 36:this.$=o[s-2]+(o[s-1]||"")+o[s];break;case 25:this.$=o[s],this.$[o[s-4]]=o[s-2];break;case 26:this.$={},this.$[o[s-2]]=o[s];break;case 29:this.$=o[s].join("");break;case 37:case 39:case 45:this.$=[];break;case 38:case 40:case 46:case 50:o[s-1].push(o[s]);break;case 49:this.$=[o[s]]}},table:[t([5,9,11,12,13,19,21,23,26,28,30,31,32,33],i,{3:1,4:2,6:3}),{1:[3]},{5:[1,4]},t([5,18,22,25,27,35,37,39],[2,2],{7:5,8:6,10:8,9:[1,7],11:[1,9],12:[1,10],13:[1,11],19:[1,12],21:[1,13],23:[1,14],26:[1,15],28:[1,16],30:[1,17],31:[1,18],32:[1,19],33:[1,20]}),{1:[2,1]},t(n,[2,38]),t(n,[2,3]),t(n,[2,4]),t(n,[2,5]),t(n,[2,6]),t(n,[2,7]),{11:a,12:o,14:21,29:28,40:r,41:s,47:l,50:c,52:d,53:22,54:23},{20:32,41:u},{20:34,41:u},{20:35,41:u},t([9,11,12,13,19,21,23,26,27,28,30,31,32,33],i,{6:3,4:36}),{29:37,40:r},{29:38,40:r},{29:39,40:r},t(n,[2,16]),t(n,[2,17]),{15:[1,40]},t([15,45,49],[2,29],{29:28,54:41,11:a,12:o,40:r,41:s,47:l,50:c,52:d}),t(h,[2,49]),t(h,[2,30]),t(h,[2,31]),t(h,[2,32]),t(h,[2,33]),t(h,[2,34]),t(h,[2,35]),{11:a,12:o,14:42,29:28,40:r,41:s,47:l,50:c,52:d,53:22,54:23},{41:[1,43]},{15:[1,44]},{50:[1,45]},{15:[1,46]},{15:[1,47]},{27:[1,48]},{15:[1,49]},{15:[1,50]},{15:[1,51]},t(p,i,{6:3,4:52}),t(h,[2,50]),{49:[1,53]},t(f,[2,45],{42:54}),t(n,[2,9]),{29:57,40:r,51:55,52:[1,56]},t([9,11,12,13,19,21,22,23,26,28,30,31,32,33],i,{6:3,4:58}),t([9,11,12,13,19,21,23,25,26,28,30,31,32,33,39],i,{6:3,4:59}),t(n,[2,12]),t(n,[2,13]),t(n,[2,14]),t(n,[2,15]),t(m,[2,39],{16:60}),t(h,[2,36]),t([11,12,15,40,41,45,49,50,52],[2,21],{43:61,44:[1,62],46:[1,63],47:[1,64]}),{12:[1,65],15:[2,26]},t(g,[2,27]),t(g,[2,28]),{22:[1,66]},{24:67,25:[2,43],38:68,39:[1,69]},{17:70,18:[2,41],34:72,35:[1,74],36:71,37:[1,73]},t(f,[2,46]),{11:a,12:o,14:75,29:28,40:r,41:s,47:l,50:c,52:d,53:22,54:23},{41:[1,76]},{11:a,12:o,14:78,29:28,40:r,41:s,47:l,48:77,49:[2,47],50:c,52:d,53:22,54:23},{20:79,41:u},t(n,[2,10]),{25:[1,80]},{25:[2,44]},t([9,11,12,13,19,21,23,25,26,28,30,31,32,33],i,{6:3,4:81}),{18:[1,82]},t(m,[2,40]),{18:[2,42]},{11:a,12:o,14:83,29:28,40:r,41:s,47:l,50:c,52:d,53:22,54:23},t([9,11,12,13,18,19,21,23,26,28,30,31,32,33],i,{6:3,4:84}),{45:[1,85]},t(f,[2,23]),{49:[1,86]},{49:[2,48]},{15:[2,25]},t(n,[2,11]),{25:[2,20]},t(n,[2,8]),{15:[1,87]},{18:[2,18]},t(f,[2,22]),t(f,[2,24]),t(p,i,{6:3,4:88}),t(m,[2,19])],defaultActions:{4:[2,1],68:[2,44],72:[2,42],78:[2,48],79:[2,25],81:[2,20],84:[2,18]},parseError:function(e,t){function i(e,t){this.message=e,this.hash=t}if(!t.recoverable)throw i.prototype=Error,new i(e,t);this.trace(e)},parse:function(e){var t=this,i=[0],n=[null],a=[],o=this.table,r="",s=0,l=0,c=0,d=a.slice.call(arguments,1),u=Object.create(this.lexer),h={yy:{}};for(var p in this.yy)Object.prototype.hasOwnProperty.call(this.yy,p)&&(h.yy[p]=this.yy[p]);u.setInput(e,h.yy),h.yy.lexer=u,h.yy.parser=this,void 0===u.yylloc&&(u.yylloc={});var f=u.yylloc;a.push(f);var m=u.options&&u.options.ranges;"function"==typeof h.yy.parseError?this.parseError=h.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var g,v,_,b,w,y,C,E,L,S=function(){var e;return e=u.lex()||1,"number"!=typeof e&&(e=t.symbols_[e]||e),e},D={};;){if(_=i[i.length-1],this.defaultActions[_]?b=this.defaultActions[_]:(null!==g&&void 0!==g||(g=S()),b=o[_]&&o[_][g]),void 0===b||!b.length||!b[0]){var x="";L=[];for(y in o[_])this.terminals_[y]&&y>2&&L.push("'"+this.terminals_[y]+"'");x=u.showPosition?"Parse error on line "+(s+1)+":\n"+u.showPosition()+"\nExpecting "+L.join(", ")+", got '"+(this.terminals_[g]||g)+"'":"Parse error on line "+(s+1)+": Unexpected "+(1==g?"end of input":"'"+(this.terminals_[g]||g)+"'"),this.parseError(x,{text:u.match,token:this.terminals_[g]||g,line:u.yylineno,loc:f,expected:L})}if(b[0]instanceof Array&&b.length>1)throw new Error("Parse Error: multiple actions possible at state: "+_+", token: "+g);switch(b[0]){case 1:i.push(g),n.push(u.yytext),a.push(u.yylloc),i.push(b[1]),g=null,v?(g=v,v=null):(l=u.yyleng,r=u.yytext,s=u.yylineno,f=u.yylloc,c>0&&c--);break;case 2:if(C=this.productions_[b[1]][1],D.$=n[n.length-C],D._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(D._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(w=this.performAction.apply(D,[r,l,s,h.yy,b[1],n,a].concat(d))))return w;C&&(i=i.slice(0,-1*C*2),n=n.slice(0,-1*C),a=a.slice(0,-1*C)),i.push(this.productions_[b[1]][0]),n.push(D.$),a.push(D._$),E=o[i[i.length-2]][i[i.length-1]],i.push(E);break;case 3:return!0}}return!0}},_=function(){return{EOF:1,parseError:function(e,t){if(!this.yy.parser)throw new Error(e);this.yy.parser.parseError(e,t)},setInput:function(e,t){return this.yy=t||this.yy||{},this._input=e,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var e=this._input[0];return this.yytext+=e,this.yyleng++,this.offset++,this.match+=e,this.matched+=e,e.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),e},unput:function(e){var t=e.length,i=e.split(/(?:\r\n?|\n)/g);this._input=e+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-t),this.offset-=t;var n=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),i.length-1&&(this.yylineno-=i.length-1);var a=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:i?(i.length===n.length?this.yylloc.first_column:0)+n[n.length-i.length].length-i[0].length:this.yylloc.first_column-t},this.options.ranges&&(this.yylloc.range=[a[0],a[0]+this.yyleng-t]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(e){this.unput(this.match.slice(e))},pastInput:function(){var e=this.matched.substr(0,this.matched.length-this.match.length);return(e.length>20?"...":"")+e.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var e=this.match;return e.length<20&&(e+=this._input.substr(0,20-e.length)),(e.substr(0,20)+(e.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var e=this.pastInput(),t=new Array(e.length+1).join("-");return e+this.upcomingInput()+"\n"+t+"^"},test_match:function(e,t){var i,n,a;if(this.options.backtrack_lexer&&(a={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(a.yylloc.range=this.yylloc.range.slice(0))),n=e[0].match(/(?:\r\n?|\n).*/g),n&&(this.yylineno+=n.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:n?n[n.length-1].length-n[n.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],i=this.performAction.call(this,this.yy,this,t,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),i)return i;if(this._backtrack){for(var o in a)this[o]=a[o];return!1}return!1},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var e,t,i,n;this._more||(this.yytext="",this.match="");for(var a=this._currentRules(),o=0;o<a.length;o++)if((i=this._input.match(this.rules[a[o]]))&&(!t||i[0].length>t[0].length)){if(t=i,n=o,this.options.backtrack_lexer){if(!1!==(e=this.test_match(i,a[o])))return e;if(this._backtrack){t=!1;continue}return!1}if(!this.options.flex)break}return t?!1!==(e=this.test_match(t,a[n]))&&e:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var e=this.next();return e||this.lex()},begin:function(e){this.conditionStack.push(e)},popState:function(){
-return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(e){return e=this.conditionStack.length-1-Math.abs(e||0),e>=0?this.conditionStack[e]:"INITIAL"},pushState:function(e){this.begin(e)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(e,t,i,n){switch(i){case 0:break;case 1:return t.yytext=t.yytext.substring(9,t.yytext.length-10),9;case 2:case 3:return 52;case 4:return 40;case 5:return 41;case 6:return 46;case 7:return 44;case 8:return 45;case 9:return 47;case 10:return 49;case 11:return 50;case 12:return 32;case 13:return 33;case 14:return this.begin("command"),30;case 15:return this.begin("command"),31;case 16:return this.begin("command"),13;case 17:case 18:return this.begin("command"),37;case 19:return 35;case 20:return 18;case 21:return 26;case 22:return 27;case 23:return this.begin("command"),19;case 24:return this.begin("command"),21;case 25:return 22;case 26:return this.begin("command"),23;case 27:return 39;case 28:return 25;case 29:return this.begin("command"),28;case 30:return this.popState(),15;case 31:return 12;case 32:return 5;case 33:return 11}},rules:[/^(?:\{\*[\s\S]*?\*\})/,/^(?:\{literal\}[\s\S]*?\{\/literal\})/,/^(?:"([^"]|\\\.)*")/,/^(?:'([^']|\\\.)*')/,/^(?:\$)/,/^(?:[_a-zA-Z][_a-zA-Z0-9]*)/,/^(?:\.)/,/^(?:\[)/,/^(?:\])/,/^(?:\()/,/^(?:\))/,/^(?:=)/,/^(?:\{ldelim\})/,/^(?:\{rdelim\})/,/^(?:\{#)/,/^(?:\{@)/,/^(?:\{if )/,/^(?:\{else if )/,/^(?:\{elseif )/,/^(?:\{else\})/,/^(?:\{\/if\})/,/^(?:\{lang\})/,/^(?:\{\/lang\})/,/^(?:\{include )/,/^(?:\{implode )/,/^(?:\{\/implode\})/,/^(?:\{foreach )/,/^(?:\{foreachelse\})/,/^(?:\{\/foreach\})/,/^(?:\{(?!\s))/,/^(?:\})/,/^(?:\s+)/,/^(?:$)/,/^(?:[^{])/],conditions:{command:{rules:[0,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],inclusive:!0},INITIAL:{rules:[0,1,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33],inclusive:!0}}}}();return v.lexer=_,v}),define("WoltLabSuite/Core/NumberUtil",[],function(){"use strict";return{round:function(e,t){return void 0===t||0==+t?Math.round(e):(e=+e,t=+t,isNaN(e)||"number"!=typeof t||t%1!=0?NaN:(e=e.toString().split("e"),e=Math.round(+(e[0]+"e"+(e[1]?+e[1]-t:-t))),e=e.toString().split("e"),+(e[0]+"e"+(e[1]?+e[1]+t:t))))}}}),define("WoltLabSuite/Core/StringUtil",["Language","./NumberUtil"],function(e,t){"use strict";return{addThousandsSeparator:function(t){return void 0===e&&(e=require("Language")),String(t).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g,"$1"+e.get("wcf.global.thousandsSeparator"))},escapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")},escapeRegExp:function(e){return String(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},formatNumeric:function(i,n){void 0===e&&(e=require("Language")),i=String(t.round(i,n||-2));var a=i.split(".");return i=this.addThousandsSeparator(a[0]),a.length>1&&(i+=e.get("wcf.global.decimalPoint")+a[1]),i=i.replace("-","−")},lcfirst:function(e){return String(e).substring(0,1).toLowerCase()+e.substring(1)},ucfirst:function(e){return String(e).substring(0,1).toUpperCase()+e.substring(1)},unescapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">")},shortUnit:function(e){var i="";return e>=1e6?(e/=1e6,e=e>10?Math.floor(e):t.round(e,-1),i="M"):e>=1e3&&(e/=1e3,e=e>10?Math.floor(e):t.round(e,-1),i="k"),this.formatNumeric(e)+i}}}),define("WoltLabSuite/Core/Template",["./Template.grammar","./StringUtil","Language"],function(e,t,i){"use strict";function n(){this.yy={}}function a(n){void 0===i&&(i=require("Language")),void 0===t&&(t=require("StringUtil"));try{n=e.parse(n),n="var tmp = {};\nfor (var key in v) tmp[key] = v[key];\nv = tmp;\nv.__wcf = window.WCF; v.__window = window;\nreturn "+n,this.fetch=new Function("StringUtil","Language","v",n).bind(void 0,t,i)}catch(e){throw console.debug(e.message),e}}return n.prototype=e,e.Parser=n,e=new n,Object.defineProperty(a,"callbacks",{enumerable:!1,configurable:!1,get:function(){throw new Error("WCF.Template.callbacks is no longer supported")},set:function(e){throw new Error("WCF.Template.callbacks is no longer supported")}}),a.prototype={fetch:function(e){throw new Error("This Template is not initialized.")}},a}),define("WoltLabSuite/Core/Language",["Dictionary","./Template"],function(e,t){"use strict";var i=new e;return{addObject:function(t){i.merge(e.fromObject(t))},add:function(e,t){i.set(e,t)},get:function(e,n){n||(n={});var a=i.get(e);if(void 0===a)return e;if(void 0===t&&(t=require("WoltLabSuite/Core/Template")),"string"==typeof a){try{i.set(e,new t(a))}catch(n){i.set(e,new t("{literal}"+a.replace(/\{\/literal\}/g,"{/literal}{ldelim}/literal}{literal}")+"{/literal}"))}a=i.get(e)}return a instanceof t&&(a=a.fetch(n)),a}}}),define("WoltLabSuite/Core/CallbackList",["Dictionary"],function(e){"use strict";function t(){this._dictionary=new e}return t.prototype={add:function(e,t){if("function"!=typeof t)throw new TypeError("Expected a valid callback as second argument for identifier '"+e+"'.");this._dictionary.has(e)||this._dictionary.set(e,[]),this._dictionary.get(e).push(t)},remove:function(e){this._dictionary.delete(e)},forEach:function(e,t){if(null===e)this._dictionary.forEach(function(e,i){e.forEach(t)});else{var i=this._dictionary.get(e);void 0!==i&&i.forEach(t)}}},t}),define("WoltLabSuite/Core/Dom/Change/Listener",["CallbackList"],function(e){"use strict";var t=new e,i=!1;return{add:t.add.bind(t),remove:t.remove.bind(t),trigger:function(){if(!i)try{i=!0,t.forEach(null,function(e){e()})}finally{i=!1}}}}),define("WoltLabSuite/Core/Environment",[],function(){"use strict";var e="other",t="none",i="desktop",n=!1;return{setup:function(){if("object"==typeof window.chrome)e="chrome";else for(var a=window.getComputedStyle(document.documentElement),o=0,r=a.length;o<r;o++){var s=a[o];0===s.indexOf("-ms-")?e="microsoft":0===s.indexOf("-moz-")?e="firefox":"firefox"!==e&&0===s.indexOf("-webkit-")&&(e="safari")}var l=window.navigator.userAgent.toLowerCase();-1!==l.indexOf("crios")?(e="chrome",i="ios"):/(?:iphone|ipad|ipod)/.test(l)?(e="safari",i="ios"):-1!==l.indexOf("android")?i="android":-1!==l.indexOf("iemobile")&&(e="microsoft",i="windows"),"desktop"!==i||-1===l.indexOf("mobile")&&-1===l.indexOf("tablet")||(i="mobile"),t="redactor",n=!!("ontouchstart"in window)||!!("msMaxTouchPoints"in window.navigator)&&window.navigator.msMaxTouchPoints>0||window.DocumentTouch&&document instanceof DocumentTouch},browser:function(){return e},editor:function(){return t},platform:function(){return i},touch:function(){return n}}}),define("WoltLabSuite/Core/Dom/Util",["Environment","StringUtil"],function(e,t){"use strict";function i(e,t,i){if(!t.contains(e))throw new Error("Ancestor element does not contain target element.");for(var n,a=i+"Sibling";null!==e&&e!==t;){if(null!==e[i+"ElementSibling"])return!1;if(e[a])for(n=e[a];n;){if(""!==n.textContent.trim())return!1;n=n[a]}e=e.parentNode}return!0}var n=0,a={createFragmentFromHtml:function(e){var t=elCreate("div");this.setInnerHtml(t,e);for(var i=document.createDocumentFragment();t.childNodes.length;)i.appendChild(t.childNodes[0]);return i},getUniqueId:function(){var e;do{e="wcf"+n++}while(null!==elById(e));return e},identify:function(e){if(!(e instanceof Element))throw new TypeError("Expected a valid DOM element as argument.");var t=elAttr(e,"id");return t||(t=this.getUniqueId(),elAttr(e,"id",t)),t},outerHeight:function(e,t){t=t||window.getComputedStyle(e);var i=e.offsetHeight;return i+=~~t.marginTop+~~t.marginBottom},outerWidth:function(e,t){t=t||window.getComputedStyle(e);var i=e.offsetWidth;return i+=~~t.marginLeft+~~t.marginRight},outerDimensions:function(e){var t=window.getComputedStyle(e);return{height:this.outerHeight(e,t),width:this.outerWidth(e,t)}},offset:function(e){var t=e.getBoundingClientRect();return{top:Math.round(t.top+(window.scrollY||window.pageYOffset)),left:Math.round(t.left+(window.scrollX||window.pageXOffset))}},prepend:function(e,t){0===t.childNodes.length?t.appendChild(e):t.insertBefore(e,t.childNodes[0])},insertAfter:function(e,t){null!==t.nextSibling?t.parentNode.insertBefore(e,t.nextSibling):t.parentNode.appendChild(e)},setStyles:function(e,t){var i=!1;for(var n in t)t.hasOwnProperty(n)&&(/ !important$/.test(t[n])?(i=!0,t[n]=t[n].replace(/ !important$/,"")):i=!1,"important"!==e.style.getPropertyPriority(n)||i||e.style.removeProperty(n),e.style.setProperty(n,t[n],i?"important":""))},styleAsInt:function(e,t){var i=e.getPropertyValue(t);return null===i?0:parseInt(i)},setInnerHtml:function(e,t){e.innerHTML=t;for(var i,n,a=elBySelAll("script",e),o=0,r=a.length;o<r;o++)n=a[o],i=elCreate("script"),n.src?i.src=n.src:i.textContent=n.textContent,e.appendChild(i),elRemove(n)},insertHtml:function(e,t,i){var n=elCreate("div");if(this.setInnerHtml(n,e),n.childNodes.length){var a=n.childNodes[0];switch(i){case"append":t.appendChild(a);break;case"after":this.insertAfter(a,t);break;case"prepend":this.prepend(a,t);break;case"before":t.parentNode.insertBefore(a,t);break;default:throw new Error("Unknown insert method '"+i+"'.")}for(var o;n.childNodes.length;)o=n.childNodes[0],this.insertAfter(o,a),a=o}},contains:function(e,t){for(;null!==t;)if(t=t.parentNode,e===t)return!0;return!1},getDataAttributes:function(e,i,n,a){i=i||"",/^data-/.test(i)||(i="data-"+i),n=!0===n,a=!0===a;for(var o,r,s,l={},c=0,d=e.attributes.length;c<d;c++)if(o=e.attributes[c],0===o.name.indexOf(i)){if(r=o.name.replace(new RegExp("^"+i),""),n){s=r.split("-"),r="";for(var u=0,h=s.length;u<h;u++)r.length&&(a&&"id"===s[u]?s[u]="ID":s[u]=t.ucfirst(s[u])),r+=s[u]}l[r]=o.value}return l},unwrapChildNodes:function(e){for(var t=e.parentNode;e.childNodes.length;)t.insertBefore(e.childNodes[0],e);elRemove(e)},replaceElement:function(e,t){for(;e.childNodes.length;)t.appendChild(e.childNodes[0]);e.parentNode.insertBefore(t,e),elRemove(e)},isAtNodeStart:function(e,t){return i(e,t,"previous")},isAtNodeEnd:function(e,t){return i(e,t,"next")},getFixedParent:function(e){for(;e&&e!==document.body;){if("fixed"===window.getComputedStyle(e).getPropertyValue("position"))return e;e=e.offsetParent}return null}};return window.bc_wcfDomUtil=a,a}),function(e,t,i){var n=window.matchMedia;"undefined"!=typeof module&&module.exports?module.exports=i(n):"function"==typeof define&&define.amd?define("enquire",[],function(){return t.enquire=i(n)}):t.enquire=i(n)}(0,this,function(e){"use strict";function t(e,t){var i=0,n=e.length;for(i;i<n&&!1!==t(e[i],i);i++);}function i(e){return"[object Array]"===Object.prototype.toString.apply(e)}function n(e){return"function"==typeof e}function a(e){this.options=e,!e.deferSetup&&this.setup()}function o(t,i){this.query=t,this.isUnconditional=i,this.handlers=[],this.mql=e(t);var n=this;this.listener=function(e){n.mql=e,n.assess()},this.mql.addListener(this.listener)}function r(){if(!e)throw new Error("matchMedia not present, legacy browsers require a polyfill");this.queries={},this.browserIsIncapable=!e("only all").matches}return a.prototype={setup:function(){this.options.setup&&this.options.setup(),this.initialised=!0},on:function(){!this.initialised&&this.setup(),this.options.match&&this.options.match()},off:function(){this.options.unmatch&&this.options.unmatch()},destroy:function(){this.options.destroy?this.options.destroy():this.off()},equals:function(e){return this.options===e||this.options.match===e}},o.prototype={addHandler:function(e){var t=new a(e);this.handlers.push(t),this.matches()&&t.on()},removeHandler:function(e){var i=this.handlers;t(i,function(t,n){if(t.equals(e))return t.destroy(),!i.splice(n,1)})},matches:function(){return this.mql.matches||this.isUnconditional},clear:function(){t(this.handlers,function(e){e.destroy()}),this.mql.removeListener(this.listener),this.handlers.length=0},assess:function(){var e=this.matches()?"on":"off";t(this.handlers,function(t){t[e]()})}},r.prototype={register:function(e,a,r){var s=this.queries,l=r&&this.browserIsIncapable;return s[e]||(s[e]=new o(e,l)),n(a)&&(a={match:a}),i(a)||(a=[a]),t(a,function(t){n(t)&&(t={match:t}),s[e].addHandler(t)}),this},unregister:function(e,t){var i=this.queries[e];return i&&(t?i.removeHandler(t):(i.clear(),delete this.queries[e])),this}},new r}),define("WoltLabSuite/Core/ObjectMap",[],function(){"use strict";function e(){this._map=t?new WeakMap:{key:[],value:[]}}var t=objOwns(window,"WeakMap")&&"function"==typeof window.WeakMap;return e.prototype={set:function(e,i){if("object"!=typeof e||null===e)throw new TypeError("Only objects can be used as key");if("object"!=typeof i||null===i)throw new TypeError("Only objects can be used as value");t?this._map.set(e,i):(this._map.key.push(e),this._map.value.push(i))},delete:function(e){if(t)this._map.delete(e);else{var i=this._map.key.indexOf(e);this._map.key.splice(i),this._map.value.splice(i)}},has:function(e){return t?this._map.has(e):-1!==this._map.key.indexOf(e)},get:function(e){if(t)return this._map.get(e);var i=this._map.key.indexOf(e);return-1!==i?this._map.value[i]:void 0}},e}),define("WoltLabSuite/Core/Dom/Traverse",[],function(){"use strict";var e=[function(e,t){return!0},function(e,t){return e.matches(t)},function(e,t){return e.classList.contains(t)},function(e,t){return e.nodeName===t}],t=function(t,i,n){if(!(t instanceof Element))throw new TypeError("Expected a valid element as first argument.");for(var a=[],o=0;o<t.childElementCount;o++)e[i](t.children[o],n)&&a.push(t.children[o]);return a},i=function(t,i,n,a){if(!(t instanceof Element))throw new TypeError("Expected a valid element as first argument.");for(t=t.parentNode;t instanceof Element;){if(t===a)return null;if(e[i](t,n))return t;t=t.parentNode}return null},n=function(t,i,n,a){if(!(t instanceof Element))throw new TypeError("Expected a valid element as first argument.");return t instanceof Element&&null!==t[i]&&e[n](t[i],a)?t[i]:null};return{childBySel:function(e,i){return t(e,1,i)[0]||null},childByClass:function(e,i){return t(e,2,i)[0]||null},childByTag:function(e,i){return t(e,3,i)[0]||null},childrenBySel:function(e,i){return t(e,1,i)},childrenByClass:function(e,i){return t(e,2,i)},childrenByTag:function(e,i){return t(e,3,i)},parentBySel:function(e,t,n){return i(e,1,t,n)},parentByClass:function(e,t,n){return i(e,2,t,n)},parentByTag:function(e,t,n){return i(e,3,t,n)},next:function(e){return n(e,"nextElementSibling",0,null)},nextBySel:function(e,t){return n(e,"nextElementSibling",1,t)},nextByClass:function(e,t){return n(e,"nextElementSibling",2,t)},nextByTag:function(e,t){return n(e,"nextElementSibling",3,t)},prev:function(e){return n(e,"previousElementSibling",0,null)},prevBySel:function(e,t){return n(e,"previousElementSibling",1,t)},prevByClass:function(e,t){return n(e,"previousElementSibling",2,t)},prevByTag:function(e,t){return n(e,"previousElementSibling",3,t)}}}),define("WoltLabSuite/Core/Ui/Confirmation",["Core","Language","Ui/Dialog"],function(e,t,i){"use strict";var n=!1,a=null,o=null,r={},s=null;return{show:function(t){if(void 0===i&&(i=require("Ui/Dialog")),!n){if(r=e.extend({cancel:null,confirm:null,legacyCallback:null,message:"",messageIsHtml:!1,parameters:{},template:""},t),r.message="string"==typeof r.message?r.message.trim():"",!r.message.length)throw new Error("Expected a non-empty string for option 'message'.");if("function"!=typeof r.confirm&&"function"!=typeof r.legacyCallback)throw new TypeError("Expected a valid callback for option 'confirm'.");null===o&&this._createDialog(),o.innerHTML="string"==typeof r.template?r.template.trim():"",r.messageIsHtml?s.innerHTML=r.message:s.textContent=r.message,n=!0,i.open(this)}},_dialogSetup:function(){return{id:"wcfSystemConfirmation",options:{onClose:this._onClose.bind(this),onShow:this._onShow.bind(this),title:t.get("wcf.global.confirmation.title")}}},getContentElement:function(){return o},_createDialog:function(){var e=elCreate("div");elAttr(e,"id","wcfSystemConfirmation"),e.classList.add("systemConfirmation"),s=elCreate("p"),e.appendChild(s),o=elCreate("div"),elAttr(o,"id","wcfSystemConfirmationContent"),e.appendChild(o);var n=elCreate("div");n.classList.add("formSubmit"),e.appendChild(n),a=elCreate("button"),a.classList.add("buttonPrimary"),a.textContent=t.get("wcf.global.confirmation.confirm"),a.addEventListener(WCF_CLICK_EVENT,this._confirm.bind(this)),n.appendChild(a);var r=elCreate("button");r.textContent=t.get("wcf.global.confirmation.cancel"),r.addEventListener(WCF_CLICK_EVENT,function(){i.close("wcfSystemConfirmation")}),n.appendChild(r),document.body.appendChild(e)},_confirm:function(){"function"==typeof r.legacyCallback?r.legacyCallback("confirm",r.parameters,o):r.confirm(r.parameters,o),n=!1,i.close("wcfSystemConfirmation")},_onClose:function(){n&&(a.blur(),n=!1,"function"==typeof r.legacyCallback?r.legacyCallback("cancel",r.parameters,o):"function"==typeof r.cancel&&r.cancel(r.parameters))},_onShow:function(){a.blur(),a.focus()}}}),define("WoltLabSuite/Core/Ui/Screen",["Core","Dictionary","Environment"],function(e,t,i){"use strict";var n=null,a=new t,o=0,r=null,s=0,l=t.fromObject({"screen-xs":"(max-width: 544px)","screen-sm":"(min-width: 545px) and (max-width: 768px)","screen-sm-down":"(max-width: 768px)","screen-sm-up":"(min-width: 545px)","screen-sm-md":"(min-width: 545px) and (max-width: 1024px)","screen-md":"(min-width: 769px) and (max-width: 1024px)","screen-md-down":"(max-width: 1024px)","screen-md-up":"(min-width: 769px)","screen-lg":"(min-width: 1025px)"}),c=new t;return{on:function(t,i){var n=e.getUuid(),a=this._getQueryObject(t);return"function"==typeof i.match&&a.callbacksMatch.set(n,i.match),"function"==typeof i.unmatch&&a.callbacksUnmatch.set(n,i.unmatch),"function"==typeof i.setup&&(a.mql.matches?i.setup():a.callbacksSetup.set(n,i.setup)),n},remove:function(e,t){var i=this._getQueryObject(e);i.callbacksMatch.delete(t),i.callbacksUnmatch.delete(t),i.callbacksSetup.delete(t)},is:function(e){return this._getQueryObject(e).mql.matches},scrollDisable:function(){if(0===o){s=document.body.scrollTop,r="body",s||(s=document.documentElement.scrollTop,r="documentElement");var e=elById("pageContainer");"ios"===i.platform()?(e.style.setProperty("position","relative",""),e.style.setProperty("top","-"+s+"px","")):e.style.setProperty("margin-top","-"+s+"px",""),document.documentElement.classList.add("disableScrolling")}o++},scrollEnable:function(){if(o&&0===--o){document.documentElement.classList.remove("disableScrolling");var e=elById("pageContainer");"ios"===i.platform()?(e.style.removeProperty("position"),e.style.removeProperty("top")):e.style.removeProperty("margin-top"),s&&(document[r].scrollTop=~~s)}},setDialogContainer:function(e){n=e},_getQueryObject:function(e){if("string"!=typeof e||""===e.trim())throw new TypeError("Expected a non-empty string for parameter 'query'.");c.has(e)&&(e=c.get(e)),l.has(e)&&(e=l.get(e));var i=a.get(e);return i||(i={callbacksMatch:new t,callbacksUnmatch:new t,callbacksSetup:new t,mql:window.matchMedia(e)},i.mql.addListener(this._mqlChange.bind(this)),a.set(e,i),e!==i.mql.media&&c.set(i.mql.media,e)),i},_mqlChange:function(e){var i=this._getQueryObject(e.media);e.matches?i.callbacksSetup.size?(i.callbacksSetup.forEach(function(e){e()}),i.callbacksSetup=new t):i.callbacksMatch.forEach(function(e){e()}):i.callbacksUnmatch.forEach(function(e){e()})}}}),define("WoltLabSuite/Core/Ui/Alignment",["Core","Language","Dom/Traverse","Dom/Util"],function(e,t,i,n){"use strict";return{set:function(a,o,r){r=e.extend({verticalOffset:0,pointer:!1,pointerOffset:4,pointerClassNames:[],refDimensionsElement:null,horizontal:"left",vertical:"bottom",allowFlip:"both"},r),Array.isArray(r.pointerClassNames)&&r.pointerClassNames.length===(r.pointer?1:2)||(r.pointerClassNames=[]),-1===["left","right","center"].indexOf(r.horizontal)&&(r.horizontal="left"),"bottom"!==r.vertical&&(r.vertical="top"),-1===["both","horizontal","vertical","none"].indexOf(r.allowFlip)&&(r.allowFlip="both"),n.setStyles(a,{bottom:"auto !important",left:"0 !important",right:"auto !important",top:"0 !important",visibility:"hidden !important"});var s=n.outerDimensions(a),l=n.outerDimensions(r.refDimensionsElement instanceof Element?r.refDimensionsElement:o),c=n.offset(o),d=window.innerHeight,u=document.body.clientWidth,h={result:null},p=!1;if("center"===r.horizontal&&(p=!0,h=this._tryAlignmentHorizontal(r.horizontal,s,l,c,u),h.result||("both"===r.allowFlip||"horizontal"===r.allowFlip?r.horizontal="left":h.result=!0)),"rtl"===t.get("wcf.global.pageDirection")&&(r.horizontal="left"===r.horizontal?"right":"left"),!h.result){var f=h;if(h=this._tryAlignmentHorizontal(r.horizontal,s,l,c,u),!h.result&&("both"===r.allowFlip||"horizontal"===r.allowFlip)){var m=this._tryAlignmentHorizontal("left"===r.horizontal?"right":"left",s,l,c,u);m.result?h=m:p&&(h=f)}}var g=h.left,v=h.right,_=this._tryAlignmentVertical(r.vertical,s,l,c,d,r.verticalOffset);if(!_.result&&("both"===r.allowFlip||"vertical"===r.allowFlip)){var b=this._tryAlignmentVertical("top"===r.vertical?"bottom":"top",s,l,c,d,r.verticalOffset);b.result&&(_=b)}var w=_.bottom,y=_.top;if(r.pointer){var C=i.childrenByClass(a,"elementPointer");if(null===(C=C[0]||null))throw new Error("Expected the .elementPointer element to be a direct children.");"center"===h.align?(C.classList.add("center"),C.classList.remove("left"),C.classList.remove("right")):(C.classList.add(h.align),C.classList.remove("center"),C.classList.remove("left"===h.align?"right":"left")),"top"===_.align?C.classList.add("flipVertical"):C.classList.remove("flipVertical")}else if(2===r.pointerClassNames.length){a.classList["auto"===y?"add":"remove"](r.pointerClassNames[0]),a.classList["auto"===g?"add":"remove"](r.pointerClassNames[1])}"auto"!==w&&(w=Math.round(w)+"px"),"auto"!==g&&(g=Math.ceil(g)+"px"),"auto"!==v&&(v=Math.floor(v)+"px"),"auto"!==y&&(y=Math.round(y)+"px"),n.setStyles(a,{bottom:w,left:g,right:v,top:y}),elShow(a),a.style.removeProperty("visibility")},_tryAlignmentHorizontal:function(e,t,i,n,a){var o="auto",r="auto",s=!0;return"left"===e?(o=n.left)+t.width>a&&(s=!1):"right"===e?n.left+i.width<t.width?s=!1:(r=a-(n.left+i.width))<0&&(s=!1):(o=n.left+i.width/2-t.width/2,((o=~~o)<0||o+t.width>a)&&(s=!1)),{align:e,left:o,right:r,result:s}},_tryAlignmentVertical:function(e,t,i,n,a,o){var r="auto",s="auto",l=!0;if("top"===e){var c=document.body.clientHeight;r=c-n.top+o,c-(r+t.height)<(window.scrollY||window.pageYOffset)&&(l=!1)}else(s=n.top+i.height+o)+t.height-(window.scrollY||window.pageYOffset)>a&&(l=!1);return{align:e,bottom:r,top:s,result:l}}}}),define("WoltLabSuite/Core/Ui/CloseOverlay",["CallbackList"],function(e){"use strict";var t=new e,i={setup:function(){document.body.addEventListener(WCF_CLICK_EVENT,this.execute.bind(this))},add:t.add.bind(t),remove:t.remove.bind(t),execute:function(){t.forEach(null,function(e){e()})}};return i.setup(),i}),define("WoltLabSuite/Core/Ui/Dropdown/Simple",["CallbackList","Core","Dictionary","Ui/Alignment","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/CloseOverlay"],function(e,t,i,n,a,o,r,s){"use strict";var l=null,c=new e,d=!1,u=new i,h=new i,p=null;return{setup:function(){d||(d=!0,p=elCreate("div"),p.className="dropdownMenuContainer",document.body.appendChild(p),l=elByClass("dropdownToggle"),this.initAll(),s.add("WoltLabSuite/Core/Ui/Dropdown/Simple",this.closeAll.bind(this)),a.add("WoltLabSuite/Core/Ui/Dropdown/Simple",this.initAll.bind(this)),document.addEventListener("scroll",this._onScroll.bind(this)),window.bc_wcfSimpleDropdown=this)},initAll:function(){for(var e=0,t=l.length;e<t;e++)this.init(l[e],!1)},init:function(e,i){if(this.setup(),e.classList.contains("jsDropdownEnabled")||elData(e,"target"))return!1;var n=o.parentByClass(e,"dropdown");if(null===n)throw new Error("Invalid dropdown passed, button '"+r.identify(e)+"' does not have a parent with .dropdown.");var a=o.nextByClass(e,"dropdownMenu");if(null===a)throw new Error("Invalid dropdown passed, button '"+r.identify(e)+"' does not have a menu as next sibling.");p.appendChild(a);var s=r.identify(n);if(!u.has(s)&&(e.classList.add("jsDropdownEnabled"),e.addEventListener(WCF_CLICK_EVENT,this._toggle.bind(this)),u.set(s,n),h.set(s,a),s.match(/^wcf\d+$/)||elData(a,"source",s),a.childElementCount&&a.children[0].classList.contains("scrollableDropdownMenu"))){a=a.children[0],elData(a,"scroll-to-active",!0);var l=null,c=null;a.addEventListener("wheel",function(e){null===l&&(l=a.clientHeight),null===c&&(c=a.scrollHeight),e.deltaY<0&&0===a.scrollTop?e.preventDefault():e.deltaY>0&&a.scrollTop+l===c&&e.preventDefault()},{passive:!1})}elData(e,"target",s),i&&setTimeout(function(){t.triggerEvent(e,WCF_CLICK_EVENT)},10)},initFragment:function(e,t){this.setup();var i=r.identify(e);u.has(i)||(u.set(i,e),p.appendChild(t),h.set(i,t))},registerCallback:function(e,t){c.add(e,t)},getDropdown:function(e){return u.get(e)},getDropdownMenu:function(e){return h.get(e)},toggleDropdown:function(e,t){this._toggle(null,e,t)},setAlignment:function(e,t,i){var a,o=elBySel(".dropdownToggle",e);null!==o&&o.parentNode.classList.contains("inputAddonTextarea")&&(a=o),n.set(t,i||e,{pointerClassNames:["dropdownArrowBottom","dropdownArrowRight"],refDimensionsElement:a||null,horizontal:"right"===elData(t,"dropdown-alignment-horizontal")?"right":"left",vertical:"top"===elData(t,"dropdown-alignment-vertical")?"top":"bottom"})},setAlignmentById:function(e){var t=u.get(e);if(void 0===t)throw new Error("Unknown dropdown identifier '"+e+"'.");var i=h.get(e);this.setAlignment(t,i)},isOpen:function(e){var t=h.get(e);return void 0!==t&&t.classList.contains("dropdownOpen")},open:function(e){var t=h.get(e);void 0===t||t.classList.contains("dropdownOpen")||this.toggleDropdown(e)},close:function(e){var t=u.get(e);void 0!==t&&(t.classList.remove("dropdownOpen"),h.get(e).classList.remove("dropdownOpen"))},closeAll:function(){u.forEach(function(e,t){e.classList.contains("dropdownOpen")&&(e.classList.remove("dropdownOpen"),h.get(t).classList.remove("dropdownOpen"),this._notifyCallbacks(t,"close"))}.bind(this))},destroy:function(e){if(!u.has(e))return!1;try{this.close(e),elRemove(h.get(e))}catch(e){}return h.delete(e),u.delete(e),!0},_onDialogScroll:function(e){for(var t=e.currentTarget,i=elBySelAll(".dropdown.dropdownOpen",t),n=0,a=i.length;n<a;n++){var o=i[n],s=r.identify(o),l=r.offset(o),c=r.offset(t);l.top+o.clientHeight<=c.top?this.toggleDropdown(s):l.top>=c.top+t.offsetHeight?this.toggleDropdown(s):l.left<=c.left?this.toggleDropdown(s):l.left>=c.left+t.offsetWidth?this.toggleDropdown(s):this.setAlignment(s,h.get(s))}},_onScroll:function(){u.forEach(function(e,t){e.classList.contains("dropdownOpen")&&(elDataBool(e,"is-overlay-dropdown-button")?this.setAlignment(e,h.get(t)):this.close(t))}.bind(this))},_notifyCallbacks:function(e,t){c.forEach(e,function(i){i(e,t)})},_toggle:function(e,t,i){null!==e&&(e.preventDefault(),e.stopPropagation(),t=elData(e.currentTarget,"target"));var n=u.get(t),a=!1;if(void 0!==n){if(e){var r=e.currentTarget,s=r.parentNode;s!==n&&(s.classList.add("dropdown"),s.id=n.id,n.classList.remove("dropdown"),n.id="",n=s,u.set(t,s))}if(elDataBool(n,"dropdown-prevent-toggle")&&n.classList.contains("dropdownOpen")&&(a=!0),null===elData(n,"is-overlay-dropdown-button")){var l=o.parentByClass(n,"dialogContent");elData(n,"is-overlay-dropdown-button",null!==l),null!==l&&l.addEventListener("scroll",this._onDialogScroll.bind(this))}}return u.forEach(function(e,n){var o=h.get(n);if(e.classList.contains("dropdownOpen"))!1===a&&(e.classList.remove("dropdownOpen"),o.classList.remove("dropdownOpen"),this._notifyCallbacks(n,"close"));else if(n===t&&o.childElementCount>0){if(e.classList.add("dropdownOpen"),o.classList.add("dropdownOpen"),o.childElementCount&&elDataBool(o.children[0],"scroll-to-active")){var r=o.children[0];r.removeAttribute("data-scroll-to-active");for(var s=null,l=0,c=r.childElementCount;l<c;l++)if(r.children[l].classList.contains("active")){s=r.children[l];break}s&&(r.scrollTop=Math.max(s.offsetTop+s.clientHeight-o.clientHeight,0))}var d=elBySel(".scrollableDropdownMenu",o);null!==d&&d.classList[d.scrollHeight>d.clientHeight?"add":"remove"]("forceScrollbar"),this._notifyCallbacks(n,"open"),this.setAlignment(e,o,i)}}.bind(this)),window.WCF.Dropdown.Interactive.Handler.closeAll(),null===e}}}),define("WoltLabSuite/Core/Devtools",[],function(){"use strict";var e={editorAutosave:!0,eventLogging:!1},t=function(){window.sessionStorage&&window.sessionStorage.setItem("__wsc_devtools_config",JSON.stringify(e))},i={help:function(){window.console.log(""),window.console.log("%cAvailable commands:","text-decoration: underline");var e=[];for(var t in i)"_internal_"!==t&&i.hasOwnProperty(t)&&e.push(t);e.sort().forEach(function(e){window.console.log("\tDevtools."+e+"()")}),window.console.log("")},toggleEditorAutosave:function(i){e.editorAutosave=!0!==i&&!e.editorAutosave,t(),window.console.log("%c\tEditor autosave "+(e.editorAutosave?"enabled":"disabled"),"font-style: italic")},toggleEventLogging:function(i){e.eventLogging=!0===i||!e.eventLogging,t(),window.console.log("%c\tEvent logging "+(e.eventLogging?"enabled":"disabled"),"font-style: italic")},_internal_:{enable:function(){if(window.Devtools=i,window.console.log("%cDevtools for WoltLab Suite loaded","font-weight: bold"),window.sessionStorage){var t=window.sessionStorage.getItem("__wsc_devtools_config");try{null!==t&&(e=JSON.parse(t))}catch(e){}e.editorAutosave||i.toggleEditorAutosave(!0),e.eventLogging&&i.toggleEventLogging(!0)}window.console.log("Settings are saved per browser session, enter `Devtools.help()` to learn more."),window.console.log("")},editorAutosave:function(){return e.editorAutosave},eventLog:function(t,i){e.eventLogging&&window.console.log("[Devtools.EventLogging] Firing event: "+i+" @ "+t)}}};return i}),define("WoltLabSuite/Core/Event/Handler",["Core","Devtools","Dictionary"],function(e,t,i){"use strict";var n=new i;return{add:function(t,a,o){if("function"!=typeof o)throw new TypeError("[WoltLabSuite/Core/Event/Handler] Expected a valid callback for '"+a+"@"+t+"'.");var r=n.get(t);void 0===r&&(r=new i,n.set(t,r));var s=r.get(a);void 0===s&&(s=new i,r.set(a,s));var l=e.getUuid();return s.set(l,o),l},fire:function(e,i,a){t._internal_.eventLog(e,i),a=a||{};var o=n.get(e);if(void 0!==o){var r=o.get(i);void 0!==r&&r.forEach(function(e){e(a)})}},remove:function(e,t,i){var a=n.get(e);if(void 0!==a){var o=a.get(t);void 0!==o&&o.delete(i)}},removeAll:function(e,t){"string"!=typeof t&&(t=void 0);var i=n.get(e);void 0!==i&&(void 0===t?n.delete(e):i.delete(t))},removeAllBySuffix:function(e,t){var i=n.get(e);if(void 0!==i){t="_"+t;var a=-1*t.length;i.forEach(function(i,n){n.substr(a)===t&&this.removeAll(e,n)}.bind(this))}}}}),define("WoltLabSuite/Core/List",[],function(){"use strict";function e(){this._set=t?new Set:[]}var t=objOwns(window,"Set")&&"function"==typeof window.Set;return e.prototype={add:function(e){t?this._set.add(e):this.has(e)||this._set.push(e)},clear:function(){t?this._set.clear():this._set=[]},delete:function(e){if(t)return this._set.delete(e);var i=this._set.indexOf(e);return-1!==i&&(this._set.splice(i,1),!0)},forEach:function(e){if(t)this._set.forEach(e);else for(var i=0,n=this._set.length;i<n;i++)e(this._set[i])},has:function(e){return t?this._set.has(e):-1!==this._set.indexOf(e)}},Object.defineProperty(e.prototype,"size",{enumerable:!1,configurable:!0,get:function(){return t?this._set.size:this._set.length}}),e}),define("WoltLabSuite/Core/Event/Key",[],function(){"use strict";function e(e,t,i){if(!(e instanceof Event))throw new TypeError("Expected a valid event when testing for key '"+t+"'.");return e.key===t||e.which===i}return{ArrowDown:function(t){return e(t,"ArrowDown",40)},ArrowLeft:function(t){
-return e(t,"ArrowLeft",37)},ArrowRight:function(t){return e(t,"ArrowRight",39)},ArrowUp:function(t){return e(t,"ArrowUp",38)},Comma:function(t){return e(t,",",44)},Enter:function(t){return e(t,"Enter",13)},Escape:function(t){return e(t,"Escape",27)},Tab:function(t){return e(t,"Tab",9)}}}),define("WoltLabSuite/Core/Ui/Dialog",["enquire","Ajax","Core","Dictionary","Environment","Language","ObjectMap","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Confirmation","Ui/Screen","Ui/SimpleDropdown","EventHandler","List","EventKey"],function(e,t,i,n,a,o,r,s,l,c,d,u,h,p,f,m){"use strict";var g=null,v=null,_=new n,b=!1,w=new r,y=new n,C=null,E=elByClass("jsStaticDialog"),L=["onBeforeClose","onClose","onShow"],S=["number","password","search","tel","text","url"];return{setup:function(){void 0===t&&(t=require("Ajax")),v=elCreate("div"),v.classList.add("dialogOverlay"),elAttr(v,"aria-hidden","true"),v.addEventListener(WCF_CLICK_EVENT,this._closeOnBackdrop.bind(this)),v.addEventListener("wheel",function(e){e.target===v&&e.preventDefault()},{passive:!1}),elById("content").appendChild(v),C=function(e){return 27!==e.keyCode||"INPUT"===e.target.nodeName||"TEXTAREA"===e.target.nodeName||(this.close(g),!1)}.bind(this),u.on("screen-xs",{match:function(){b=!0},unmatch:function(){b=!1},setup:function(){b=!0}}),this._initStaticDialogs(),s.add("Ui/Dialog",this._initStaticDialogs.bind(this)),u.setDialogContainer(v),"ios"===a.platform()&&window.addEventListener("resize",function(){_.forEach(function(e){elAttrBool(e.dialog,"aria-hidden")||this.rebuild(elData(e.dialog,"id"))}.bind(this))}.bind(this))},_initStaticDialogs:function(){for(var e,t,i;E.length;)e=E[0],e.classList.remove("jsStaticDialog"),(i=elData(e,"dialog-id"))&&(t=elById(i))&&function(e,t){t.classList.remove("jsStaticDialogContent"),elData(t,"is-static-dialog",!0),elHide(t),e.addEventListener(WCF_CLICK_EVENT,this.openStatic.bind(this,t.id,null,{title:elData(t,"title")}))}.bind(this)(e,t)},open:function(e,n){var a=w.get(e);if(i.isPlainObject(a))return this.openStatic(a.id,n);if("function"!=typeof e._dialogSetup)throw new Error("Callback object does not implement the method '_dialogSetup()'.");var o=e._dialogSetup();if(!i.isPlainObject(o))throw new Error("Expected an object literal as return value of '_dialogSetup()'.");a={id:o.id};var r=!0;if(void 0===o.source){var s=elById(o.id);if(null===s)throw new Error("Element id '"+o.id+"' is invalid and no source attribute was given. If you want to use the `html` argument instead, please add `source: null` to your dialog configuration.");o.source=document.createDocumentFragment(),o.source.appendChild(s),s.removeAttribute("id"),elShow(s)}else if(null===o.source)o.source=n;else if("function"==typeof o.source)o.source();else if(i.isPlainObject(o.source)){if("string"!=typeof n||""===n.trim())return t.api(this,o.source.data,function(t){t.returnValues&&"string"==typeof t.returnValues.template&&(this.open(e,t.returnValues.template),"function"==typeof o.source.after&&o.source.after(_.get(o.id).content,t))}.bind(this)),{};o.source=n}else{if("string"==typeof o.source){var s=elCreate("div");elAttr(s,"id",o.id),c.setInnerHtml(s,o.source),o.source=document.createDocumentFragment(),o.source.appendChild(s)}if(!o.source.nodeType||o.source.nodeType!==Node.DOCUMENT_FRAGMENT_NODE)throw new Error("Expected at least a document fragment as 'source' attribute.");r=!1}return w.set(e,a),y.set(o.id,e),this.openStatic(o.id,o.source,o.options,r)},openStatic:function(e,t,n,r){document.documentElement.classList.add("pageOverlayActive"),"desktop"!==a.platform()&&(this.isOpen(e)||u.scrollDisable()),_.has(e)?this._updateDialog(e,t):(n=i.extend({backdropCloseOnClick:!0,closable:!0,closeButtonLabel:o.get("wcf.global.button.close"),closeConfirmMessage:"",disableContentPadding:!1,title:"",onBeforeClose:null,onClose:null,onShow:null},n),n.closable||(n.backdropCloseOnClick=!1),n.closeConfirmMessage&&(n.onBeforeClose=function(e){d.show({confirm:this.close.bind(this,e),message:n.closeConfirmMessage})}.bind(this)),this._createDialog(e,t,n));var s=_.get(e);return"ios"===a.platform()&&window.setTimeout(function(){var e=elBySel("input, textarea",s.content);null!==e&&e.focus()}.bind(this),200),s},setTitle:function(e,t){e=this._getDialogId(e);var i=_.get(e);if(void 0===i)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");var n=elByClass("dialogTitle",i.dialog);n.length&&(n[0].textContent=t)},setCallback:function(e,t,i){if("object"==typeof e){var n=w.get(e);void 0!==n&&(e=n.id)}var a=_.get(e);if(void 0===a)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");if(-1===L.indexOf(t))throw new Error("Invalid callback identifier, '"+t+"' is not recognized.");if("function"!=typeof i&&null!==i)throw new Error("Only functions or the 'null' value are acceptable callback values ('"+typeof i+"' given).");a[t]=i},_createDialog:function(e,t,i,n){var a=null;if(null===t&&null===(a=elById(e)))throw new Error("Expected either a HTML string or an existing element id.");var o=elCreate("div");o.classList.add("dialogContainer"),elAttr(o,"aria-hidden","true"),elAttr(o,"role","dialog"),elData(o,"id",e);var r=elCreate("header");o.appendChild(r);var s=c.getUniqueId();elAttr(o,"aria-labelledby",s);var l=elCreate("span");if(l.classList.add("dialogTitle"),l.textContent=i.title,elAttr(l,"id",s),r.appendChild(l),i.closable){var d=elCreate("a");d.className="dialogCloseButton jsTooltip",elAttr(d,"title",i.closeButtonLabel),elAttr(d,"aria-label",i.closeButtonLabel),d.addEventListener(WCF_CLICK_EVENT,this._close.bind(this)),r.appendChild(d);var u=elCreate("span");u.className="icon icon24 fa-times",d.appendChild(u)}var h=elCreate("div");h.classList.add("dialogContent"),i.disableContentPadding&&h.classList.add("dialogContentNoPadding"),o.appendChild(h),h.addEventListener("wheel",function(e){for(var t,i,n,a=!1,o=e.target;;){if(t=o.clientHeight,i=o.scrollHeight,t<i){if(n=o.scrollTop,e.deltaY<0&&n>0){a=!0;break}if(e.deltaY>0&&n+t<i){a=!0;break}}if(!o||o===h)break;o=o.parentNode}!1===a&&e.preventDefault()},{passive:!1});var p;if(null===a)if("string"==typeof t)p=elCreate("div"),p.id=e,c.setInnerHtml(p,t);else{if(!(t instanceof DocumentFragment))throw new TypeError("'html' must either be a string or a DocumentFragment");for(var m,g=[],b=0,w=t.childNodes.length;b<w;b++)m=t.childNodes[b],m.nodeType===Node.ELEMENT_NODE&&g.push(m);"DIV"!==g[0].nodeName||g.length>1?(p=elCreate("div"),p.id=e,p.appendChild(t)):p=g[0]}else p=a;h.appendChild(p),"none"===p.style.getPropertyValue("display")&&elShow(p),_.set(e,{backdropCloseOnClick:i.backdropCloseOnClick,closable:i.closable,content:p,dialog:o,header:r,onBeforeClose:i.onBeforeClose,onClose:i.onClose,onShow:i.onShow,submitButton:null,inputFields:new f}),c.prepend(o,v),"function"==typeof i.onSetup&&i.onSetup(p),!0!==n&&this._updateDialog(e,null)},_updateDialog:function(e,t){var i=_.get(e);if(void 0===i)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");if("string"==typeof t&&c.setInnerHtml(i.content,t),"true"===elAttr(i.dialog,"aria-hidden")){i.closable&&"true"===elAttr(v,"aria-hidden")&&window.addEventListener("keyup",C),elAttr(i.dialog,"aria-hidden","false"),elAttr(v,"aria-hidden","false"),elData(v,"close-on-click",i.backdropCloseOnClick?"true":"false"),g=e;var n=elBySel(".jsDialogAutoFocus",i.dialog);null!==n&&null!==n.offsetParent&&("username"!==n.id&&"username"!==n.name||"safari"===a.browser()&&"ios"===a.platform()&&(n=null),n&&n.focus()),"function"==typeof i.onShow&&i.onShow(i.content),elDataBool(i.content,"is-static-dialog")&&p.fire("com.woltlab.wcf.dialog","openStatic",{content:i.content,id:e}),h.closeAll(),window.WCF.Dropdown.Interactive.Handler.closeAll()}this.rebuild(e),s.trigger()},rebuild:function(e){e=this._getDialogId(e);var t=_.get(e);if(void 0===t)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");if("true"!==elAttr(t.dialog,"aria-hidden")){var i=t.content.parentNode,n=elBySel(".formSubmit",t.content),o=0;null!==n?(i.classList.add("dialogForm"),n.classList.add("dialogFormSubmit"),o+=c.outerHeight(n),o-=1,i.style.setProperty("margin-bottom",o+"px","")):(i.classList.remove("dialogForm"),i.style.removeProperty("margin-bottom")),o+=c.outerHeight(t.header);var r=window.innerHeight*(b?1:.8)-o;if(i.style.setProperty("max-height",~~r+"px",""),"chrome"===a.browser()&&(t.content.scrollHeight>r?t.content.style.setProperty("margin-right","-1px",""):t.content.style.removeProperty("margin-right")),"chrome"===a.browser()||"safari"===a.browser()){var s=parseFloat(window.getComputedStyle(t.content).width),l=Math.round(s)%2!=0;t.content.parentNode.classList[l?"add":"remove"]("jsWebKitFractionalPixel")}var d=y.get(e);if(void 0!==d&&"function"==typeof d._dialogSubmit){var u=elBySelAll('input[data-dialog-submit-on-enter="true"]',t.content),h=elBySel('.formSubmit > input[type="submit"], .formSubmit > button[data-type="submit"]',t.content);if(null===h)return void(0===u.length&&console.warn("Broken dialog, expected a submit button.",t.content));if(t.submitButton!==h){t.submitButton=h,h.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),this._submit(e)}.bind(this));for(var p,f=null,g=0,v=u.length;g<v;g++)p=u[g],t.inputFields.has(p)||(-1!==S.indexOf(p.type)?(t.inputFields.add(p),null===f&&(f=function(t){m.Enter(t)&&(t.preventDefault(),this._submit(e))}.bind(this)),p.addEventListener("keydown",f)):console.warn("Unsupported input type.",p))}}}},_submit:function(e){var t=_.get(e),i=!0;t.inputFields.forEach(function(e){e.required&&(""===e.value.trim()?(elInnerError(e,o.get("wcf.global.form.error.empty")),i=!1):elInnerError(e,!1))}),i&&y.get(e)._dialogSubmit()},_close:function(e){e.preventDefault();var t=_.get(g);if("function"==typeof t.onBeforeClose)return t.onBeforeClose(g),!1;this.close(g)},_closeOnBackdrop:function(e){if(e.target!==v)return!0;"true"===elData(v,"close-on-click")?this._close(e):e.preventDefault()},close:function(e){e=this._getDialogId(e);var t=_.get(e);if(void 0===t)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");elAttr(t.dialog,"aria-hidden","true"),document.activeElement.closest(".dialogContainer")===t.dialog&&document.activeElement.blur(),"function"==typeof t.onClose&&t.onClose(e),g=null;for(var i=0;i<v.childElementCount;i++){var n=v.children[i];if("false"===elAttr(n,"aria-hidden")){g=elData(n,"id");break}}null===g?(elAttr(v,"aria-hidden","true"),elData(v,"close-on-click","false"),t.closable&&window.removeEventListener("keyup",C),document.documentElement.classList.remove("pageOverlayActive")):(t=_.get(g),elData(v,"close-on-click",t.backdropCloseOnClick?"true":"false")),"desktop"!==a.platform()&&u.scrollEnable()},getDialog:function(e){return _.get(this._getDialogId(e))},isOpen:function(e){var t=this.getDialog(e);return void 0!==t&&"false"===elAttr(t.dialog,"aria-hidden")},destroy:function(e){if("object"!=typeof e||e instanceof String)throw new TypeError("Expected the callback object as parameter.");if(w.has(e)){var t=w.get(e).id;this.isOpen(t)&&this.close(t),_.delete(t),w.delete(e)}},_getDialogId:function(e){if("object"==typeof e){var t=w.get(e);if(void 0!==t)return t.id}return e.toString()},_ajaxSetup:function(){return{}}}}),define("WoltLabSuite/Core/Ajax/Status",["Language"],function(e){"use strict";var t=0,i=null,n=null;return{_init:function(){i=elCreate("div"),i.classList.add("spinner");var t=elCreate("span");t.className="icon icon48 fa-spinner",i.appendChild(t);var n=elCreate("span");n.textContent=e.get("wcf.global.loading"),i.appendChild(n),document.body.appendChild(i)},show:function(){null===i&&this._init(),t++,null===n&&(n=window.setTimeout(function(){t&&i.classList.add("active"),n=null},250))},hide:function(){0===--t&&(null!==n&&window.clearTimeout(n),i.classList.remove("active"))}}}),define("WoltLabSuite/Core/Ajax/Request",["Core","Language","Dom/ChangeListener","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ajax/Status"],function(e,t,i,n,a,o){"use strict";function r(e){this._data=null,this._options={},this._previousXhr=null,this._xhr=null,this._init(e)}var s=!1,l=!1;return r.prototype={_init:function(t){this._options=e.extend({data:{},contentType:"application/x-www-form-urlencoded; charset=UTF-8",responseType:"application/json",type:"POST",url:"",withCredentials:!1,autoAbort:!1,ignoreError:!1,pinData:!1,silent:!1,includeRequestedWith:!0,failure:null,finalize:null,success:null,progress:null,uploadProgress:null,callbackObject:null},t),"object"==typeof t.callbackObject&&(this._options.callbackObject=t.callbackObject),this._options.url=e.convertLegacyUrl(this._options.url),0===this._options.url.indexOf("index.php")&&(this._options.url=WSC_API_URL+this._options.url),0===this._options.url.indexOf(WSC_API_URL)&&(this._options.includeRequestedWith=!0,this._options.withCredentials=!0),this._options.pinData&&(this._data=e.extend({},this._options.data)),null!==this._options.callbackObject&&("function"==typeof this._options.callbackObject._ajaxFailure&&(this._options.failure=this._options.callbackObject._ajaxFailure.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxFinalize&&(this._options.finalize=this._options.callbackObject._ajaxFinalize.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxSuccess&&(this._options.success=this._options.callbackObject._ajaxSuccess.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxProgress&&(this._options.progress=this._options.callbackObject._ajaxProgress.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxUploadProgress&&(this._options.uploadProgress=this._options.callbackObject._ajaxUploadProgress.bind(this._options.callbackObject))),!1===s&&(s=!0,window.addEventListener("beforeunload",function(){l=!0}))},sendRequest:function(t){(!0===t||this._options.autoAbort)&&this.abortPrevious(),this._options.silent||o.show(),this._xhr instanceof XMLHttpRequest&&(this._previousXhr=this._xhr),this._xhr=new XMLHttpRequest,this._xhr.open(this._options.type,this._options.url,!0),this._options.contentType&&this._xhr.setRequestHeader("Content-Type",this._options.contentType),(this._options.withCredentials||this._options.includeRequestedWith)&&this._xhr.setRequestHeader("X-Requested-With","XMLHttpRequest"),this._options.withCredentials&&(this._xhr.withCredentials=!0);var i=this,n=e.clone(this._options);if(this._xhr.onload=function(){this.readyState===XMLHttpRequest.DONE&&(this.status>=200&&this.status<300||304===this.status?n.responseType&&0!==this.getResponseHeader("Content-Type").indexOf(n.responseType)?i._failure(this,n):i._success(this,n):i._failure(this,n))},this._xhr.onerror=function(){i._failure(this,n)},this._options.progress&&(this._xhr.onprogress=this._options.progress),this._options.uploadProgress&&(this._xhr.upload.onprogress=this._options.uploadProgress),"POST"===this._options.type){var a=this._options.data;"object"==typeof a&&"FormData"!==e.getType(a)&&(a=e.serialize(a)),this._xhr.send(a)}else this._xhr.send()},abortPrevious:function(){null!==this._previousXhr&&(this._previousXhr.abort(),this._previousXhr=null,this._options.silent||o.hide())},setOption:function(e,t){this._options[e]=t},getOption:function(e){return objOwns(this._options,e)?this._options[e]:null},setData:function(t){null!==this._data&&"FormData"!==e.getType(t)&&(t=e.extend(this._data,t)),this._options.data=t},_success:function(e,t){if(t.silent||o.hide(),"function"==typeof t.success){var i=null;if("application/json"===e.getResponseHeader("Content-Type")){try{i=JSON.parse(e.responseText)}catch(i){return void this._failure(e,t)}i&&i.returnValues&&void 0!==i.returnValues.template&&(i.returnValues.template=i.returnValues.template.trim()),i&&i.forceBackgroundQueuePerform&&require(["WoltLabSuite/Core/BackgroundQueue"],function(e){e.invoke()})}t.success(i,e.responseText,e,t.data)}this._finalize(t)},_failure:function(e,i){if(!l){i.silent||o.hide();var r=null;try{r=JSON.parse(e.responseText)}catch(e){}var s=!0;if("function"==typeof i.failure&&(s=i.failure(r||{},e.responseText||"",e,i.data)),!0!==i.ignoreError&&!1!==s){var c=this.getErrorHtml(r,e);c&&(void 0===a&&(a=require("Ui/Dialog")),a.openStatic(n.getUniqueId(),c,{title:t.get("wcf.global.error.title")}))}this._finalize(i)}},getErrorHtml:function(e,t){var i="",n="";if(null!==e?(e.stacktrace?i="<br><p>Stacktrace:</p><p>"+e.stacktrace+"</p>":e.exceptionID&&(i="<br><p>Exception ID: <code>"+e.exceptionID+"</code></p>"),n=e.message,e.previous.forEach(function(e){i+="<hr><p>"+e.message+"</p>",i+="<br><p>Stacktrace</p><p>"+e.stacktrace+"</p>"})):n=t.responseText,!n||"undefined"===n){if(!ENABLE_DEBUG_MODE)return null;n="XMLHttpRequest failed without a responseText. Check your browser console."}return'<div class="ajaxDebugMessage"><p>'+n+"</p>"+i+"</div>"},_finalize:function(e){"function"==typeof e.finalize&&e.finalize(this._xhr),this._previousXhr=null,i.trigger();for(var t=elBySelAll('a[href*="#"]'),n=0,a=t.length;n<a;n++){var o=t[n],r=elAttr(o,"href");-1===r.indexOf("AJAXProxy")&&-1===r.indexOf("ajax-proxy")||(r=r.substr(r.indexOf("#")),elAttr(o,"href",document.location.toString().replace(/#.*/,"")+r))}}},r}),define("WoltLabSuite/Core/Ajax",["AjaxRequest","Core","ObjectMap"],function(e,t,i){"use strict";var n=new i;return{api:function(t,i,a,o){void 0===e&&(e=require("AjaxRequest")),"object"!=typeof i&&(i={});var r=n.get(t);if(void 0===r){if("function"!=typeof t._ajaxSetup)throw new TypeError("Callback object must implement at least _ajaxSetup().");var s=t._ajaxSetup();s.pinData=!0,s.callbackObject=t,s.url||(s.url="index.php?ajax-proxy/&t="+SECURITY_TOKEN,s.withCredentials=!0),r=new e(s),n.set(t,r)}var l=null,c=null;return"function"==typeof a&&(l=r.getOption("success"),r.setOption("success",a)),"function"==typeof o&&(c=r.getOption("failure"),r.setOption("failure",o)),r.setData(i),r.sendRequest(),null!==l&&r.setOption("success",l),null!==c&&r.setOption("failure",c),r},apiOnce:function(t){void 0===e&&(e=require("AjaxRequest")),t.pinData=!1,t.callbackObject=null,t.url||(t.url="index.php?ajax-proxy/&t="+SECURITY_TOKEN,t.withCredentials=!0),new e(t).sendRequest(!1)},getRequestObject:function(e){if(!n.has(e))throw new Error("Expected a previously used callback object, provided object is unknown.");return n.get(e)}}}),define("WoltLabSuite/Core/BackgroundQueue",["Ajax"],function(e){"use strict";var t=0,i=!1,n="";return{setUrl:function(e){n=e},invoke:function(){if(""===n)return void console.error("The background queue has not been initialized yet.");i||(i=!0,e.api(this))},_ajaxSuccess:function(e){t++,e>0&&t<5?window.setTimeout(function(){i=!1,this.invoke()}.bind(this),1e3):(i=!1,t=0)},_ajaxSetup:function(){return{url:n,ignoreError:!0,silent:!0}}}}),function(){var e=function(e){"use strict";function t(e){if(e.paused||e.ended||g)return!1;try{d.clearRect(0,0,l,s),d.drawImage(e,0,0,l,s)}catch(e){}b=setTimeout(function(){t(e)},B.duration),M.setIcon(c)}function i(e){var t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;e=e.replace(t,function(e,t,i,n){return t+t+i+i+n+n});var i=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return!!i&&{r:parseInt(i[1],16),g:parseInt(i[2],16),b:parseInt(i[3],16)}}function n(e,t){var i,n={};for(i in e)n[i]=e[i];for(i in t)n[i]=t[i];return n}function a(){return w.hidden||w.msHidden||w.webkitHidden||w.mozHidden}e=e||{};var o,r,s,l,c,d,u,h,p,f,m,g,v,_,b,w,y={bgColor:"#d00",textColor:"#fff",fontFamily:"sans-serif",fontStyle:"bold",type:"circle",position:"down",animation:"slide",elementId:!1,element:null,dataUrl:!1,win:window};v={},v.ff="undefined"!=typeof InstallTrigger,v.chrome=!!window.chrome,v.opera=!!window.opera||navigator.userAgent.indexOf("Opera")>=0,v.ie=!1,v.safari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,v.supported=v.chrome||v.ff||v.opera;var C=[];m=function(){},h=g=!1;var E={};E.ready=function(){h=!0,E.reset(),m()},E.reset=function(){h&&(C=[],p=!1,f=!1,d.clearRect(0,0,l,s),d.drawImage(u,0,0,l,s),M.setIcon(c),window.clearTimeout(_),window.clearTimeout(b))},E.start=function(){if(h&&!f){var e=function(){p=C[0],f=!1,C.length>0&&(C.shift(),E.start())};if(C.length>0){f=!0;var t=function(){["type","animation","bgColor","textColor","fontFamily","fontStyle"].forEach(function(e){e in C[0].options&&(o[e]=C[0].options[e])}),B.run(C[0].options,function(){e()},!1)};p?B.run(p.options,function(){t()},!0):t()}}};var L={},S=function(e){return e.n="number"==typeof e.n?Math.abs(0|e.n):e.n,e.x=l*e.x,e.y=s*e.y,e.w=l*e.w,e.h=s*e.h,e.len=(""+e.n).length,e};L.circle=function(e){e=S(e);var t=!1;2===e.len?(e.x=e.x-.4*e.w,e.w=1.4*e.w,t=!0):e.len>=3&&(e.x=e.x-.65*e.w,e.w=1.65*e.w,t=!0),d.clearRect(0,0,l,s),d.drawImage(u,0,0,l,s),d.beginPath(),d.font=o.fontStyle+" "+Math.floor(e.h*(e.n>99?.85:1))+"px "+o.fontFamily,d.textAlign="center",t?(d.moveTo(e.x+e.w/2,e.y),d.lineTo(e.x+e.w-e.h/2,e.y),d.quadraticCurveTo(e.x+e.w,e.y,e.x+e.w,e.y+e.h/2),d.lineTo(e.x+e.w,e.y+e.h-e.h/2),d.quadraticCurveTo(e.x+e.w,e.y+e.h,e.x+e.w-e.h/2,e.y+e.h),d.lineTo(e.x+e.h/2,e.y+e.h),d.quadraticCurveTo(e.x,e.y+e.h,e.x,e.y+e.h-e.h/2),d.lineTo(e.x,e.y+e.h/2),d.quadraticCurveTo(e.x,e.y,e.x+e.h/2,e.y)):d.arc(e.x+e.w/2,e.y+e.h/2,e.h/2,0,2*Math.PI),d.fillStyle="rgba("+o.bgColor.r+","+o.bgColor.g+","+o.bgColor.b+","+e.o+")",d.fill(),d.closePath(),d.beginPath(),d.stroke(),d.fillStyle="rgba("+o.textColor.r+","+o.textColor.g+","+o.textColor.b+","+e.o+")","number"==typeof e.n&&e.n>999?d.fillText((e.n>9999?9:Math.floor(e.n/1e3))+"k+",Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.2*e.h)):d.fillText(e.n,Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.15*e.h)),d.closePath()},L.rectangle=function(e){e=S(e);2===e.len?(e.x=e.x-.4*e.w,e.w=1.4*e.w):e.len>=3&&(e.x=e.x-.65*e.w,e.w=1.65*e.w),d.clearRect(0,0,l,s),d.drawImage(u,0,0,l,s),d.beginPath(),d.font=o.fontStyle+" "+Math.floor(e.h*(e.n>99?.9:1))+"px "+o.fontFamily,d.textAlign="center",d.fillStyle="rgba("+o.bgColor.r+","+o.bgColor.g+","+o.bgColor.b+","+e.o+")",d.fillRect(e.x,e.y,e.w,e.h),d.fillStyle="rgba("+o.textColor.r+","+o.textColor.g+","+o.textColor.b+","+e.o+")","number"==typeof e.n&&e.n>999?d.fillText((e.n>9999?9:Math.floor(e.n/1e3))+"k+",Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.2*e.h)):d.fillText(e.n,Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.15*e.h)),d.closePath()};var D=function(e,t){t=("string"==typeof t?{animation:t}:t)||{},m=function(){try{if("number"==typeof e?e>0:""!==e){var n={type:"badge",options:{n:e}};if("animation"in t&&B.types[""+t.animation]&&(n.options.animation=""+t.animation),"type"in t&&L[""+t.type]&&(n.options.type=""+t.type),["bgColor","textColor"].forEach(function(e){e in t&&(n.options[e]=i(t[e]))}),["fontStyle","fontFamily"].forEach(function(e){e in t&&(n.options[e]=t[e])}),C.push(n),C.length>100)throw new Error("Too many badges requests in queue.");E.start()}else E.reset()}catch(e){throw new Error("Error setting badge. Message: "+e.message)}},h&&m()},x=function(e){m=function(){try{var t=e.width,i=e.height,n=document.createElement("img"),a=t/l<i/s?t/l:i/s;n.setAttribute("crossOrigin","anonymous"),n.onload=function(){d.clearRect(0,0,l,s),d.drawImage(n,0,0,l,s),M.setIcon(c)},n.setAttribute("src",e.getAttribute("src")),n.height=i/a,n.width=t/a}catch(e){throw new Error("Error setting image. Message: "+e.message)}},h&&m()},I=function(e){m=function(){M.setIconSrc(e)},h&&m()},T=function(e){m=function(){try{if("stop"===e)return g=!0,E.reset(),void(g=!1);e.addEventListener("play",function(){t(this)},!1)}catch(e){throw new Error("Error setting video. Message: "+e.message)}},h&&m()},N=function(e){if(window.URL&&window.URL.createObjectURL||(window.URL=window.URL||{},window.URL.createObjectURL=function(e){return e}),v.supported){var i=!1;navigator.getUserMedia=navigator.getUserMedia||navigator.oGetUserMedia||navigator.msGetUserMedia||navigator.mozGetUserMedia||navigator.webkitGetUserMedia,m=function(){try{if("stop"===e)return g=!0,E.reset(),void(g=!1);i=document.createElement("video"),i.width=l,i.height=s,navigator.getUserMedia({video:!0,audio:!1},function(e){i.src=URL.createObjectURL(e),i.play(),t(i)},function(){})}catch(e){throw new Error("Error setting webcam. Message: "+e.message)}},h&&m()}},k=function(e,t){var n=e;null==t&&"[object Object]"==Object.prototype.toString.call(e)||(n={},n[e]=t);for(var a=Object.keys(n),r=0;r<a.length;r++)"bgColor"==a[r]||"textColor"==a[r]?o[a[r]]=i(n[a[r]]):o[a[r]]=n[a[r]];C.push(p),E.start()},M={};M.getIcons=function(){var e=[];return o.element?e=[o.element]:o.elementId?(e=[w.getElementById(o.elementId)],e[0].setAttribute("href",e[0].getAttribute("src"))):(e=function(){for(var e=[],t=w.getElementsByTagName("head")[0].getElementsByTagName("link"),i=0;i<t.length;i++)/(^|\s)icon(\s|$)/i.test(t[i].getAttribute("rel"))&&e.push(t[i]);return e}(),0===e.length&&(e=[w.createElement("link")],e[0].setAttribute("rel","icon"),w.getElementsByTagName("head")[0].appendChild(e[0]))),e.forEach(function(e){e.setAttribute("type","image/png")}),e},M.setIcon=function(e){var t=e.toDataURL("image/png");M.setIconSrc(t)},M.setIconSrc=function(e){if(o.dataUrl&&o.dataUrl(e),o.element)o.element.setAttribute("href",e),o.element.setAttribute("src",e);else if(o.elementId){var t=w.getElementById(o.elementId);t.setAttribute("href",e),t.setAttribute("src",e)}else if(v.ff||v.opera){var i=r[r.length-1],n=w.createElement("link");r=[n],v.opera&&n.setAttribute("rel","icon"),n.setAttribute("rel","icon"),n.setAttribute("type","image/png"),w.getElementsByTagName("head")[0].appendChild(n),n.setAttribute("href",e),i.parentNode&&i.parentNode.removeChild(i)}else r.forEach(function(t){t.setAttribute("href",e)})};var B={};return B.duration=40,B.types={},B.types.fade=[{x:.4,y:.4,w:.6,h:.6,o:0},{x:.4,y:.4,w:.6,h:.6,o:.1},{x:.4,y:.4,w:.6,h:.6,o:.2},{x:.4,y:.4,w:.6,h:.6,o:.3},{x:.4,y:.4,w:.6,h:.6,o:.4},{x:.4,y:.4,w:.6,h:.6,o:.5},{x:.4,y:.4,w:.6,h:.6,o:.6},{x:.4,y:.4,w:.6,h:.6,o:.7},{x:.4,y:.4,w:.6,h:.6,o:.8},{x:.4,y:.4,w:.6,h:.6,o:.9},{x:.4,y:.4,w:.6,h:.6,o:1}],B.types.none=[{x:.4,y:.4,w:.6,h:.6,o:1}],B.types.pop=[{x:1,y:1,w:0,h:0,o:1},{x:.9,y:.9,w:.1,h:.1,o:1},{x:.8,y:.8,w:.2,h:.2,o:1},{x:.7,y:.7,w:.3,h:.3,o:1},{x:.6,y:.6,w:.4,h:.4,o:1},{x:.5,y:.5,w:.5,h:.5,o:1},{x:.4,y:.4,w:.6,h:.6,o:1}],B.types.popFade=[{x:.75,y:.75,w:0,h:0,o:0},{x:.65,y:.65,w:.1,h:.1,o:.2},{x:.6,y:.6,w:.2,h:.2,o:.4},{x:.55,y:.55,w:.3,h:.3,o:.6},{x:.5,y:.5,w:.4,h:.4,o:.8},{x:.45,y:.45,w:.5,h:.5,o:.9},{x:.4,y:.4,w:.6,h:.6,o:1}],B.types.slide=[{x:.4,y:1,w:.6,h:.6,o:1},{x:.4,y:.9,w:.6,h:.6,o:1},{x:.4,y:.9,w:.6,h:.6,o:1},{x:.4,y:.8,w:.6,h:.6,o:1},{x:.4,y:.7,w:.6,h:.6,o:1},{x:.4,y:.6,w:.6,h:.6,o:1},{x:.4,y:.5,w:.6,h:.6,o:1},{x:.4,y:.4,w:.6,h:.6,o:1}],B.run=function(e,t,i,r){var s=B.types[a()?"none":o.animation];if(r=!0===i?void 0!==r?r:s.length-1:void 0!==r?r:0,t=t||function(){},!(r<s.length&&r>=0))return void t();L[o.type](n(e,s[r])),_=setTimeout(function(){i?r-=1:r+=1,B.run(e,t,i,r)},B.duration),M.setIcon(c)},function(){o=n(y,e),o.bgColor=i(o.bgColor),o.textColor=i(o.textColor),o.position=o.position.toLowerCase(),o.animation=B.types[""+o.animation]?o.animation:y.animation,w=o.win.document;var t=o.position.indexOf("up")>-1,a=o.position.indexOf("left")>-1;if(t||a)for(var h in B.types)for(var p=0;p<B.types[h].length;p++){var f=B.types[h][p];t&&(f.y<.6?f.y=f.y-.4:f.y=f.y-2*f.y+(1-f.w)),a&&(f.x<.6?f.x=f.x-.4:f.x=f.x-2*f.x+(1-f.h)),B.types[h][p]=f}o.type=L[""+o.type]?o.type:y.type,r=M.getIcons(),c=document.createElement("canvas"),u=document.createElement("img");var m=r[r.length-1];m.hasAttribute("href")?(u.setAttribute("crossOrigin","anonymous"),u.onload=function(){s=u.height>0?u.height:32,l=u.width>0?u.width:32,c.height=s,c.width=l,d=c.getContext("2d"),E.ready()},u.setAttribute("src",m.getAttribute("href"))):(s=32,l=32,u.height=s,u.width=l,c.height=s,c.width=l,d=c.getContext("2d"),E.ready())}(),{badge:D,video:T,image:x,rawImageSrc:I,webcam:N,setOpt:k,reset:E.reset,browser:{supported:v.supported}}};void 0!==define&&define.amd?define("favico",[],function(){return e}):"undefined"!=typeof module&&module.exports?module.exports=e:this.Favico=e}(),function e(t,i,n){function a(r,s){if(!i[r]){if(!t[r]){var l="function"==typeof require&&require;if(!s&&l)return l(r,!0);if(o)return o(r,!0);var c=new Error("Cannot find module '"+r+"'");throw c.code="MODULE_NOT_FOUND",c}var d=i[r]={exports:{}};t[r][0].call(d.exports,function(e){var i=t[r][1][e];return a(i||e)},d,d.exports,e,t,i,n)}return i[r].exports}for(var o="function"==typeof require&&require,r=0;r<n.length;r++)a(n[r]);return a}({1:[function(e,t,i){"use strict";var n=e("../main");"function"==typeof define&&define.amd?define("perfect-scrollbar",n):(window.PerfectScrollbar=n,void 0===window.Ps&&(window.Ps=n))},{"../main":7}],2:[function(e,t,i){"use strict";function n(e,t){var i=e.className.split(" ");i.indexOf(t)<0&&i.push(t),e.className=i.join(" ")}function a(e,t){var i=e.className.split(" "),n=i.indexOf(t);n>=0&&i.splice(n,1),e.className=i.join(" ")}i.add=function(e,t){e.classList?e.classList.add(t):n(e,t)},i.remove=function(e,t){e.classList?e.classList.remove(t):a(e,t)},i.list=function(e){return e.classList?e.classList:e.className.split(" ")}},{}],3:[function(e,t,i){"use strict";function n(e,t){return window.getComputedStyle(e)[t]}function a(e,t,i){return"number"==typeof i&&(i=i.toString()+"px"),e.style[t]=i,e}function o(e,t){for(var i in t){var n=t[i];"number"==typeof n&&(n=n.toString()+"px"),e.style[i]=n}return e}i.e=function(e,t){var i=document.createElement(e);return i.className=t,i},i.appendTo=function(e,t){return t.appendChild(e),e},i.css=function(e,t,i){return"object"==typeof t?o(e,t):void 0===i?n(e,t):a(e,t,i)},i.matches=function(e,t){return void 0!==e.matches?e.matches(t):void 0!==e.matchesSelector?e.matchesSelector(t):void 0!==e.webkitMatchesSelector?e.webkitMatchesSelector(t):void 0!==e.mozMatchesSelector?e.mozMatchesSelector(t):void 0!==e.msMatchesSelector?e.msMatchesSelector(t):void 0},i.remove=function(e){void 0!==e.remove?e.remove():e.parentNode&&e.parentNode.removeChild(e)}},{}],4:[function(e,t,i){"use strict";var n=function(e){this.element=e,this.events={}};n.prototype.bind=function(e,t){void 0===this.events[e]&&(this.events[e]=[]),this.events[e].push(t),this.element.addEventListener(e,t,!1)},n.prototype.unbind=function(e,t){var i=void 0!==t;this.events[e]=this.events[e].filter(function(n){return!(!i||n===t)||(this.element.removeEventListener(e,n,!1),!1)},this)},n.prototype.unbindAll=function(){for(var e in this.events)this.unbind(e)};var a=function(){this.eventElements=[]};a.prototype.eventElement=function(e){var t=this.eventElements.filter(function(t){return t.element===e})[0];return void 0===t&&(t=new n(e),this.eventElements.push(t)),t},a.prototype.bind=function(e,t,i){this.eventElement(e).bind(t,i)},a.prototype.unbind=function(e,t,i){this.eventElement(e).unbind(t,i)},a.prototype.unbindAll=function(){for(var e=0;e<this.eventElements.length;e++)this.eventElements[e].unbindAll()},a.prototype.once=function(e,t,i){var n=this.eventElement(e),a=function(e){n.unbind(t,a),i(e)};n.bind(t,a)},t.exports=a},{}],5:[function(e,t,i){"use strict";t.exports=function(){function e(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return function(){return e()+e()+"-"+e()+"-"+e()+"-"+e()+"-"+e()+e()+e()}}()},{}],6:[function(e,t,i){"use strict";var n=e("./class"),a=e("./dom");i.toInt=function(e){return"string"==typeof e?parseInt(e,10):~~e},i.clone=function(e){if(null===e)return null;if("object"==typeof e){var t={};for(var i in e)t[i]=this.clone(e[i]);return t}return e},i.extend=function(e,t){var i=this.clone(e);for(var n in t)i[n]=this.clone(t[n]);return i},i.isEditable=function(e){return a.matches(e,"input,[contenteditable]")||a.matches(e,"select,[contenteditable]")||a.matches(e,"textarea,[contenteditable]")||a.matches(e,"button,[contenteditable]")},i.removePsClasses=function(e){for(var t=n.list(e),i=0;i<t.length;i++){var a=t[i];0===a.indexOf("ps-")&&n.remove(e,a)}},i.outerWidth=function(e){
-return this.toInt(a.css(e,"width"))+this.toInt(a.css(e,"paddingLeft"))+this.toInt(a.css(e,"paddingRight"))+this.toInt(a.css(e,"borderLeftWidth"))+this.toInt(a.css(e,"borderRightWidth"))},i.startScrolling=function(e,t){n.add(e,"ps-in-scrolling"),void 0!==t?n.add(e,"ps-"+t):(n.add(e,"ps-x"),n.add(e,"ps-y"))},i.stopScrolling=function(e,t){n.remove(e,"ps-in-scrolling"),void 0!==t?n.remove(e,"ps-"+t):(n.remove(e,"ps-x"),n.remove(e,"ps-y"))},i.env={isWebKit:"WebkitAppearance"in document.documentElement.style,supportsTouch:"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch,supportsIePointer:null!==window.navigator.msMaxTouchPoints}},{"./class":2,"./dom":3}],7:[function(e,t,i){"use strict";var n=e("./plugin/destroy"),a=e("./plugin/initialize"),o=e("./plugin/update");t.exports={initialize:a,update:o,destroy:n}},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":20}],8:[function(e,t,i){"use strict";t.exports={wheelSpeed:1,wheelPropagation:!1,swipePropagation:!0,minScrollbarLength:null,maxScrollbarLength:null,useBothWheelAxes:!1,useKeyboard:!0,suppressScrollX:!1,suppressScrollY:!1,scrollXMarginOffset:0,scrollYMarginOffset:0}},{}],9:[function(e,t,i){"use strict";var n=e("../lib/dom"),a=e("../lib/helper"),o=e("./instances");t.exports=function(e){var t=o.get(e);t.event.unbindAll(),n.remove(t.scrollbarX),n.remove(t.scrollbarY),n.remove(t.scrollbarXRail),n.remove(t.scrollbarYRail),a.removePsClasses(e),o.remove(e)}},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(e,t,i){"use strict";function n(e,t){function i(e){return e.getBoundingClientRect()}var n=window.Event.prototype.stopPropagation.bind;t.event.bind(t.scrollbarY,"click",n),t.event.bind(t.scrollbarYRail,"click",function(n){var o=a.toInt(t.scrollbarYHeight/2),s=n.pageY-i(t.scrollbarYRail).top-o,l=t.containerHeight-t.scrollbarYHeight,c=s/l;c<0?c=0:c>1&&(c=1),e.scrollTop=(t.contentHeight-t.containerHeight)*c,r(e)}),t.event.bind(t.scrollbarX,"click",n),t.event.bind(t.scrollbarXRail,"click",function(n){var o=a.toInt(t.scrollbarXWidth/2),s=n.pageX-i(t.scrollbarXRail).left-o;console.log(n.pageX,t.scrollbarXRail.offsetLeft);var l=t.containerWidth-t.scrollbarXWidth,c=s/l;c<0?c=0:c>1&&(c=1),e.scrollLeft=(t.contentWidth-t.containerWidth)*c,r(e)})}var a=e("../../lib/helper"),o=e("../instances"),r=e("../update-geometry");t.exports=function(e){n(e,o.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],11:[function(e,t,i){"use strict";function n(e,t){function i(i){var a=n+i,o=t.containerWidth-t.scrollbarXWidth;t.scrollbarXLeft=a<0?0:a>o?o:a;var s=r.toInt(t.scrollbarXLeft*(t.contentWidth-t.containerWidth)/(t.containerWidth-t.scrollbarXWidth));e.scrollLeft=s}var n=null,a=null,s=function(t){i(t.pageX-a),l(e),t.stopPropagation(),t.preventDefault()},c=function(){r.stopScrolling(e,"x"),t.event.unbind(t.ownerDocument,"mousemove",s)};t.event.bind(t.scrollbarX,"mousedown",function(i){a=i.pageX,n=r.toInt(o.css(t.scrollbarX,"left")),r.startScrolling(e,"x"),t.event.bind(t.ownerDocument,"mousemove",s),t.event.once(t.ownerDocument,"mouseup",c),i.stopPropagation(),i.preventDefault()})}function a(e,t){function i(i){var a=n+i,o=t.containerHeight-t.scrollbarYHeight;t.scrollbarYTop=a<0?0:a>o?o:a;var s=r.toInt(t.scrollbarYTop*(t.contentHeight-t.containerHeight)/(t.containerHeight-t.scrollbarYHeight));e.scrollTop=s}var n=null,a=null,s=function(t){i(t.pageY-a),l(e),t.stopPropagation(),t.preventDefault()},c=function(){r.stopScrolling(e,"y"),t.event.unbind(t.ownerDocument,"mousemove",s)};t.event.bind(t.scrollbarY,"mousedown",function(i){a=i.pageY,n=r.toInt(o.css(t.scrollbarY,"top")),r.startScrolling(e,"y"),t.event.bind(t.ownerDocument,"mousemove",s),t.event.once(t.ownerDocument,"mouseup",c),i.stopPropagation(),i.preventDefault()})}var o=e("../../lib/dom"),r=e("../../lib/helper"),s=e("../instances"),l=e("../update-geometry");t.exports=function(e){var t=s.get(e);n(e,t),a(e,t)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19}],12:[function(e,t,i){"use strict";function n(e,t){function i(i,n){var a=e.scrollTop;if(0===i){if(!t.scrollbarYActive)return!1;if(0===a&&n>0||a>=t.contentHeight-t.containerHeight&&n<0)return!t.settings.wheelPropagation}var o=e.scrollLeft;if(0===n){if(!t.scrollbarXActive)return!1;if(0===o&&i<0||o>=t.contentWidth-t.containerWidth&&i>0)return!t.settings.wheelPropagation}return!0}var n=!1;t.event.bind(e,"mouseenter",function(){n=!0}),t.event.bind(e,"mouseleave",function(){n=!1});var o=!1;t.event.bind(t.ownerDocument,"keydown",function(s){if((!s.isDefaultPrevented||!s.isDefaultPrevented())&&n){var l=document.activeElement?document.activeElement:t.ownerDocument.activeElement;if(l){for(;l.shadowRoot;)l=l.shadowRoot.activeElement;if(a.isEditable(l))return}var c=0,d=0;switch(s.which){case 37:c=-30;break;case 38:d=30;break;case 39:c=30;break;case 40:d=-30;break;case 33:d=90;break;case 32:case 34:d=-90;break;case 35:d=s.ctrlKey?-t.contentHeight:-t.containerHeight;break;case 36:d=s.ctrlKey?e.scrollTop:t.containerHeight;break;default:return}e.scrollTop=e.scrollTop-d,e.scrollLeft=e.scrollLeft+c,r(e),o=i(c,d),o&&s.preventDefault()}})}var a=e("../../lib/helper"),o=e("../instances"),r=e("../update-geometry");t.exports=function(e){n(e,o.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],13:[function(e,t,i){"use strict";function n(e,t){function i(i,n){var a=e.scrollTop;if(0===i){if(!t.scrollbarYActive)return!1;if(0===a&&n>0||a>=t.contentHeight-t.containerHeight&&n<0)return!t.settings.wheelPropagation}var o=e.scrollLeft;if(0===n){if(!t.scrollbarXActive)return!1;if(0===o&&i<0||o>=t.contentWidth-t.containerWidth&&i>0)return!t.settings.wheelPropagation}return!0}function n(e){var t=e.deltaX,i=-1*e.deltaY;return void 0!==t&&void 0!==i||(t=-1*e.wheelDeltaX/6,i=e.wheelDeltaY/6),e.deltaMode&&1===e.deltaMode&&(t*=10,i*=10),t!==t&&i!==i&&(t=0,i=e.wheelDelta),[t,i]}function o(t,i){var n=e.querySelector("textarea:hover");if(n){var a=n.scrollHeight-n.clientHeight;if(a>0&&!(0===n.scrollTop&&i>0||n.scrollTop===a&&i<0))return!0;var o=n.scrollLeft-n.clientWidth;if(o>0&&!(0===n.scrollLeft&&t<0||n.scrollLeft===o&&t>0))return!0}return!1}function s(s){if(a.env.isWebKit||!e.querySelector("select:focus")){var c=n(s),d=c[0],u=c[1];o(d,u)||(l=!1,t.settings.useBothWheelAxes?t.scrollbarYActive&&!t.scrollbarXActive?(e.scrollTop=u?e.scrollTop-u*t.settings.wheelSpeed:e.scrollTop+d*t.settings.wheelSpeed,l=!0):t.scrollbarXActive&&!t.scrollbarYActive&&(e.scrollLeft=d?e.scrollLeft+d*t.settings.wheelSpeed:e.scrollLeft-u*t.settings.wheelSpeed,l=!0):(e.scrollTop=e.scrollTop-u*t.settings.wheelSpeed,e.scrollLeft=e.scrollLeft+d*t.settings.wheelSpeed),r(e),(l=l||i(d,u))&&(s.stopPropagation(),s.preventDefault()))}}var l=!1;void 0!==window.onwheel?t.event.bind(e,"wheel",s):void 0!==window.onmousewheel&&t.event.bind(e,"mousewheel",s)}var a=e("../../lib/helper"),o=e("../instances"),r=e("../update-geometry");t.exports=function(e){n(e,o.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],14:[function(e,t,i){"use strict";function n(e,t){t.event.bind(e,"scroll",function(){o(e)})}var a=e("../instances"),o=e("../update-geometry");t.exports=function(e){n(e,a.get(e))}},{"../instances":18,"../update-geometry":19}],15:[function(e,t,i){"use strict";function n(e,t){function i(){var e=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===e.toString().length?null:e.getRangeAt(0).commonAncestorContainer}function n(){l||(l=setInterval(function(){if(!o.get(e))return void clearInterval(l);e.scrollTop=e.scrollTop+c.top,e.scrollLeft=e.scrollLeft+c.left,r(e)},50))}function s(){l&&(clearInterval(l),l=null),a.stopScrolling(e)}var l=null,c={top:0,left:0},d=!1;t.event.bind(t.ownerDocument,"selectionchange",function(){e.contains(i())?d=!0:(d=!1,s())}),t.event.bind(window,"mouseup",function(){d&&(d=!1,s())}),t.event.bind(window,"mousemove",function(t){if(d){var i={x:t.pageX,y:t.pageY},o={left:e.offsetLeft,right:e.offsetLeft+e.offsetWidth,top:e.offsetTop,bottom:e.offsetTop+e.offsetHeight};i.x<o.left+3?(c.left=-5,a.startScrolling(e,"x")):i.x>o.right-3?(c.left=5,a.startScrolling(e,"x")):c.left=0,i.y<o.top+3?(c.top=o.top+3-i.y<5?-5:-20,a.startScrolling(e,"y")):i.y>o.bottom-3?(c.top=i.y-o.bottom+3<5?5:20,a.startScrolling(e,"y")):c.top=0,0===c.top&&0===c.left?s():n()}})}var a=e("../../lib/helper"),o=e("../instances"),r=e("../update-geometry");t.exports=function(e){n(e,o.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],16:[function(e,t,i){"use strict";function n(e,t,i,n){function r(i,n){var a=e.scrollTop,o=e.scrollLeft,r=Math.abs(i),s=Math.abs(n);if(s>r){if(n<0&&a===t.contentHeight-t.containerHeight||n>0&&0===a)return!t.settings.swipePropagation}else if(r>s&&(i<0&&o===t.contentWidth-t.containerWidth||i>0&&0===o))return!t.settings.swipePropagation;return!0}function s(t,i){e.scrollTop=e.scrollTop-i,e.scrollLeft=e.scrollLeft-t,o(e)}function l(){b=!0}function c(){b=!1}function d(e){return e.targetTouches?e.targetTouches[0]:e}function u(e){return!(!e.targetTouches||1!==e.targetTouches.length)||!(!e.pointerType||"mouse"===e.pointerType||e.pointerType===e.MSPOINTER_TYPE_MOUSE)}function h(e){if(u(e)){w=!0;var t=d(e);m.pageX=t.pageX,m.pageY=t.pageY,g=(new Date).getTime(),null!==_&&clearInterval(_),e.stopPropagation()}}function p(e){if(!b&&w&&u(e)){var t=d(e),i={pageX:t.pageX,pageY:t.pageY},n=i.pageX-m.pageX,a=i.pageY-m.pageY;s(n,a),m=i;var o=(new Date).getTime(),l=o-g;l>0&&(v.x=n/l,v.y=a/l,g=o),r(n,a)&&(e.stopPropagation(),e.preventDefault())}}function f(){!b&&w&&(w=!1,clearInterval(_),_=setInterval(function(){return a.get(e)?Math.abs(v.x)<.01&&Math.abs(v.y)<.01?void clearInterval(_):(s(30*v.x,30*v.y),v.x*=.8,void(v.y*=.8)):void clearInterval(_)},10))}var m={},g=0,v={},_=null,b=!1,w=!1;i&&(t.event.bind(window,"touchstart",l),t.event.bind(window,"touchend",c),t.event.bind(e,"touchstart",h),t.event.bind(e,"touchmove",p),t.event.bind(e,"touchend",f)),n&&(window.PointerEvent?(t.event.bind(window,"pointerdown",l),t.event.bind(window,"pointerup",c),t.event.bind(e,"pointerdown",h),t.event.bind(e,"pointermove",p),t.event.bind(e,"pointerup",f)):window.MSPointerEvent&&(t.event.bind(window,"MSPointerDown",l),t.event.bind(window,"MSPointerUp",c),t.event.bind(e,"MSPointerDown",h),t.event.bind(e,"MSPointerMove",p),t.event.bind(e,"MSPointerUp",f)))}var a=e("../instances"),o=e("../update-geometry");t.exports=function(e,t,i){n(e,a.get(e),t,i)}},{"../instances":18,"../update-geometry":19}],17:[function(e,t,i){"use strict";var n=e("../lib/class"),a=e("../lib/helper"),o=e("./instances"),r=e("./update-geometry"),s=e("./handler/click-rail"),l=e("./handler/drag-scrollbar"),c=e("./handler/keyboard"),d=e("./handler/mouse-wheel"),u=e("./handler/native-scroll"),h=e("./handler/selection"),p=e("./handler/touch");t.exports=function(e,t){t="object"==typeof t?t:{},n.add(e,"ps-container");var i=o.add(e);i.settings=a.extend(i.settings,t),s(e),l(e),d(e),u(e),h(e),(a.env.supportsTouch||a.env.supportsIePointer)&&p(e,a.env.supportsTouch,a.env.supportsIePointer),i.settings.useKeyboard&&c(e),r(e)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(e,t,i){"use strict";function n(e){var t=this;t.settings=u.clone(l),t.containerWidth=null,t.containerHeight=null,t.contentWidth=null,t.contentHeight=null,t.isRtl="rtl"===s.css(e,"direction"),t.event=new c,t.ownerDocument=e.ownerDocument||document,t.scrollbarXRail=s.appendTo(s.e("div","ps-scrollbar-x-rail"),e),t.scrollbarX=s.appendTo(s.e("div","ps-scrollbar-x"),t.scrollbarXRail),t.scrollbarXActive=null,t.scrollbarXWidth=null,t.scrollbarXLeft=null,t.scrollbarXBottom=u.toInt(s.css(t.scrollbarXRail,"bottom")),t.isScrollbarXUsingBottom=t.scrollbarXBottom===t.scrollbarXBottom,t.scrollbarXTop=t.isScrollbarXUsingBottom?null:u.toInt(s.css(t.scrollbarXRail,"top")),t.railBorderXWidth=u.toInt(s.css(t.scrollbarXRail,"borderLeftWidth"))+u.toInt(s.css(t.scrollbarXRail,"borderRightWidth")),t.railXMarginWidth=u.toInt(s.css(t.scrollbarXRail,"marginLeft"))+u.toInt(s.css(t.scrollbarXRail,"marginRight")),t.railXWidth=null,t.scrollbarYRail=s.appendTo(s.e("div","ps-scrollbar-y-rail"),e),t.scrollbarY=s.appendTo(s.e("div","ps-scrollbar-y"),t.scrollbarYRail),t.scrollbarYActive=null,t.scrollbarYHeight=null,t.scrollbarYTop=null,t.scrollbarYRight=u.toInt(s.css(t.scrollbarYRail,"right")),t.isScrollbarYUsingRight=t.scrollbarYRight===t.scrollbarYRight,t.scrollbarYLeft=t.isScrollbarYUsingRight?null:u.toInt(s.css(t.scrollbarYRail,"left")),t.scrollbarYOuterWidth=t.isRtl?u.outerWidth(t.scrollbarY):null,t.railBorderYWidth=u.toInt(s.css(t.scrollbarYRail,"borderTopWidth"))+u.toInt(s.css(t.scrollbarYRail,"borderBottomWidth")),t.railYMarginHeight=u.toInt(s.css(t.scrollbarYRail,"marginTop"))+u.toInt(s.css(t.scrollbarYRail,"marginBottom")),t.railYHeight=null}function a(e){return void 0===e.dataset?e.getAttribute("data-ps-id"):e.dataset.psId}function o(e,t){void 0===e.dataset?e.setAttribute("data-ps-id",t):e.dataset.psId=t}function r(e){void 0===e.dataset?e.removeAttribute("data-ps-id"):delete e.dataset.psId}var s=e("../lib/dom"),l=e("./default-setting"),c=e("../lib/event-manager"),d=e("../lib/guid"),u=e("../lib/helper"),h={};i.add=function(e){var t=d();return o(e,t),h[t]=new n(e),h[t]},i.remove=function(e){delete h[a(e)],r(e)},i.get=function(e){return h[a(e)]}},{"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(e,t,i){"use strict";function n(e,t){return e.settings.minScrollbarLength&&(t=Math.max(t,e.settings.minScrollbarLength)),e.settings.maxScrollbarLength&&(t=Math.min(t,e.settings.maxScrollbarLength)),t}function a(e,t){var i={width:t.railXWidth};t.isRtl?i.left=e.scrollLeft+t.containerWidth-t.contentWidth:i.left=e.scrollLeft,t.isScrollbarXUsingBottom?i.bottom=t.scrollbarXBottom-e.scrollTop:i.top=t.scrollbarXTop+e.scrollTop,r.css(t.scrollbarXRail,i);var n={top:e.scrollTop,height:t.railYHeight};t.isScrollbarYUsingRight?t.isRtl?n.right=t.contentWidth-e.scrollLeft-t.scrollbarYRight-t.scrollbarYOuterWidth:n.right=t.scrollbarYRight-e.scrollLeft:t.isRtl?n.left=e.scrollLeft+2*t.containerWidth-t.contentWidth-t.scrollbarYLeft-t.scrollbarYOuterWidth:n.left=t.scrollbarYLeft+e.scrollLeft,r.css(t.scrollbarYRail,n),r.css(t.scrollbarX,{left:t.scrollbarXLeft,width:t.scrollbarXWidth-t.railBorderXWidth}),r.css(t.scrollbarY,{top:t.scrollbarYTop,height:t.scrollbarYHeight-t.railBorderYWidth})}var o=e("../lib/class"),r=e("../lib/dom"),s=e("../lib/helper"),l=e("./instances");t.exports=function(e){var t=l.get(e);t.containerWidth=e.clientWidth,t.containerHeight=e.clientHeight,t.contentWidth=e.scrollWidth,t.contentHeight=e.scrollHeight,e.contains(t.scrollbarXRail)||r.appendTo(t.scrollbarXRail,e),e.contains(t.scrollbarYRail)||r.appendTo(t.scrollbarYRail,e),!t.settings.suppressScrollX&&t.containerWidth+t.settings.scrollXMarginOffset<t.contentWidth?(t.scrollbarXActive=!0,t.railXWidth=t.containerWidth-t.railXMarginWidth,t.scrollbarXWidth=n(t,s.toInt(t.railXWidth*t.containerWidth/t.contentWidth)),t.scrollbarXLeft=s.toInt(e.scrollLeft*(t.railXWidth-t.scrollbarXWidth)/(t.contentWidth-t.containerWidth))):(t.scrollbarXActive=!1,t.scrollbarXWidth=0,t.scrollbarXLeft=0,e.scrollLeft=0),!t.settings.suppressScrollY&&t.containerHeight+t.settings.scrollYMarginOffset<t.contentHeight?(t.scrollbarYActive=!0,t.railYHeight=t.containerHeight-t.railYMarginHeight,t.scrollbarYHeight=n(t,s.toInt(t.railYHeight*t.containerHeight/t.contentHeight)),t.scrollbarYTop=s.toInt(e.scrollTop*(t.railYHeight-t.scrollbarYHeight)/(t.contentHeight-t.containerHeight))):(t.scrollbarYActive=!1,t.scrollbarYHeight=0,t.scrollbarYTop=0,e.scrollTop=0),t.scrollbarXLeft>=t.railXWidth-t.scrollbarXWidth&&(t.scrollbarXLeft=t.railXWidth-t.scrollbarXWidth),t.scrollbarYTop>=t.railYHeight-t.scrollbarYHeight&&(t.scrollbarYTop=t.railYHeight-t.scrollbarYHeight),a(e,t),o[t.scrollbarXActive?"add":"remove"](e,"ps-active-x"),o[t.scrollbarYActive?"add":"remove"](e,"ps-active-y")}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18}],20:[function(e,t,i){"use strict";var n=e("../lib/dom"),a=e("./instances"),o=e("./update-geometry");t.exports=function(e){var t=a.get(e);n.css(t.scrollbarXRail,"display","none"),n.css(t.scrollbarYRail,"display","none"),o(e),n.css(t.scrollbarXRail,"display","block"),n.css(t.scrollbarYRail,"display","block")}},{"../lib/dom":3,"./instances":18,"./update-geometry":19}]},{},[1]),define("WoltLabSuite/Core/Date/Util",["Language"],function(e){"use strict";return{formatDate:function(t){return this.format(t,e.get("wcf.date.dateFormat"))},formatTime:function(t){return this.format(t,e.get("wcf.date.timeFormat"))},formatDateTime:function(t){return this.format(t,e.get("wcf.date.dateTimeFormat").replace(/%date%/,e.get("wcf.date.dateFormat")).replace(/%time%/,e.get("wcf.date.timeFormat")))},format:function(t,i){var n,a="";"c"===i&&(i="Y-m-dTH:i:sP");for(var o=0,r=i.length;o<r;o++){switch(i[o]){case"s":n=("0"+t.getSeconds().toString()).slice(-2);break;case"i":n=t.getMinutes(),n<10&&(n="0"+n);break;case"a":n=t.getHours()>11?"pm":"am";break;case"g":n=t.getHours(),0===n?n=12:n>12&&(n-=12);break;case"h":n=t.getHours(),0===n?n=12:n>12&&(n-=12),n=("0"+n.toString()).slice(-2);break;case"A":n=t.getHours()>11?"PM":"AM";break;case"G":n=t.getHours();break;case"H":n=t.getHours(),n=("0"+n.toString()).slice(-2);break;case"d":n=t.getDate(),n=("0"+n.toString()).slice(-2);break;case"j":n=t.getDate();break;case"l":n=e.get("__days")[t.getDay()];break;case"D":n=e.get("__daysShort")[t.getDay()];break;case"S":n="";break;case"m":n=t.getMonth()+1,n=("0"+n.toString()).slice(-2);break;case"n":n=t.getMonth()+1;break;case"F":n=e.get("__months")[t.getMonth()];break;case"M":n=e.get("__monthsShort")[t.getMonth()];break;case"y":n=t.getYear().toString().replace(/^\d{2}/,"");break;case"Y":n=t.getFullYear();break;case"P":var s=t.getTimezoneOffset();n=s>0?"-":"+",s=Math.abs(s),n+=("0"+(~~(s/60)).toString()).slice(-2),n+=":",n+=("0"+(s%60).toString()).slice(-2);break;case"r":n=t.toString();break;case"U":n=Math.round(t.getTime()/1e3);break;case"\\":n="",o+1<r&&(n=i[++o]);break;default:n=i[o]}a+=n}return a},gmdate:function(e){return e instanceof Date||(e=new Date),Math.round(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDay(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds())/1e3)},getTimeElement:function(t){var i=elCreate("time");i.className="datetime";var n=this.formatDate(t),a=this.formatTime(t);return elAttr(i,"datetime",this.format(t,"c")),elData(i,"timestamp",(t.getTime()-t.getMilliseconds())/1e3),elData(i,"date",n),elData(i,"time",a),elData(i,"offset",60*t.getTimezoneOffset()),t.getTime()>Date.now()&&(elData(i,"is-future-date","true"),i.textContent=e.get("wcf.date.dateTimeFormat").replace("%time%",a).replace("%date%",n)),i},getTimezoneDate:function(e,t){var i=new Date(e),n=6e4*i.getTimezoneOffset();return new Date(e+n+t)}}}),define("WoltLabSuite/Core/Timer/Repeating",[],function(){"use strict";function e(e,t){if("function"!=typeof e)throw new TypeError("Expected a valid callback as first argument.");if(t<0||t>864e5)throw new RangeError("Invalid delta "+t+". Delta must be in the interval [0, 86400000].");this._callback=e.bind(void 0,this),this._delta=t,this._timer=void 0,this.restart()}return e.prototype={restart:function(){this.stop(),this._timer=setInterval(this._callback,this._delta)},stop:function(){void 0!==this._timer&&(clearInterval(this._timer),this._timer=void 0)},setDelta:function(e){this._delta=e,this.restart()}},e}),define("WoltLabSuite/Core/Date/Time/Relative",["Dom/ChangeListener","Language","WoltLabSuite/Core/Date/Util","WoltLabSuite/Core/Timer/Repeating"],function(e,t,i,n){"use strict";var a=elByTag("time"),o=!0,r=!1,s=null;return{setup:function(){new n(this._refresh.bind(this),6e4),e.add("WoltLabSuite/Core/Date/Time/Relative",this._refresh.bind(this)),document.addEventListener("visibilitychange",this._onVisibilityChange.bind(this))},_onVisibilityChange:function(){document.hidden?(o=!1,r=!1):(o=!0,r&&(this._refresh(),r=!1))},_refresh:function(){if(!o)return void(r||(r=!0));var e=new Date,n=(e.getTime()-e.getMilliseconds())/1e3;null===s&&(s=n-window.TIME_NOW);for(var l=0,c=a.length;l<c;l++){var d=a[l];if(d.classList.contains("datetime")&&!elData(d,"is-future-date")){var u=~~elData(d,"timestamp")+s,h=elData(d,"date"),p=elData(d,"time"),f=elData(d,"offset");if(elAttr(d,"title")||elAttr(d,"title",t.get("wcf.date.dateTimeFormat").replace(/%date%/,h).replace(/%time%/,p)),u>=n||n<u+60)d.textContent=t.get("wcf.date.relative.now");else if(n<u+3540){var m=Math.max(Math.round((n-u)/60),1);d.textContent=t.get("wcf.date.relative.minutes",{minutes:m})}else if(n<u+86400){var g=Math.round((n-u)/3600);d.textContent=t.get("wcf.date.relative.hours",{hours:g})}else if(n<u+518400){var v=new Date(e.getFullYear(),e.getMonth(),e.getDate()),_=Math.ceil((v/1e3-u)/86400),b=i.getTimezoneDate(1e3*u,1e3*f),w=b.getDay(),y=t.get("__days")[w];d.textContent=t.get("wcf.date.relative.pastDays",{days:_,day:y,time:p})}else d.textContent=t.get("wcf.date.shortDateTimeFormat").replace(/%date%/,h).replace(/%time%/,p)}}}}}),define("WoltLabSuite/Core/Ui/Page/Menu/Abstract",["Core","Environment","EventHandler","Language","ObjectMap","Dom/Traverse","Dom/Util","Ui/Screen"],function(e,t,i,n,a,o,r,s){"use strict";function l(e,t,i){this.init(e,t,i)}var c=elById("pageContainer"),d="";return l.prototype={init:function(e,n,o){if("packageInstallationSetup"!==elData(document.body,"template")){this._activeList=[],this._depth=0,this._enabled=!0,this._eventIdentifier=e,this._items=new a,this._menu=elById(n),this._removeActiveList=!1;var s=this.open.bind(this);this._button=elBySel(o),this._button.addEventListener(WCF_CLICK_EVENT,s),this._initItems(),this._initHeader(),i.add(this._eventIdentifier,"open",s),i.add(this._eventIdentifier,"close",this.close.bind(this)),i.add(this._eventIdentifier,"updateButtonState",this._updateButtonState.bind(this));var l,c=elByClass("menuOverlayItemList",this._menu);this._menu.addEventListener("animationend",function(){if(!this._menu.classList.contains("open"))for(var e=0,t=c.length;e<t;e++)l=c[e],l.classList.remove("active"),l.classList.remove("hidden")}.bind(this)),this._menu.children[0].addEventListener("transitionend",function(){if(this._menu.classList.add("allowScroll"),this._removeActiveList){this._removeActiveList=!1;var e=this._activeList.pop();e&&e.classList.remove("activeList")}}.bind(this));var d=elCreate("div");d.className="menuOverlayMobileBackdrop",d.addEventListener(WCF_CLICK_EVENT,this.close.bind(this)),r.insertAfter(d,this._menu),this._updateButtonState(),"android"===t.platform()&&this._initializeAndroid()}},open:function(e){return!!this._enabled&&(e instanceof Event&&e.preventDefault(),this._menu.classList.add("open"),this._menu.classList.add("allowScroll"),this._menu.children[0].classList.add("activeList"),s.scrollDisable(),c.classList.add("menuOverlay-"+this._menu.id),document.documentElement.classList.add("pageOverlayActive"),!0)},close:function(e){return e instanceof Event&&e.preventDefault(),!!this._menu.classList.contains("open")&&(this._menu.classList.remove("open"),s.scrollEnable(),c.classList.remove("menuOverlay-"+this._menu.id),document.documentElement.classList.remove("pageOverlayActive"),!0)},enable:function(){this._enabled=!0},disable:function(){this._enabled=!1,this.close(!0)},_initializeAndroid:function(){var t,i,n;switch(this._menu.id){case"pageUserMenuMobile":t="right";break;case"pageMainMenuMobile":t="left";break;default:return}i=this._menu.nextElementSibling,n=null,document.addEventListener("touchstart",function(i){var a,o,r,s;if(a=i.touches,o=this._menu.classList.contains("open"),"left"===t?(r=!o&&a[0].clientX<20,s=o&&Math.abs(this._menu.offsetWidth-a[0].clientX)<20):"right"===t&&(r=o&&Math.abs(document.body.clientWidth-this._menu.offsetWidth-a[0].clientX)<20,s=!o&&document.body.clientWidth-a[0].clientX<20),a.length>1)return void(d&&e.triggerEvent(document,"touchend"));if(!d&&(r||s)){if(document.documentElement.classList.contains("pageOverlayActive")){for(var l=!1,u=0;u<c.classList.length;u++)c.classList[u]==="menuOverlay-"+this._menu.id&&(l=!0);if(!l)return}document.documentElement.classList.contains("redactorActive")||(n={x:a[0].clientX,y:a[0].clientY},r&&(d="left"),s&&(d="right"))}}.bind(this)),document.addEventListener("touchend",function(e){if(d&&null!==n){if(!this._menu.classList.contains("open"))return n=null,void(d="");var a;a=e?e.changedTouches[0].clientX:n.x,this._menu.classList.add("androidMenuTouchEnd"),this._menu.style.removeProperty("transform"),i.style.removeProperty(t),this._menu.addEventListener("transitionend",function(){this._menu.classList.remove("androidMenuTouchEnd")}.bind(this),{once:!0}),"left"===t?("left"===d&&a<n.x+100&&this.close(),"right"===d&&a<n.x-100&&this.close()):"right"===t&&("left"===d&&a>n.x+100&&this.close(),"right"===d&&a>n.x-100&&this.close()),n=null,d=""}}.bind(this)),document.addEventListener("touchmove",function(e){if(d&&null!==n){var a=e.touches,o=!1,r=!1;"left"===d&&(o=a[0].clientX>n.x+5),"right"===d&&(o=a[0].clientX<n.x-5),r=Math.abs(a[0].clientY-n.y)>20;var s=this._menu.classList.contains("open");if(s||!o||r||(this.open(),s=!0),s){var l=a[0].clientX;"right"===t&&(l=document.body.clientWidth-l),l>this._menu.offsetWidth&&(l=this._menu.offsetWidth),l<0&&(l=0),this._menu.style.setProperty("transform","translateX("+("left"===t?1:-1)*(l-this._menu.offsetWidth)+"px)"),i.style.setProperty(t,Math.min(this._menu.offsetWidth,l)+"px")}}}.bind(this))},_initItems:function(){elBySelAll(".menuOverlayItemLink",this._menu,this._initItem.bind(this))},_initItem:function(e){var t=e.parentNode,n=elData(t,"more");if(n)return void e.addEventListener(WCF_CLICK_EVENT,function(a){a.preventDefault(),a.stopPropagation(),i.fire(this._eventIdentifier,"more",{handler:this,identifier:n,item:e,parent:t})}.bind(this));var a,r=e.nextElementSibling;if(null!==r)if("OL"!==r.nodeName&&r.classList.contains("menuOverlayItemLinkIcon"))for(a=elCreate("span"),a.className="menuOverlayItemWrapper",t.insertBefore(a,e),a.appendChild(e);a.nextElementSibling;)a.appendChild(a.nextElementSibling);else{var s="#"!==elAttr(e,"href"),l=t.parentNode,c=elData(r,"title");this._items.set(e,{itemList:r,parentItemList:l}),""===c&&(c=o.childByClass(e,"menuOverlayItemTitle").textContent,elData(r,"title",c));var d=this._showItemList.bind(this,e);if(s){a=elCreate("span"),a.className="menuOverlayItemWrapper",t.insertBefore(a,e),a.appendChild(e);var u=elCreate("a");elAttr(u,"href","#"),u.className="menuOverlayItemLinkIcon"+(e.classList.contains("active")?" active":""),u.innerHTML='<span class="icon icon24 fa-angle-right"></span>',u.addEventListener(WCF_CLICK_EVENT,d),a.appendChild(u)}else e.classList.add("menuOverlayItemLinkMore"),e.addEventListener(WCF_CLICK_EVENT,d);var h=elCreate("li");h.className="menuOverlayHeader",a=elCreate("span"),a.className="menuOverlayItemWrapper";var p=elCreate("a");elAttr(p,"href","#"),p.className="menuOverlayItemLink menuOverlayBackLink",p.textContent=elData(l,"title"),p.addEventListener(WCF_CLICK_EVENT,this._hideItemList.bind(this,e));var f=elCreate("a");if(elAttr(f,"href","#"),f.className="menuOverlayItemLinkIcon",f.innerHTML='<span class="icon icon24 fa-times"></span>',f.addEventListener(WCF_CLICK_EVENT,this.close.bind(this)),a.appendChild(p),a.appendChild(f),h.appendChild(a),r.insertBefore(h,r.firstElementChild),!h.nextElementSibling.classList.contains("menuOverlayTitle")){var m=elCreate("li");m.className="menuOverlayTitle";var g=elCreate("span");g.textContent=c,m.appendChild(g),r.insertBefore(m,h.nextElementSibling)}}},_initHeader:function(){var e=elCreate("li");e.className="menuOverlayHeader";var t=elCreate("span");t.className="menuOverlayItemWrapper",e.appendChild(t);var i=elCreate("span");i.className="menuOverlayLogoWrapper",t.appendChild(i);var n=elCreate("span");n.className="menuOverlayLogo",n.style.setProperty("background-image",'url("'+elData(this._menu,"page-logo")+'")',""),i.appendChild(n);var a=elCreate("a");elAttr(a,"href","#"),a.className="menuOverlayItemLinkIcon",a.innerHTML='<span class="icon icon24 fa-times"></span>',a.addEventListener(WCF_CLICK_EVENT,this.close.bind(this)),t.appendChild(a);var r=o.childByClass(this._menu,"menuOverlayItemList");r.insertBefore(e,r.firstElementChild)},_hideItemList:function(e,t){t instanceof Event&&t.preventDefault(),this._menu.classList.remove("allowScroll"),this._removeActiveList=!0,this._items.get(e).parentItemList.classList.remove("hidden"),this._updateDepth(!1)},_showItemList:function(e,t){t instanceof Event&&t.preventDefault();var n=this._items.get(e),a=elData(n.itemList,"load");if(a&&!elDataBool(e,"loaded")){var o=t.currentTarget.firstElementChild;return o.classList.contains("fa-angle-right")&&(o.classList.remove("fa-angle-right"),o.classList.add("fa-spinner")),void i.fire(this._eventIdentifier,"load_"+a)}this._menu.classList.remove("allowScroll"),n.itemList.classList.add("activeList"),n.parentItemList.classList.add("hidden"),this._activeList.push(n.itemList),this._updateDepth(!0)},_updateDepth:function(e){this._depth+=e?1:-1;var t=-100*this._depth;"rtl"===n.get("wcf.global.pageDirection")&&(t*=-1),this._menu.children[0].style.setProperty("transform","translateX("+t+"%)","")},_updateButtonState:function(){var e=!1;elBySelAll(".badgeUpdate",this._menu,function(t){~~t.textContent>0&&(e=!0)}),this._button.classList[e?"add":"remove"]("pageMenuMobileButtonHasContent")}},l}),define("WoltLabSuite/Core/Ui/Page/Menu/Main",["Core","Language","Dom/Traverse","./Abstract"],function(e,t,i,n){"use strict";function a(){this.init()}var o=null,r=null,s=null,l=null;return e.inherit(a,n,{init:function(){a._super.prototype.init.call(this,"com.woltlab.wcf.MainMenuMobile","pageMainMenuMobile","#pageHeader .mainMenu"),o=elById("pageMainMenuMobilePageOptionsContainer"),null!==o&&(s=i.childByClass(o,"menuOverlayItemList"),l=elBySel(".jsPageNavigationIcons"),elRemove(i.childByClass(s,"jsMenuOverlayItemPlaceholder")),s.addEventListener("click",function(e){e.target!==s&&null!==i.parentByClass(e.target,"menuOverlayItem",s)&&(this.close(),e.stopPropagation())}.bind(this))),elAttr(this._button,"aria-label",t.get("wcf.menu.page")),elAttr(this._button,"role","button")},open:function(e){if(!a._super.prototype.open.call(this,e))return!1;if(null===o)return!0;if(r=l.childElementCount>0){for(var t,i;l.childElementCount;)t=l.children[0],t.classList.add("menuOverlayItem"),i=t.children[0],i.classList.add("menuOverlayItemLink"),i.classList.add("box24"),i.children[1].classList.remove("invisible"),i.children[1].classList.add("menuOverlayItemTitle"),s.appendChild(t);elShow(o)}else elHide(o);return!0},close:function(e){if(!a._super.prototype.close.call(this,e))return!1;if(r){elHide(o);for(var t,n,c=i.childByClass(s,"menuOverlayTitle");t=c.nextElementSibling;)t.classList.remove("menuOverlayItem"),n=t.children[0],n.classList.remove("menuOverlayItemLink"),n.classList.remove("box24"),n.children[1].classList.add("invisible"),n.children[1].classList.remove("menuOverlayItemTitle"),l.appendChild(t)}return!0}}),a}),define("WoltLabSuite/Core/Ui/Page/Menu/User",["Core","EventHandler","Language","./Abstract"],function(e,t,i,n){"use strict";function a(){this.init()}return e.inherit(a,n,{init:function(){var e=elBySel("#pageUserMenuMobile > .menuOverlayItemList");if(1===e.childElementCount&&e.children[0].classList.contains("menuOverlayTitle"))return void elBySel("#pageHeader .userPanel").classList.add("hideUserPanel");a._super.prototype.init.call(this,"com.woltlab.wcf.UserMenuMobile","pageUserMenuMobile","#pageHeader .userPanel"),t.add("com.woltlab.wcf.userMenu","updateBadge",function(e){elBySelAll(".menuOverlayItemBadge",this._menu,function(t){if(elData(t,"badge-identifier")===e.identifier){var i=elBySel(".badge",t)
-;e.count?(null===i&&(i=elCreate("span"),i.className="badge badgeUpdate",t.appendChild(i)),i.textContent=e.count):null!==i&&elRemove(i),this._updateButtonState()}}.bind(this))}.bind(this)),elAttr(this._button,"aria-label",i.get("wcf.menu.user")),elAttr(this._button,"role","button")},close:function(e){var t=WCF.Dropdown.Interactive.Handler.getOpenDropdown();t?(e.preventDefault(),e.stopPropagation(),t.close()):a._super.prototype.close.call(this,e)}}),a}),define("WoltLabSuite/Core/Ui/Dropdown/Reusable",["Dictionary","Ui/SimpleDropdown"],function(e,t){"use strict";function i(e){if(!n.has(e))throw new Error("Unknown dropdown identifier '"+e+"'");return n.get(e)}var n=new e,a=0;return{init:function(e,i){if(!n.has(e)){var o=elCreate("div");o.id="reusableDropdownGhost"+a++,t.initFragment(o,i),n.set(e,o.id)}},getDropdownMenu:function(e){return t.getDropdownMenu(i(e))},registerCallback:function(e,n){t.registerCallback(i(e),n)},toggleDropdown:function(e,n){t.toggleDropdown(i(e),n)}}}),define("WoltLabSuite/Core/Ui/Mobile",["Core","Environment","EventHandler","Language","List","Dom/ChangeListener","Dom/Traverse","Ui/Alignment","Ui/CloseOverlay","Ui/Screen","./Page/Menu/Main","./Page/Menu/User","WoltLabSuite/Core/Ui/Dropdown/Reusable"],function(e,t,i,n,a,o,r,s,l,c,d,u,h){"use strict";var p=elByClass("buttonGroupNavigation"),f=null,m=null,g=null,v=!1,_=new a,b=null,w=elByClass("message"),y={},C=null,E=null,L=null,S=[],D=!1;return{setup:function(i){y=e.extend({enableMobileMenu:!0},i),b=elById("main"),elBySelAll(".sidebar",void 0,function(e){S.push(e)}),t.touch()&&document.documentElement.classList.add("touch"),"desktop"!==t.platform()&&document.documentElement.classList.add("mobile");var n=elBySel(".messageGroupList");n&&(L=elByClass("messageGroup",n)),c.on("screen-md-down",{match:this.enable.bind(this),unmatch:this.disable.bind(this),setup:this._init.bind(this)}),c.on("screen-sm-down",{match:this.enableShadow.bind(this),unmatch:this.disableShadow.bind(this),setup:this.enableShadow.bind(this)}),c.on("screen-xs",{match:this._enableSidebarXS.bind(this),unmatch:this._disableSidebarXS.bind(this),setup:this._setupSidebarXS.bind(this)})},enable:function(){v=!0,y.enableMobileMenu&&(C.enable(),E.enable())},enableShadow:function(){L&&this.rebuildShadow(L,".messageGroupLink")},disable:function(){v=!1,y.enableMobileMenu&&(C.disable(),E.disable())},disableShadow:function(){L&&this.removeShadow(L),m&&f()},_init:function(){v=!0,this._initSearchBar(),this._initButtonGroupNavigation(),this._initMessages(),this._initMobileMenu(),l.add("WoltLabSuite/Core/Ui/Mobile",this._closeAllMenus.bind(this)),o.add("WoltLabSuite/Core/Ui/Mobile",function(){this._initButtonGroupNavigation(),this._initMessages()}.bind(this))},_initSearchBar:function(){var e=elById("pageHeaderSearch"),n=elById("pageHeaderSearchInput"),a=null;i.add("com.woltlab.wcf.MainMenuMobile","more",function(i){"com.woltlab.wcf.search"===i.identifier&&(i.handler.close(!0),"ios"===t.platform()&&(a=document.body.scrollTop,c.scrollDisable()),e.style.setProperty("top",elById("pageHeader").offsetHeight+"px",""),e.classList.add("open"),n.focus(),"ios"===t.platform()&&(document.body.scrollTop=0))}),b.addEventListener(WCF_CLICK_EVENT,function(){e&&e.classList.remove("open"),"ios"===t.platform()&&null!==a&&(c.scrollEnable(),document.body.scrollTop=a,a=null)})},_initButtonGroupNavigation:function(){for(var e=0,t=p.length;e<t;e++){var i=p[e];if(!i.classList.contains("jsMobileButtonGroupNavigation")){i.classList.add("jsMobileButtonGroupNavigation");var n=elBySel(".buttonList",i);if(0!==n.childElementCount){i.parentNode.classList.add("hasMobileNavigation");var a=elCreate("a");a.className="dropdownLabel";var o=elCreate("span");o.className="icon icon24 fa-ellipsis-v",a.appendChild(o),function(e,t,i){t.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),t.stopPropagation(),e.classList.toggle("open")}),i.addEventListener(WCF_CLICK_EVENT,function(t){t.stopPropagation(),e.classList.remove("open")})}(i,a,n),i.insertBefore(a,i.firstChild)}}}},_initMessages:function(){Array.prototype.forEach.call(w,function(e){if(!_.has(e)){var t=elBySel(".jsMobileNavigation",e);if(t){t.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation(),window.setTimeout(function(){t.classList.remove("open")},10)});var i=elBySel(".messageQuickOptions",e);i&&t.childElementCount&&(i.classList.add("active"),i.addEventListener(WCF_CLICK_EVENT,function(n){v&&"LABEL"!==n.target.nodeName&&"INPUT"!==n.target.nodeName&&(n.preventDefault(),n.stopPropagation(),this._toggleMobileNavigation(e,i,t))}.bind(this)))}_.add(e)}}.bind(this))},_initMobileMenu:function(){y.enableMobileMenu&&(C=new d,E=new u)},_closeAllMenus:function(){elBySelAll(".jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open",null,function(e){e.classList.remove("open")}),v&&m&&f()},rebuildShadow:function(e,t){for(var i,n,a,o=0,s=e.length;o<s;o++)i=e[o],n=i.parentNode,null===(a=r.childByClass(n,"mobileLinkShadow"))&&elBySel(t,i).href&&(a=elCreate("a"),a.className="mobileLinkShadow",a.href=elBySel(t,i).href,n.appendChild(a),n.classList.add("mobileLinkShadowContainer"))},removeShadow:function(e){for(var t,i,n,a=0,o=e.length;a<o;a++)t=e[a],i=t.parentNode,i.classList.contains("mobileLinkShadowContainer")&&(n=r.childByClass(i,"mobileLinkShadow"),null!==n&&elRemove(n),i.classList.remove("mobileLinkShadowContainer"))},_enableSidebarXS:function(){D=!0},_disableSidebarXS:function(){D=!1,S.forEach(function(e){e.classList.remove("open")})},_setupSidebarXS:function(){S.forEach(function(e){e.addEventListener("mousedown",function(t){D&&t.target===e&&(t.preventDefault(),e.classList.toggle("open"))})}),D=!0},_toggleMobileNavigation:function(e,t,i){if(null===m)m=elCreate("ul"),m.className="dropdownMenu",h.init("com.woltlab.wcf.jsMobileNavigation",m),f=function(){m.classList.remove("dropdownOpen")};else if(m.classList.contains("dropdownOpen")&&(f(),g===e))return;m.innerHTML="",l.execute(),this._rebuildMobileNavigation(i);var n=i.previousElementSibling;if(n&&n.classList.contains("messageFooterButtonsExtra")){var a=elCreate("li");a.className="dropdownDivider",m.appendChild(a),this._rebuildMobileNavigation(n)}s.set(m,t,{horizontal:"right",allowFlip:"vertical"}),m.classList.add("dropdownOpen"),g=e},_rebuildMobileNavigation:function(t){elBySelAll(".button",t,function(t){var i=elCreate("li");t.classList.contains("active")&&(i.className="active"),i.innerHTML='<a href="#">'+elBySel("span:not(.icon)",t).textContent+"</a>",i.children[0].addEventListener(WCF_CLICK_EVENT,function(i){i.preventDefault(),i.stopPropagation(),"A"===t.nodeName?t.click():e.triggerEvent(t,WCF_CLICK_EVENT),f()}),m.appendChild(i)})}}}),define("WoltLabSuite/Core/Ui/TabMenu/Simple",["Dictionary","EventHandler","Dom/Traverse","Dom/Util"],function(e,t,i,n){"use strict";function a(t){this._container=t,this._containers=new e,this._isLegacy=null,this._store=null,this._tabs=new e}return a.prototype={validate:function(){if(!this._container.classList.contains("tabMenuContainer"))return!1;var e=i.childByTag(this._container,"NAV");if(null===e)return!1;var t=elByTag("li",e);if(0===t.length)return!1;var a,o,r,s,l=i.childrenByTag(this._container,"DIV");for(r=0,s=l.length;r<s;r++)a=l[r],o=elData(a,"name"),o||(o=n.identify(a)),elData(a,"name",o),this._containers.set(o,a);var c,d=this._container.id;for(r=0,s=t.length;r<s;r++)if(c=t[r],o=this._getTabName(c)){if(this._tabs.has(o))throw new Error("Tab names must be unique, li[data-name='"+o+"'] (tab menu id: '"+d+"') exists more than once.");if(void 0===(a=this._containers.get(o)))throw new Error("Expected content element for li[data-name='"+o+"'] (tab menu id: '"+d+"').");if(a.parentNode!==this._container)throw new Error("Expected content element '"+o+"' (tab menu id: '"+d+"') to be a direct children.");if(1!==c.childElementCount||"A"!==c.children[0].nodeName)throw new Error("Expected exactly one <a> as children for li[data-name='"+o+"'] (tab menu id: '"+d+"').");this._tabs.set(o,c)}if(!this._tabs.size)throw new Error("Expected at least one tab (tab menu id: '"+d+"').");return this._isLegacy&&(elData(this._container,"is-legacy",!0),this._tabs.forEach(function(e,t){elAttr(e,"aria-controls",t)})),!0},init:function(e){e=e||null,this._tabs.forEach(function(t){e&&e.get(elData(t,"name"))===t||t.children[0].addEventListener(WCF_CLICK_EVENT,this._onClick.bind(this))}.bind(this));var t=null;if(!e){var i=a.getIdentifierFromHash(),n=null;if(""!==i&&(n=this._tabs.get(i))&&this._container.parentNode.classList.contains("tabMenuContainer")&&(t=this._container),!n){var o=elData(this._container,"preselect")||elData(this._container,"active");"true"!==o&&o||(o=!0),!0===o?this._tabs.forEach(function(e){n||e.previousElementSibling||(n=e)}):"false"!==o&&(n=this._tabs.get(o))}n&&(this._containers.forEach(function(e){e.classList.add("hidden")}),this.select(null,n,!0));var r=elData(this._container,"store");if(r){var s=elCreate("input");s.type="hidden",s.name=r,s.value=elData(this.getActiveTab(),"name"),this._container.appendChild(s),this._store=s}}return t},select:function(e,i,n){if(!(i=i||this._tabs.get(e))){if(~~e==e){e=~~e;var o=0;this._tabs.forEach(function(t){o===e&&(i=t),o++})}if(!i)throw new Error("Expected a valid tab name, '"+e+"' given (tab menu id: '"+this._container.id+"').")}e=e||elData(i,"name");var r=this.getActiveTab(),s=null;if(r){var l=elData(r,"name");if(l===e)return;n||t.fire("com.woltlab.wcf.simpleTabMenu_"+this._container.id,"beforeSelect",{tab:r,tabName:l}),r.classList.remove("active"),s=this._containers.get(elData(r,"name")),s.classList.remove("active"),s.classList.add("hidden"),this._isLegacy&&(r.classList.remove("ui-state-active"),s.classList.remove("ui-state-active"))}i.classList.add("active");var c=this._containers.get(e);if(c.classList.add("active"),c.classList.remove("hidden"),this._isLegacy&&(i.classList.add("ui-state-active"),c.classList.add("ui-state-active")),this._store&&(this._store.value=e),!n){t.fire("com.woltlab.wcf.simpleTabMenu_"+this._container.id,"select",{active:i,activeName:e,previous:r,previousName:r?elData(r,"name"):null});var d=this._isLegacy&&"function"==typeof window.jQuery?window.jQuery:null;d&&d(this._container).trigger("wcftabsbeforeactivate",{newTab:d(i),oldTab:d(r),newPanel:d(c),oldPanel:d(s)});var u=window.location.href.replace(/#+[^#]*$/,"");a.getIdentifierFromHash()===e?u+=window.location.hash:u+="#"+e,window.history.replaceState(void 0,void 0,u)}require(["WoltLabSuite/Core/Ui/TabMenu"],function(e){e.scrollToTab(i)})},rebuild:function(){var t=new e;t.merge(this._tabs),this.validate(),this.init(t)},hasTab:function(e){return this._tabs.has(e)},_onClick:function(e){e.preventDefault(),this.select(null,e.currentTarget.parentNode)},_getTabName:function(e){var t=elData(e,"name");return t||1===e.childElementCount&&"A"===e.children[0].nodeName&&e.children[0].href.match(/#([^#]+)$/)&&(t=RegExp.$1,null===elById(t)?t=null:(this._isLegacy=!0,elData(e,"name",t))),t},getActiveTab:function(){return elBySel("#"+this._container.id+" > nav > ul > li.active")},getContainers:function(){return this._containers},getTabs:function(){return this._tabs}},a.getIdentifierFromHash=function(){return window.location.hash.match(/^#+([^\/]+)+(?:\/.+)?/)?RegExp.$1:""},a}),define("WoltLabSuite/Core/Ui/TabMenu",["Dictionary","EventHandler","Dom/ChangeListener","Dom/Util","Ui/CloseOverlay","Ui/Screen","./TabMenu/Simple"],function(e,t,i,n,a,o,r){"use strict";var s=null,l=!1,c=new e;return{setup:function(){this._init(),this._selectErroneousTabs(),i.add("WoltLabSuite/Core/Ui/TabMenu",this._init.bind(this)),a.add("WoltLabSuite/Core/Ui/TabMenu",function(){s&&(s.classList.remove("active"),s=null)}),o.on("screen-sm-down",{enable:this._scrollEnable.bind(this,!1),disable:this._scrollDisable.bind(this),setup:this._scrollEnable.bind(this,!0)}),window.addEventListener("hashchange",function(){var e=r.getIdentifierFromHash(),t=e?elById(e):null;null!==t&&t.classList.contains("tabMenuContent")&&c.forEach(function(t){t.hasTab(e)&&t.select(e)})});var e=r.getIdentifierFromHash();e&&window.setTimeout(function(){var t=elById(e);if(t&&t.classList.contains("tabMenuContent")){var i=window.scrollY||window.pageYOffset;if(i>0){var a=t.parentNode,o=a.offsetTop-50;if(o<0&&(o=0),i>o){var r=n.offset(a).top;r<=50?r=0:r-=50,window.scrollTo(0,r)}}}},100)},_init:function(){for(var e,t,i,a,o,l=elBySelAll(".tabMenuContainer:not(.staticTabMenuContainer)"),d=0,u=l.length;d<u;d++)e=l[d],t=n.identify(e),c.has(t)||(o=new r(e),o.validate()&&(a=o.init(),c.set(t,o),a instanceof Element&&(o=this.getTabMenu(a.parentNode.id),o.select(a.id,null,!0)),i=elBySel("#"+t+" > nav > ul"),function(e){e.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),t.stopPropagation(),t.target===e?(e.classList.add("active"),s=e):(e.classList.remove("active"),s=null)})}(i),elBySelAll(".tabMenu, .menu",e,function(e){var t=this._rebuildMenuOverflow.bind(this,e),i=null;elBySel("ul",e).addEventListener("scroll",function(){null!==i&&window.clearTimeout(i),i=window.setTimeout(t,10)})}.bind(this))))},_selectErroneousTabs:function(){c.forEach(function(e){var t=!1;e.getContainers().forEach(function(i){!t&&elByClass("formError",i).length&&(t=!0,e.select(i.id))})})},getTabMenu:function(e){return c.get(e)},_scrollEnable:function(e){l=!0,c.forEach(function(t){var i=t.getActiveTab();e?this._rebuildMenuOverflow(i.closest(".menu, .tabMenu")):this.scrollToTab(i)}.bind(this))},_scrollDisable:function(){l=!1},scrollToTab:function(e){if(l){var t=e.closest("ul"),i=t.clientWidth,n=t.scrollLeft,a=t.scrollWidth;if(i!==a){var o=e.offsetLeft,r=!1;o<n&&(r=!0);var s=!1;if(!r){var c=i-(o-n),d=e.clientWidth;null!==e.nextElementSibling&&(s=!0,d+=20),c<d&&(r=!0)}r&&this._scrollMenu(t,o,n,a,i,s)}}},_scrollMenu:function(e,t,i,n,a,o){o?t-=15:t>0&&(t-=15),t=t<0?0:Math.min(t,n-a),i!==t&&(e.classList.add("enableAnimation"),i<t?e.firstElementChild.style.setProperty("margin-left",i-t+"px",""):e.style.setProperty("padding-left",i-t+"px",""),setTimeout(function(){e.classList.remove("enableAnimation"),e.firstElementChild.style.removeProperty("margin-left"),e.style.removeProperty("padding-left"),e.scrollLeft=t},300))},_rebuildMenuOverflow:function(e){if(l){var t=e.clientWidth,i=elBySel("ul",e),n=i.scrollLeft,a=i.scrollWidth,o=n>0,r=elBySel(".tabMenuOverlayLeft",e);o?(null===r&&(r=elCreate("span"),r.className="tabMenuOverlayLeft icon icon24 fa-angle-left",r.addEventListener(WCF_CLICK_EVENT,function(){var e=i.clientWidth;this._scrollMenu(i,i.scrollLeft-~~(e/2),i.scrollLeft,i.scrollWidth,e,0)}.bind(this)),e.insertBefore(r,e.firstChild)),r.classList.add("active")):null!==r&&r.classList.remove("active");var s=t+n<a,c=elBySel(".tabMenuOverlayRight",e);s?(null===c&&(c=elCreate("span"),c.className="tabMenuOverlayRight icon icon24 fa-angle-right",c.addEventListener(WCF_CLICK_EVENT,function(){var e=i.clientWidth;this._scrollMenu(i,i.scrollLeft+~~(e/2),i.scrollLeft,i.scrollWidth,e,0)}.bind(this)),e.appendChild(c)),c.classList.add("active")):null!==c&&c.classList.remove("active")}}}}),define("WoltLabSuite/Core/Ui/FlexibleMenu",["Core","Dictionary","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/SimpleDropdown"],function(e,t,i,n,a,o){"use strict";var r=new t,s=new t,l=new t,c=new t;return{setup:function(){null!==elById("mainMenu")&&this.register("mainMenu");var e=elBySel(".navigationHeader");null!==e&&this.register(a.identify(e)),window.addEventListener("resize",this.rebuildAll.bind(this)),i.add("WoltLabSuite/Core/Ui/FlexibleMenu",this.registerTabMenus.bind(this))},register:function(e){var t=elById(e);if(null===t)throw"Expected a valid element id, '"+e+"' does not exist.";if(!r.has(e)){var i=n.childByTag(t,"UL");if(null===i)throw"Expected an <ul> element as child of container '"+e+"'.";r.set(e,t),c.set(e,i),this.rebuild(e)}},registerTabMenus:function(){for(var e=elBySelAll(".tabMenuContainer:not(.jsFlexibleMenuEnabled), .messageTabMenu:not(.jsFlexibleMenuEnabled)"),t=0,i=e.length;t<i;t++){var o=e[t],r=n.childByTag(o,"NAV");null!==r&&(o.classList.add("jsFlexibleMenuEnabled"),this.register(a.identify(r)))}},rebuildAll:function(){r.forEach(function(e,t){this.rebuild(t)}.bind(this))},rebuild:function(t){var i=r.get(t);if(void 0===i)throw"Expected a valid element id, '"+t+"' is unknown.";var d=window.getComputedStyle(i),u=i.parentNode.clientWidth;u-=a.styleAsInt(d,"margin-left"),u-=a.styleAsInt(d,"margin-right");var h=c.get(t),p=n.childrenByTag(h,"LI"),f=s.get(t),m=0;if(void 0!==f){for(var g=0,v=p.length;g<v;g++){var _=p[g];_.classList.contains("dropdown")||elShow(_)}null!==f.parentNode&&(m=a.outerWidth(f))}var b=h.scrollWidth-m,w=[];if(b>u)for(var g=p.length-1;g>=0;g--){var _=p[g];if(!(_.classList.contains("dropdown")||_.classList.contains("active")||_.classList.contains("ui-state-active"))&&(w.push(_),elHide(_),h.scrollWidth<u))break}if(w.length){var y;if(void 0===f){f=elCreate("li"),f.className="dropdown jsFlexibleMenuDropdown";var C=elCreate("a");C.className="icon icon16 fa-list",f.appendChild(C),y=elCreate("ul"),y.classList.add("dropdownMenu"),f.appendChild(y),s.set(t,f),l.set(t,y),o.init(C)}else y=l.get(t);null===f.parentNode&&h.appendChild(f);var E=document.createDocumentFragment(),L=this;w.forEach(function(i){var n=elCreate("li");n.innerHTML=i.innerHTML,n.addEventListener(WCF_CLICK_EVENT,function(n){n.preventDefault(),e.triggerEvent(elBySel("a",i),WCF_CLICK_EVENT),setTimeout(function(){L.rebuild(t)},59)}.bind(this)),E.appendChild(n)}),y.innerHTML="",y.appendChild(E)}else void 0!==f&&null!==f.parentNode&&elRemove(f)}}}),define("WoltLabSuite/Core/Ui/Tooltip",["Environment","Dom/ChangeListener","Ui/Alignment"],function(e,t,i){"use strict";var n=null,a=null,o=null,r=null,s=null,l=null;return{setup:function(){"desktop"===e.platform()&&(l=elCreate("div"),elAttr(l,"id","balloonTooltip"),l.classList.add("balloonTooltip"),l.addEventListener("transitionend",function(){l.classList.contains("active")||["bottom","left","right","top"].forEach(function(e){l.style.removeProperty(e)})}),s=elCreate("span"),elAttr(s,"id","balloonTooltipText"),l.appendChild(s),r=elCreate("span"),r.classList.add("elementPointer"),r.appendChild(elCreate("span")),l.appendChild(r),document.body.appendChild(l),o=elByClass("jsTooltip"),n=this._mouseEnter.bind(this),a=this._mouseLeave.bind(this),this.init(),t.add("WoltLabSuite/Core/Ui/Tooltip",this.init.bind(this)),window.addEventListener("scroll",this._mouseLeave.bind(this)))},init:function(){0!==o.length&&elBySelAll(".jsTooltip",void 0,function(e){e.classList.remove("jsTooltip");var t=elAttr(e,"title").trim();t.length&&(elData(e,"tooltip",t),e.removeAttribute("title"),e.addEventListener("mouseenter",n),e.addEventListener("mouseleave",a),e.addEventListener(WCF_CLICK_EVENT,a))})},_mouseEnter:function(e){var t=e.currentTarget,n=elAttr(t,"title");if(n="string"==typeof n?n.trim():"",""!==n&&(elData(t,"tooltip",n),t.removeAttribute("title")),n=elData(t,"tooltip"),l.style.removeProperty("top"),l.style.removeProperty("left"),!n.length)return void l.classList.remove("active");l.classList.add("active"),s.textContent=n,i.set(l,t,{horizontal:"center",verticalOffset:4,pointer:!0,pointerClassNames:["inverse"],vertical:"top"})},_mouseLeave:function(){l.classList.remove("active")}}}),define("WoltLabSuite/Core/Date/Picker",["DateUtil","Language","ObjectMap","Dom/ChangeListener","Ui/Alignment","WoltLabSuite/Core/Ui/CloseOverlay"],function(e,t,i,n,a,o){"use strict";var r=!1,s=0,l=new i,c=null,d=0,u=0,h=[],p=null,f=null,m=null,g=null,v=null,_=null,b=null,w=null,y=null,C=null,E={init:function(){this._setup();for(var t=elBySelAll('input[type="date"]:not(.inputDatePicker), input[type="datetime"]:not(.inputDatePicker)'),i=new Date,n=0,a=t.length;n<a;n++){var o=t[n];o.classList.add("inputDatePicker"),o.readOnly=!0;var r="datetime"===elAttr(o,"type"),s=r&&elDataBool(o,"time-only"),c=elDataBool(o,"disable-clear"),d=r&&elDataBool(o,"ignore-timezone"),u=o.classList.contains("birthday");elData(o,"is-date-time",r),elData(o,"is-time-only",s);var h=null,p=elAttr(o,"value"),f=/^\d+-\d+-\d+$/.test(p);if(elAttr(o,"value")){if(s){h=new Date;var m=p.split(":");h.setHours(m[0],m[1])}else{if(d||u||f){var g=new Date(p).getTimezoneOffset(),v=g>0?"-":"+";g=Math.abs(g);var _=Math.floor(g/60).toString(),b=(g%60).toString();v+=2===_.length?_:"0"+_,v+=":",v+=2===b.length?b:"0"+b,u||f?p+="T00:00:00"+v:p=p.replace(/[+-][0-9]{2}:[0-9]{2}$/,v)}h=new Date(p)}var w=h.getTime();if(isNaN(w))p="";else{elData(o,"value",w);p=e[s?"formatTime":"formatDate"+(r?"Time":"")](h)}}var y=0===p.length;if(u?(elData(o,"min-date","120"),elData(o,"max-date",(new Date).getFullYear()+"-12-31")):(o.min&&elData(o,"min-date",o.min),o.max&&elData(o,"max-date",o.max)),this._initDateRange(o,i,!0),this._initDateRange(o,i,!1),elData(o,"min-date")===elData(o,"max-date"))throw new Error("Minimum and maximum date cannot be the same (element id '"+o.id+"').");o.type="text",o.value=p,elData(o,"empty",y),elData(o,"placeholder")&&elAttr(o,"placeholder",elData(o,"placeholder"));var E=elCreate("input");E.id=o.id+"DatePicker",E.name=o.name,E.type="hidden",null!==h&&(E.value=s?e.format(h,"H:i"):d?e.format(h,"Y-m-dTH:i:s"):e.format(h,r?"c":"Y-m-d")),o.parentNode.insertBefore(E,o),o.removeAttribute("name"),o.addEventListener(WCF_CLICK_EVENT,C);var L=elCreate("div");L.className="inputAddon";var S=elCreate("a");S.className="inputSuffix button",S.addEventListener(WCF_CLICK_EVENT,C),L.appendChild(S);var D=elCreate("span");D.className="icon icon16 fa-calendar",S.appendChild(D),o.parentNode.insertBefore(L,o),L.insertBefore(o,S),c||(S=elCreate("a"),S.className="inputSuffix button",S.addEventListener(WCF_CLICK_EVENT,this.clear.bind(this,o)),y&&S.style.setProperty("visibility","hidden",""),L.appendChild(S),D=elCreate("span"),D.className="icon icon16 fa-times",S.appendChild(D));for(var x=!1,I=["tiny","short","medium","long"],T=0;T<4;T++)o.classList.contains(I[T])&&(x=!0);x||o.classList.add("short"),l.set(o,{clearButton:S,shadow:E,disableClear:c,isDateTime:r,isEmpty:y,isTimeOnly:s,ignoreTimezone:d,onClose:null})}},_initDateRange:function(e,t,i){var n="data-"+(i?"min":"max")+"-date",a=e.hasAttribute(n)?elAttr(e,n).trim():"";if(a.match(/^(\d{4})-(\d{2})-(\d{2})$/))a=new Date(a).getTime();else if("now"===a)a=t.getTime();else if(a.match(/^\d{1,3}$/)){var o=new Date(t.getTime());o.setFullYear(o.getFullYear()+~~a*(i?-1:1)),a=o.getTime()}else if(a.match(/^datePicker-(.+)$/)){if(a=RegExp.$1,null===elById(a))throw new Error("Reference date picker identified by '"+a+"' does not exists (element id: '"+e.id+"').")}else a=/^\d{4}\-\d{2}\-\d{2}T/.test(a)?new Date(a).getTime():new Date(i?1970:2038,0,1).getTime();elAttr(e,n,a)},_setup:function(){r||(r=!0,s=~~t.get("wcf.date.firstDayOfTheWeek"),C=this._open.bind(this),n.add("WoltLabSuite/Core/Date/Picker",this.init.bind(this)),o.add("WoltLabSuite/Core/Date/Picker",this._close.bind(this)))},_open:function(e){e.preventDefault(),e.stopPropagation(),this._createPicker();var t="INPUT"===e.currentTarget.nodeName?e.currentTarget:e.currentTarget.previousElementSibling;if(t!==c){c=t;var i,n=l.get(c),o=elData(c,"value");o?(i=new Date(+o),"Invalid Date"===i.toString()&&(i=new Date)):i=new Date,u=elData(c,"min-date"),u.match(/^datePicker-(.+)$/)&&(u=elData(elById(RegExp.$1),"value")),u=new Date(+u),d=elData(c,"max-date"),d.match(/^datePicker-(.+)$/)&&(d=elData(elById(RegExp.$1),"value")),d=new Date(+d),n.isDateTime?(f.value=i.getHours(),m.value=i.getMinutes(),y.classList.add("datePickerTime")):y.classList.remove("datePickerTime"),y.classList[n.isTimeOnly?"add":"remove"]("datePickerTimeOnly"),this._renderPicker(i.getDate(),i.getMonth(),i.getFullYear()),a.set(y,c)}},_close:function(){if(null!==y&&y.classList.contains("active")){y.classList.remove("active");var e=l.get(c);"function"==typeof e.onClose&&e.onClose(),c=null,u=0,d=0}},_renderPicker:function(e,t,i){this._renderGrid(e,t,i);for(var n="",a=u.getFullYear(),o=d.getFullYear();a<=o;a++)n+='<option value="'+a+'">'+a+"</option>";w.innerHTML=n,w.value=i,g.value=t,y.classList.add("active")},_renderGrid:function(e,t,i){var n,a,o=void 0!==e,r=void 0!==t;if(e=~~e||~~elData(p,"day"),t=~~t,i=~~i,r||i){var l=0!==i,c=document.createDocumentFragment();c.appendChild(p),r||(t=~~elData(p,"month")),i=i||~~elData(p,"year");var f=new Date(i+"-"+("0"+(t+1).toString()).slice(-2)+"-"+("0"+e.toString()).slice(-2));for(f<u?(i=u.getFullYear(),t=u.getMonth(),e=u.getDate(),g.value=t,w.value=i,l=!0):f>d&&(i=d.getFullYear(),t=d.getMonth(),e=d.getDate(),g.value=t,w.value=i,l=!0),f=new Date(i+"-"+("0"+(t+1).toString()).slice(-2)+"-01");f.getDay()!==s;)f.setDate(f.getDate()-1);elShow(h[35].parentNode);var m,C=new Date(u.getFullYear(),u.getMonth(),u.getDate());for(a=0;a<42;a++){if(35===a&&f.getMonth()!==t){elHide(h[35].parentNode);break}n=h[a],n.textContent=f.getDate(),m=f.getMonth()===t,m&&(f<C?m=!1:f>d&&(m=!1)),n.classList[m?"remove":"add"]("otherMonth"),f.setDate(f.getDate()+1)}if(elData(p,"month",t),elData(p,"year",i),y.insertBefore(c,b),!o&&(f=new Date(i,t,e),f.getDate()!==e)){for(;f.getMonth()!==t;)f.setDate(f.getDate()-1);e=f.getDate()}if(l){for(a=0;a<12;a++){var E=g.children[a];E.disabled=i===u.getFullYear()&&E.value<u.getMonth()||i===d.getFullYear()&&E.value>d.getMonth()}var L=new Date(i+"-"+("0"+(t+1).toString()).slice(-2)+"-01");L.setMonth(L.getMonth()+1),v.classList[L<d?"add":"remove"]("active");var S=new Date(i+"-"+("0"+(t+1).toString()).slice(-2)+"-01");S.setDate(S.getDate()-1),_.classList[S>u?"add":"remove"]("active")}}if(e){for(a=0;a<35;a++)n=h[a],n.classList[n.classList.contains("otherMonth")||~~n.textContent!==e?"remove":"add"]("active");elData(p,"day",e)}this._formatValue()},_formatValue:function(){var t,i,n,a=l.get(c);"true"!==elData(c,"empty")&&(a.isDateTime?(t=new Date(elData(p,"year"),elData(p,"month"),elData(p,"day"),f.value,m.value),a.isTimeOnly?(i=e.formatTime(t),n=e.format(t,"H:i")):a.ignoreTimezone?(i=e.formatDateTime(t),n=e.format(t,"Y-m-dTH:i:s")):(i=e.formatDateTime(t),n=e.format(t,"c"))):(t=new Date(elData(p,"year"),elData(p,"month"),elData(p,"day")),i=e.formatDate(t),n=e.format(t,"Y-m-d")),c.value=i,elData(c,"value",t.getTime()),a.disableClear||a.clearButton.style.removeProperty("visibility"),a.shadow.value=n)},_createPicker:function(){if(null===y){y=elCreate("div"),y.className="datePicker",y.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation()});var i=elCreate("header");y.appendChild(i),_=elCreate("a"),_.className="previous",_.innerHTML='<span class="icon icon16 fa-arrow-left"></span>',_.addEventListener(WCF_CLICK_EVENT,this.previousMonth.bind(this)),i.appendChild(_);var n=elCreate("span");i.appendChild(n),g=elCreate("select"),g.className="month",g.addEventListener("change",this._changeMonth.bind(this)),n.appendChild(g);var a,o="",r=t.get("__monthsShort");for(a=0;a<12;a++)o+='<option value="'+a+'">'+r[a]+"</option>";g.innerHTML=o,w=elCreate("select"),w.className="year",w.addEventListener("change",this._changeYear.bind(this)),n.appendChild(w),v=elCreate("a"),v.className="next",v.innerHTML='<span class="icon icon16 fa-arrow-right"></span>',v.addEventListener(WCF_CLICK_EVENT,this.nextMonth.bind(this)),i.appendChild(v),p=elCreate("ul"),y.appendChild(p);var l=elCreate("li");l.className="weekdays",p.appendChild(l);var c,d=t.get("__daysShort");for(a=0;a<7;a++){var u=a+s;u>6&&(u-=7),c=elCreate("span"),c.textContent=d[u],l.appendChild(c)}var C,E,L=this._click.bind(this);for(a=0;a<6;a++){E=elCreate("li"),p.appendChild(E);for(var S=0;S<7;S++)C=elCreate("a"),C.addEventListener(WCF_CLICK_EVENT,L),h.push(C),E.appendChild(C)}b=elCreate("footer"),y.appendChild(b),f=elCreate("select"),f.className="hour",f.addEventListener("change",this._formatValue.bind(this));var D="",x=new Date(2e3,0,1),I=t.get("wcf.date.timeFormat").replace(/:/,"").replace(/[isu]/g,"");for(a=0;a<24;a++)x.setHours(a),D+='<option value="'+a+'">'+e.format(x,I)+"</option>";for(f.innerHTML=D,b.appendChild(f),b.appendChild(document.createTextNode(" : ")),m=elCreate("select"),m.className="minute",m.addEventListener("change",this._formatValue.bind(this)),D="",a=0;a<60;a++)D+='<option value="'+a+'">'+(a<10?"0"+a.toString():a)+"</option>";m.innerHTML=D,b.appendChild(m),document.body.appendChild(y)}},previousMonth:function(){"0"===g.value?(g.value=11,w.value=~~w.value-1):g.value=~~g.value-1,this._renderGrid(void 0,g.value,w.value)},nextMonth:function(){"11"===g.value?(g.value=0,w.value=1+~~w.value):g.value=1+~~g.value,this._renderGrid(void 0,g.value,w.value)},_changeMonth:function(e){this._renderGrid(void 0,e.currentTarget.value)},_changeYear:function(e){this._renderGrid(void 0,void 0,e.currentTarget.value)},_click:function(e){e.currentTarget.classList.contains("otherMonth")||(elData(c,"empty",!1),this._renderGrid(e.currentTarget.textContent),this._close())},getDate:function(e){return e=this._getElement(e),e.hasAttribute("data-value")?new Date(+elData(e,"value")):null},setDate:function(t,i){t=this._getElement(t);var n=l.get(t);elData(t,"value",i.getTime()),t.value=e["formatDate"+(n.isDateTime?"Time":"")](i);var a="";a=n.ignoreTimezone?"Y-m-dTH:i:s":n.isDateTime?"c":"Y-m-d",n.shadow.value=e.format(i,a)},getValue:function(e){e=this._getElement(e);var t=l.get(e);return t?t.shadow.value:""},clear:function(e){e=this._getElement(e);var t=l.get(e);e.removeAttribute("data-value"),e.value="",t.disableClear||t.clearButton.style.setProperty("visibility","hidden",""),t.isEmpty=!0,t.shadow.value=""},destroy:function(e){e=this._getElement(e);var t=l.get(e),i=e.parentNode;i.parentNode.insertBefore(e,i),elRemove(i),elAttr(e,"type","date"+(t.isDateTime?"time":"")),e.name=t.shadow.name,e.value=t.shadow.value,e.removeAttribute("data-value"),e.removeEventListener(WCF_CLICK_EVENT,C),elRemove(t.shadow),e.classList.remove("inputDatePicker"),e.readOnly=!1,l.delete(e)},setCloseCallback:function(e,t){e=this._getElement(e),l.get(e).onClose=t},_getElement:function(e){if("string"==typeof e&&(e=elById(e)),!(e instanceof Element&&e.classList.contains("inputDatePicker")&&l.has(e)))throw new Error("Expected a valid date picker input element or id.");return e}};return window.__wcf_bc_datePicker=E,E}),define("WoltLabSuite/Core/Ui/Page/Action",["Dictionary","Dom/Util"],function(e,t){"use strict";var i=new e,n=null,a=!1;return{setup:function(){a=!0,n=elCreate("ul"),n.className="pageAction",document.body.appendChild(n)},add:function(e,o,r){!1===a&&this.setup();var s=elCreate("li");if(o.classList.add("button"),o.classList.add("buttonPrimary"),s.appendChild(o),elAttr(s,"aria-hidden","toTop"===e?"true":"false"),elData(s,"name",e),"toTop"===e)s.className="toTop initiallyHidden",n.appendChild(s);else{var l=null;r&&void 0!==(l=i.get(r))&&(l=l.parentNode),null===l&&n.childElementCount&&(l=n.children[0]),null===l?t.prepend(s,n):n.insertBefore(s,l)}i.set(e,o),this._renderContainer()},has:function(e){return i.has(e)},get:function(e){return i.get(e)},remove:function(e){var t=i.get(e);if(void 0!==t){var a=t.parentNode;a.addEventListener("animationend",function(){try{n.removeChild(a),i.delete(e)}catch(e){}}),this.hide(e)}},hide:function(e){var t=i.get(e);t&&(elAttr(t.parentNode,"aria-hidden","true"),this._renderContainer())},show:function(e){var t=i.get(e);t&&(t.parentNode.classList.contains("initiallyHidden")&&t.parentNode.classList.remove("initiallyHidden"),elAttr(t.parentNode,"aria-hidden","false"),this._renderContainer())},_renderContainer:function(){var e=!1;if(n.childElementCount)for(var t=0,i=n.childElementCount;t<i;t++)if("false"===elAttr(n.children[t],"aria-hidden")){e=!0;break}n.classList[e?"add":"remove"]("active")}}}),define("WoltLabSuite/Core/Ui/Page/JumpToTop",["Environment","Language","./Action"],function(e,t,i){"use strict";function n(){this.init()}return n.prototype={init:function(){if("desktop"===e.platform()){this._callbackScrollEnd=this._afterScroll.bind(this),this._timeoutScroll=null;var n=elCreate("a");n.className="jsTooltip",n.href="#",elAttr(n,"title",t.get("wcf.global.scrollUp")),n.innerHTML='<span class="icon icon32 fa-angle-up"></span>',n.addEventListener(WCF_CLICK_EVENT,this._jump.bind(this)),i.add("toTop",n),
-window.addEventListener("scroll",this._scroll.bind(this)),this._afterScroll()}},_jump:function(e){e.preventDefault(),elById("top").scrollIntoView({behavior:"smooth"})},_scroll:function(){null!==this._timeoutScroll&&window.clearTimeout(this._timeoutScroll),this._timeoutScroll=window.setTimeout(this._callbackScrollEnd,100)},_afterScroll:function(){this._timeoutScroll=null,i[window.pageYOffset>=300?"show":"hide"]("toTop")}},n}),define("WoltLabSuite/Core/Bootstrap",["favico","enquire","perfect-scrollbar","WoltLabSuite/Core/Date/Time/Relative","Ui/SimpleDropdown","WoltLabSuite/Core/Ui/Mobile","WoltLabSuite/Core/Ui/TabMenu","WoltLabSuite/Core/Ui/FlexibleMenu","Ui/Dialog","WoltLabSuite/Core/Ui/Tooltip","WoltLabSuite/Core/Language","WoltLabSuite/Core/Environment","WoltLabSuite/Core/Date/Picker","EventHandler","Core","WoltLabSuite/Core/Ui/Page/JumpToTop","Devtools"],function(e,t,i,n,a,o,r,s,l,c,d,u,h,p,f,m,g){"use strict";return window.Favico=e,window.enquire=t,null==window.WCF&&(window.WCF={}),null==window.WCF.Language&&(window.WCF.Language={}),window.WCF.Language.get=d.get,window.WCF.Language.add=d.add,window.WCF.Language.addObject=d.addObject,window.__wcf_bc_eventHandler=p,{setup:function(e){e=f.extend({enableMobileMenu:!0},e),window.ENABLE_DEVELOPER_TOOLS&&g._internal_.enable(),u.setup(),n.setup(),h.init(),a.setup(),o.setup({enableMobileMenu:e.enableMobileMenu}),r.setup(),l.setup(),c.setup();for(var t=elBySelAll("form[method=get]"),i=0,s=t.length;i<s;i++)t[i].setAttribute("method","post");"microsoft"===u.browser()&&(window.onbeforeunload=function(){});var d=0;d=window.setInterval(function(){"function"==typeof window.jQuery&&(window.clearInterval(d),window.jQuery(function(){new m}),window.jQuery.holdReady(!1))},20)}}}),define("WoltLabSuite/Core/Controller/Style/Changer",["Ajax","Language","Ui/Dialog"],function(e,t,i){"use strict";return{setup:function(){var e=elBySel(".jsButtonStyleChanger");e&&e.addEventListener(WCF_CLICK_EVENT,this.showDialog.bind(this))},showDialog:function(e){e.preventDefault(),i.open(this)},_dialogSetup:function(){return{id:"styleChanger",options:{disableContentPadding:!0,title:t.get("wcf.style.changeStyle")},source:{data:{actionName:"getStyleChooser",className:"wcf\\data\\style\\StyleAction"},after:function(e){for(var t=elBySelAll(".styleList > li",e),i=0,n=t.length;i<n;i++){var a=t[i];a.classList.add("pointer"),a.addEventListener(WCF_CLICK_EVENT,this._click.bind(this))}}.bind(this)}}},_click:function(t){t.preventDefault(),e.apiOnce({data:{actionName:"changeStyle",className:"wcf\\data\\style\\StyleAction",objectIDs:[elData(t.currentTarget,"style-id")]},success:function(){window.location.reload()}})}}}),define("WoltLabSuite/Core/Controller/Popover",["Ajax","Dictionary","Environment","Dom/ChangeListener","Dom/Util","Ui/Alignment"],function(e,t,i,n,a,o){"use strict";var r=null,s=new t,l=new t,c=new t,d=null,u=!1,h=null,p=null,f=null,m=null,g=null,v=null,_=null,b=null;return{_setup:function(){if(null===f){f=elCreate("div"),f.className="popover forceHide",m=elCreate("div"),m.className="popoverContent",f.appendChild(m);var e=elCreate("span");e.className="elementPointer",e.appendChild(elCreate("span")),f.appendChild(e),document.body.appendChild(f),g=this._hide.bind(this),_=this._mouseEnter.bind(this),b=this._mouseLeave.bind(this),f.addEventListener("mouseenter",this._popoverMouseEnter.bind(this)),f.addEventListener("mouseleave",b),f.addEventListener("animationend",this._clearContent.bind(this)),window.addEventListener("beforeunload",function(){u=!0,null!==h&&window.clearTimeout(h),this._hide(!0)}.bind(this)),n.add("WoltLabSuite/Core/Controller/Popover",this._init.bind(this))}},init:function(e){"desktop"===i.platform()&&(e.attributeName=e.attributeName||"data-object-id",e.legacy=!0===e.legacy,this._setup(),c.has(e.identifier)||(c.set(e.identifier,{attributeName:e.attributeName,elements:e.legacy?e.className:elByClass(e.className),legacy:e.legacy,loadCallback:e.loadCallback}),this._init(e.identifier)))},_init:function(e){"string"==typeof e&&e.length?this._initElements(c.get(e),e):c.forEach(this._initElements.bind(this))},_initElements:function(e,t){for(var i=e.legacy?elBySelAll(e.elements):e.elements,n=0,o=i.length;n<o;n++){var r=i[n],c=a.identify(r);if(s.has(c))return;if(null!==r.closest(".popover"))return void s.set(c,{content:null,state:0});var d=e.legacy?c:~~r.getAttribute(e.attributeName);if(0!==d){r.addEventListener("mouseenter",_),r.addEventListener("mouseleave",b),"A"===r.nodeName&&elAttr(r,"href")&&r.addEventListener(WCF_CLICK_EVENT,g);var u=t+"-"+d;elData(r,"cache-id",u),l.set(c,{element:r,identifier:t,objectId:d}),s.has(u)||s.set(t+"-"+d,{content:null,state:0})}}},setContent:function(e,t,i){var n=e+"-"+t,o=s.get(n);if(void 0===o)throw new Error("Unable to find element for object id '"+t+"' (identifier: '"+e+"').");var c=a.createFragmentFromHtml(i);if(c.childElementCount||(c=a.createFragmentFromHtml("<p>"+i+"</p>")),o.content=c,o.state=2,r){var d=l.get(r).element;elData(d,"cache-id")===n&&this._show()}},_mouseEnter:function(e){if(!u){null!==h&&(window.clearTimeout(h),h=null);var t=a.identify(e.currentTarget);r===t&&null!==p&&(window.clearTimeout(p),p=null),d=t,h=window.setTimeout(function(){h=null,d===t&&this._show()}.bind(this),800)}},_mouseLeave:function(){d=null,null===p&&(null===v&&(v=this._hide.bind(this)),null!==p&&window.clearTimeout(p),p=window.setTimeout(v,500))},_popoverMouseEnter:function(){null!==p&&(window.clearTimeout(p),p=null)},_show:function(){null!==p&&(window.clearTimeout(p),p=null);var e=!1;f.classList.contains("active")?r!==d&&(this._hide(),e=!0):m.childElementCount&&(e=!0),e&&(f.classList.add("forceHide"),f.offsetTop,this._clearContent(),f.classList.remove("forceHide")),r=d;var t=l.get(r);if(void 0!==t){var i=s.get(elData(t.element,"cache-id"));2===i.state?(m.appendChild(i.content),this._rebuild(r)):0===i.state&&(i.state=1,c.get(t.identifier).loadCallback(t.objectId,this))}},_hide:function(){null!==p&&(window.clearTimeout(p),p=null),f.classList.remove("active")},_clearContent:function(){if(r&&m.childElementCount&&!f.classList.contains("active"))for(var e=s.get(elData(l.get(r).element,"cache-id"));m.childNodes.length;)e.content.appendChild(m.childNodes[0])},_rebuild:function(){f.classList.contains("active")||(f.classList.remove("forceHide"),f.classList.add("active"),o.set(f,l.get(r).element,{pointer:!0,vertical:"top"}))},_ajaxSetup:function(){return{silent:!0}},ajaxApi:function(t,i,n){if("function"!=typeof i)throw new TypeError("Expected a valid callback for parameter 'success'.");e.api(this,t,i,n)}}}),define("WoltLabSuite/Core/Ui/User/Ignore",["List","Dom/ChangeListener"],function(e,t){"use strict";var i=elByClass("ignoredUserMessage"),n=null,a=new e;return{init:function(){n=this._removeClass.bind(this),this._rebuild(),t.add("WoltLabSuite/Core/Ui/User/Ignore",this._rebuild.bind(this))},_rebuild:function(){for(var e,t=0,o=i.length;t<o;t++)e=i[t],a.has(e)||(e.addEventListener(WCF_CLICK_EVENT,n),a.add(e))},_removeClass:function(e){e.preventDefault();var t=e.currentTarget;t.classList.remove("ignoredUserMessage"),t.removeEventListener(WCF_CLICK_EVENT,n),a.delete(t),window.getSelection().removeAllRanges()}}}),define("WoltLabSuite/Core/Ui/Page/Header/Menu",["Environment","Ui/Screen"],function(e,t){"use strict";var i,n,a,o,r=!1,s=0,l=[],c=[];return{init:function(){if(o=elBySel(".mainMenu .boxMenu"),null===(a=o&&o.childElementCount?o.children[0]:null))throw new Error("Unable to find the menu.");t.on("screen-lg",{enable:this._enable.bind(this),disable:this._disable.bind(this),setup:this._setup.bind(this)})},_enable:function(){r=!0,"safari"===e.browser()?window.setTimeout(this._rebuildVisibility.bind(this),1e3):(this._rebuildVisibility(),window.setTimeout(this._rebuildVisibility.bind(this),1e3))},_disable:function(){r=!1},_showNext:function(e){if(e.preventDefault(),c.length){var t=c.slice(0,3).pop();this._setMarginLeft(o.clientWidth-(t.offsetLeft+t.clientWidth)),o.lastElementChild===t&&i.classList.remove("active"),n.classList.add("active")}},_showPrevious:function(e){if(e.preventDefault(),l.length){var t=l.slice(-3)[0];this._setMarginLeft(-1*t.offsetLeft),o.firstElementChild===t&&n.classList.remove("active"),i.classList.add("active")}},_setMarginLeft:function(e){s=Math.min(s+e,0),a.style.setProperty("margin-left",s+"px","")},_rebuildVisibility:function(){if(r){l=[],c=[];var e=o.clientWidth;if(o.scrollWidth>e||s<0)for(var t,a=0,d=o.childElementCount;a<d;a++){t=o.children[a];var u=t.offsetLeft;u<0?l.push(t):u+t.clientWidth>e&&c.push(t)}n.classList[l.length?"add":"remove"]("active"),i.classList[c.length?"add":"remove"]("active")}},_setup:function(){i=elCreate("a"),i.className="mainMenuShowNext",i.href="#",i.innerHTML='<span class="icon icon32 fa-angle-right"></span>',i.addEventListener(WCF_CLICK_EVENT,this._showNext.bind(this)),o.parentNode.appendChild(i),n=elCreate("a"),n.className="mainMenuShowPrevious",n.href="#",n.innerHTML='<span class="icon icon32 fa-angle-left"></span>',n.addEventListener(WCF_CLICK_EVENT,this._showPrevious.bind(this)),o.parentNode.insertBefore(n,o.parentNode.firstChild);var e=this._rebuildVisibility.bind(this);a.addEventListener("transitionend",e),window.addEventListener("resize",function(){a.style.setProperty("margin-left","0px",""),s=0,e()}),this._enable()}}}),define("WoltLabSuite/Core/BootstrapFrontend",["WoltLabSuite/Core/BackgroundQueue","WoltLabSuite/Core/Bootstrap","WoltLabSuite/Core/Controller/Style/Changer","WoltLabSuite/Core/Controller/Popover","WoltLabSuite/Core/Ui/User/Ignore","WoltLabSuite/Core/Ui/Page/Header/Menu"],function(e,t,i,n,a,o){"use strict";return{setup:function(n){n.backgroundQueue.url=WSC_API_URL+n.backgroundQueue.url.substr(WCF_PATH.length),t.setup(),o.init(),n.styleChanger&&i.setup(),n.enableUserPopover&&this._initUserPopover(),e.setUrl(n.backgroundQueue.url),(Math.random()<.1||n.backgroundQueue.force)&&e.invoke(),a.init()},_initUserPopover:function(){n.init({attributeName:"data-user-id",className:"userLink",identifier:"com.woltlab.wcf.user",loadCallback:function(e,t){var i=function(i){t.setContent("com.woltlab.wcf.user",e,i.returnValues.template)};t.ajaxApi({actionName:"getUserProfile",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[e]},i,i)}})}}}),define("WoltLabSuite/Core/ColorUtil",[],function(){"use strict";var e={hsvToRgb:function(e,t,i){var n,a,o,r,s,l={r:0,g:0,b:0};if(n=Math.floor(e/60),a=e/60-n,t/=100,i/=100,o=i*(1-t),r=i*(1-t*a),s=i*(1-t*(1-a)),0==t)l.r=l.g=l.b=i;else switch(n){case 1:l.r=r,l.g=i,l.b=o;break;case 2:l.r=o,l.g=i,l.b=s;break;case 3:l.r=o,l.g=r,l.b=i;break;case 4:l.r=s,l.g=o,l.b=i;break;case 5:l.r=i,l.g=o,l.b=r;break;case 0:case 6:l.r=i,l.g=s,l.b=o}return{r:Math.round(255*l.r),g:Math.round(255*l.g),b:Math.round(255*l.b)}},rgbToHsv:function(e,t,i){var n,a,o,r,s,l;if(e/=255,t/=255,i/=255,r=Math.max(Math.max(e,t),i),s=Math.min(Math.min(e,t),i),l=r-s,n=0,r!==s){switch(r){case e:n=(t-i)/l*60;break;case t:n=60*(2+(i-e)/l);break;case i:n=60*(4+(e-t)/l)}n<0&&(n+=360)}return a=0===r?0:l/r,o=r,{h:Math.round(n),s:Math.round(100*a),v:Math.round(100*o)}},hexToRgb:function(e){if(/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(e)){var t=e.split("");return"#"===t[0]&&t.shift(),3===t.length?{r:parseInt(t[0]+""+t[0],16),g:parseInt(t[1]+""+t[1],16),b:parseInt(t[2]+""+t[2],16)}:{r:parseInt(t[0]+""+t[1],16),g:parseInt(t[2]+""+t[3],16),b:parseInt(t[4]+""+t[5],16)}}return Number.NaN},rgbToHex:function(e,t,i){var n="0123456789ABCDEF";return void 0===t&&e.toString().match(/^rgba?\((\d+), ?(\d+), ?(d\+)(?:, ?[0-9.]+)?\)$/)&&(e=RegExp.$1,t=RegExp.$2,i=RegExp.$3),n.charAt((e-e%16)/16)+""+n.charAt(e%16)+n.charAt((t-t%16)/16)+n.charAt(t%16)+n.charAt((i-i%16)/16)+n.charAt(i%16)}};return window.__wcf_bc_colorUtil=e,e}),define("WoltLabSuite/Core/FileUtil",["Dictionary","StringUtil"],function(e,t){"use strict";var i=e.fromObject({zip:"archive",rar:"archive",tar:"archive",gz:"archive",mp3:"audio",ogg:"audio",wav:"audio",php:"code",html:"code",htm:"code",tpl:"code",js:"code",xls:"excel",ods:"excel",xlsx:"excel",gif:"image",jpg:"image",jpeg:"image",png:"image",bmp:"image",avi:"video",wmv:"video",mov:"video",mp4:"video",mpg:"video",mpeg:"video",flv:"video",pdf:"pdf",ppt:"powerpoint",pptx:"powerpoint",txt:"text",doc:"word",docx:"word",odt:"word"});return{formatFilesize:function(e,i){void 0===i&&(i=2);var n="Byte";return e>=1e3&&(e/=1e3,n="kB"),e>=1e3&&(e/=1e3,n="MB"),e>=1e3&&(e/=1e3,n="GB"),e>=1e3&&(e/=1e3,n="TB"),t.formatNumeric(e,-i)+" "+n},getIconNameByFilename:function(e){var t=e.lastIndexOf(".");if(!1!==t){var n=e.substr(t+1);if(i.has(n))return i.get(n)}return""}}}),define("WoltLabSuite/Core/Permission",["Dictionary"],function(e){"use strict";var t=new e;return{add:function(e,i){if("boolean"!=typeof i)throw new TypeError("Permission value has to be boolean.");t.set(e,i)},addObject:function(e){for(var t in e)objOwns(e,t)&&this.add(t,e[t])},get:function(e){return!!t.has(e)&&t.get(e)}}}),define("WoltLabSuite/Core/Upload",["AjaxRequest","Core","Dom/ChangeListener","Language","Dom/Util","Dom/Traverse"],function(e,t,i,n,a,o){"use strict";function r(e,i,n){if(n=n||{},void 0===n.className)throw new Error("Missing class name.");if(this._options=t.extend({action:"upload",multiple:!1,name:"__files[]",singleFileRequests:!1,url:"index.php?ajax-upload/&t="+SECURITY_TOKEN},n),this._options.url=t.convertLegacyUrl(this._options.url),0===this._options.url.indexOf("index.php")&&(this._options.url=WSC_API_URL+this._options.url),this._buttonContainer=elById(e),null===this._buttonContainer)throw new Error("Element id '"+e+"' is unknown.");if(this._target=elById(i),null===i)throw new Error("Element id '"+i+"' is unknown.");if(n.multiple&&"UL"!==this._target.nodeName&&"OL"!==this._target.nodeName&&"TBODY"!==this._target.nodeName)throw new Error("Target element has to be list or table body if uploading multiple files is supported.");this._fileElements=[],this._internalFileId=0,this._createButton()}return r.prototype={_createButton:function(){this._fileUpload=elCreate("input"),elAttr(this._fileUpload,"type","file"),elAttr(this._fileUpload,"name",this._options.name),this._options.multiple&&elAttr(this._fileUpload,"multiple","true"),this._fileUpload.addEventListener("change",this._upload.bind(this)),this._button=elCreate("p"),this._button.className="button uploadButton";var e=elCreate("span");e.textContent=n.get("wcf.global.button.upload"),this._button.appendChild(e),a.prepend(this._fileUpload,this._button),this._insertButton(),i.trigger()},_createFileElement:function(e){var t=elCreate("progress");if(elAttr(t,"max",100),"OL"===this._target.nodeName||"UL"===this._target.nodeName){var i=elCreate("li");return i.innerText=e.name,i.appendChild(t),this._target.appendChild(i),i}if("TBODY"===this._target.nodeName)return this._createFileTableRow(e);var n=elCreate("p");return n.appendChild(t),this._target.appendChild(n),n},_createFileElements:function(e){if(e.length){var t=this._fileElements.length;this._fileElements[t]=[];for(var n=0,a=e.length;n<a;n++){var o=e[n],r=this._createFileElement(o);r.classList.contains("uploadFailed")||(elData(r,"filename",o.name),elData(r,"internal-file-id",this._internalFileId++),this._fileElements[t][n]=r)}return i.trigger(),t}return null},_createFileTableRow:function(e){throw new Error("Has to be implemented in subclass.")},_failure:function(e,t,i,n,a){return!0},_getParameters:function(){return{}},_insertButton:function(){a.prepend(this._button,this._buttonContainer)},_progress:function(e,t){var i=Math.round(t.loaded/t.total*100);for(var n in this._fileElements[e]){var a=elByTag("PROGRESS",this._fileElements[e][n]);1===a.length&&elAttr(a[0],"value",i)}},_removeButton:function(){elRemove(this._button),i.trigger()},_success:function(e,t,i,n,a){},_upload:function(e,t,i){for(var n=o.childrenByClass(this._target,"uploadFailed"),a=0,r=n.length;a<r;a++)elRemove(n[a]);var s=null,l=[];if(t)l.push(t);else if(i){var c="";switch(i.type){case"image/jpeg":c=".jpg";break;case"image/gif":c=".gif";break;case"image/png":c=".png"}l.push({name:"pasted-from-clipboard"+c})}else l=this._fileUpload.files;if(l.length)if(this._options.singleFileRequests){s=[];for(var a=0,r=l.length;a<r;a++)s.push(this._uploadFiles([l[a]],i))}else s=this._uploadFiles(l,i);return this._removeButton(),this._createButton(),s},_uploadFiles:function(t,i){var n=this._createFileElements(t);if(!this._fileElements[n].length)return null;for(var a=new FormData,o=0,r=t.length;o<r;o++)if(this._fileElements[n][o]){var s=elData(this._fileElements[n][o],"internal-file-id");i?a.append("__files["+s+"]",i,t[o].name):a.append("__files["+s+"]",t[o])}a.append("actionName",this._options.action),a.append("className",this._options.className),"upload"===this._options.action&&a.append("interfaceName","wcf\\data\\IUploadAction");var l=function(e,t){t=t||"";for(var i in e)"object"==typeof e[i]?l(e[i],t+"["+i+"]"):a.append("parameters"+t+"["+i+"]",e[i])};return l(this._getParameters()),new e({data:a,contentType:!1,failure:this._failure.bind(this,n),silent:!0,success:this._success.bind(this,n),uploadProgress:this._progress.bind(this,n),url:this._options.url,withCredentials:!0}).sendRequest(),n},uploadBlob:function(e){return this._upload(null,null,e)},uploadFile:function(e){return this._upload(null,e)}},r}),define("WoltLabSuite/Core/User",[],function(){"use strict";var e,t=!1;return{getLink:function(){return e},init:function(i,n,a){if(t)throw new Error("User has already been initialized.");Object.defineProperty(this,"userId",{value:i,writable:!1}),Object.defineProperty(this,"username",{value:n,writable:!1}),e=a,t=!0}}}),define("WoltLabSuite/Core/Ajax/Jsonp",["Core"],function(e){"use strict";return{send:function(t,i,n,a){if(t="string"==typeof t?t.trim():"",0===t.length)throw new Error("Expected a non-empty string for parameter 'url'.");if("function"!=typeof i)throw new TypeError("Expected a valid callback function for parameter 'success'.");a=e.extend({parameterName:"callback",timeout:10},a||{});var o,r="wcf_jsonp_"+e.getUuid().replace(/-/g,"").substr(0,8),s=window.setTimeout(function(){"function"==typeof n&&n(),window[r]=void 0,elRemove(o)},1e3*(~~a.timeout||10));window[r]=function(){window.clearTimeout(s),i.apply(null,arguments),window[r]=void 0,elRemove(o)},t+=-1===t.indexOf("?")?"?":"&",t+=a.parameterName+"="+r,o=elCreate("script"),o.async=!0,elAttr(o,"src",t),document.head.appendChild(o)}}}),define("WoltLabSuite/Core/Bbcode/Collapsible",[],function(){"use strict";var e=elByClass("jsCollapsibleBbcode");return{observe:function(){for(var t,i;e.length;)t=e[0],i=null,elBySelAll(".toggleButton:not(.jsToggleButtonEnabled)",t,function(e){e.closest(".jsCollapsibleBbcode")===t&&(i=e)}),i&&function(e,t){var i=function(i){if(e.classList.toggle("collapsed")){if(t.textContent=elData(t,"title-expand"),i instanceof Event){var n=e.getBoundingClientRect().top;if(n<0){var a=window.pageYOffset+(n-100);a<0&&(a=0),window.scrollTo(window.pageXOffset,a)}}}else t.textContent=elData(t,"title-collapse")};t.classList.add("jsToggleButtonEnabled"),t.addEventListener(WCF_CLICK_EVENT,i),0!==e.scrollTop&&i()}(t,i),t.classList.remove("jsCollapsibleBbcode")}}}),define("WoltLabSuite/Core/Controller/Captcha",["Dictionary"],function(e){"use strict";var t=new e;return{add:function(e,i){if(t.has(e))throw new Error("Captcha with id '"+e+"' is already registered.");if("function"!=typeof i)throw new TypeError("Expected a valid callback for parameter 'callback'.");t.set(e,i)},delete:function(e){if(!t.has(e))throw new Error("Unknown captcha with id '"+e+"'.");t.delete(e)()},has:function(e){return t.has(e)},getData:function(e){if(!t.has(e))throw new Error("Unknown captcha with id '"+e+"'.");return t.get(e)()}}}),define("WoltLabSuite/Core/Controller/Clipboard",["Ajax","Core","Dictionary","EventHandler","Language","List","ObjectMap","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Confirmation","Ui/SimpleDropdown","WoltLabSuite/Core/Ui/Page/Action"],function(e,t,i,n,a,o,r,s,l,c,d,u,h){"use strict";var p=new i,f=new i,m=new i,g=elByClass("jsClipboardContainer"),v=new r,_=new o,b={},w=null,y=null,C=null,E=!1;return{setup:function(e){if(!e.pageClassName)throw new Error("Expected a non-empty string for parameter 'pageClassName'.");if(null===w)w=this._mark.bind(this),y=this._executeAction.bind(this),C=this._unmarkAll.bind(this),b=t.extend({hasMarkedItems:!1,pageClassNames:[e.pageClassName],pageObjectId:0},e),delete b.pageClassName;else{if(e.pageObjectId)throw new Error("Cannot load secondary clipboard with page object id set.");b.pageClassNames.push(e.pageClassName)}this._initContainers(),b.hasMarkedItems&&g.length&&this._loadMarkedItems(),s.add("WoltLabSuite/Core/Controller/Clipboard",this._initContainers.bind(this))},reload:function(){p.size&&this._loadMarkedItems()},_initContainers:function(){for(var e=0,t=g.length;e<t;e++){var i=g[e],n=c.identify(i),a=p.get(n);if(void 0===a){var r=elBySel(".jsClipboardMarkAll",i);null!==r&&(elData(r,"container-id",n),r.addEventListener(WCF_CLICK_EVENT,this._markAll.bind(this))),a={checkboxes:elByClass("jsClipboardItem",i),element:i,markAll:r,markedObjectIds:new o},p.set(n,a)}for(var s=0,l=a.checkboxes.length;s<l;s++){var d=a.checkboxes[s];_.has(d)||(elData(d,"container-id",n),function(e){null===e.closest("a")?e.addEventListener(WCF_CLICK_EVENT,w):e.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),window.setTimeout(function(){e.checked=!e.checked,w(null,e)},10)})}(d),_.add(d))}}},_loadMarkedItems:function(){e.api(this,{actionName:"getMarkedItems",parameters:{pageClassNames:b.pageClassNames,pageObjectID:b.pageObjectId}})},_markAll:function(e){for(var t=e.currentTarget,i="INPUT"!==t.nodeName||t.checked,n=[],a=elData(t,"container-id"),o=p.get(a),r=elData(o.element,"type"),s=0,c=o.checkboxes.length;s<c;s++){var d=o.checkboxes[s],u=~~elData(d,"object-id");i?d.checked||(d.checked=!0,o.markedObjectIds.add(u),n.push(u)):d.checked&&(d.checked=!1,o.markedObjectIds.delete(u),n.push(u));var h=l.parentByClass(t,"jsClipboardObject");null!==h&&h.classList[i?"addClass":"removeClass"]("jsMarked")}this._saveState(r,n,i)},_mark:function(e,t){t=e instanceof Event?e.currentTarget:t;var i=~~elData(t,"object-id"),n=t.checked,a=elData(t,"container-id"),o=p.get(a),r=elData(o.element,"type"),s=l.parentByClass(t,"jsClipboardObject");if(o.markedObjectIds[n?"add":"delete"](i),s.classList[n?"add":"remove"]("jsMarked"),null!==o.markAll){for(var c=!0,d=0,u=o.checkboxes.length;d<u;d++)if(!o.checkboxes[d].checked){c=!1;break}o.markAll.checked=c}this._saveState(r,[i],n)},_saveState:function(t,i,n){e.api(this,{actionName:n?"mark":"unmark",parameters:{pageClassNames:b.pageClassNames,pageObjectID:b.pageObjectId,objectIDs:i,objectType:t}})},_executeAction:function(e){var t=e.currentTarget,i=v.get(t);if(i.url)return void(window.location.href=i.url);var a=function(){var e=elData(t,"type");n.fire("com.woltlab.wcf.clipboard",e,{data:i,listItem:t,responseData:null})},o="string"==typeof i.internalData.confirmMessage?i.internalData.confirmMessage:"",r=!0;if("object"==typeof i.parameters&&i.parameters.actionName&&i.parameters.className){if("unmarkAll"===i.parameters.actionName||Array.isArray(i.parameters.objectIDs))if(o.length){var s="string"==typeof i.internalData.template?i.internalData.template:"";d.show({confirm:function(){var e={};if(s.length)for(var n=elBySelAll("input, select, textarea",d.getContentElement()),a=0,o=n.length;a<o;a++){var r=n[a],l=elAttr(r,"name");switch(r.nodeName){case"INPUT":("checkbox"!==r.type&&"radio"!==r.type||r.checked)&&(e[l]=elAttr(r,"value"));break;case"SELECT":e[l]=r.value;break;case"TEXTAREA":e[l]=r.value.trim()}}this._executeProxyAction(t,i,e)}.bind(this),message:o,template:s})}else this._executeProxyAction(t,i)}else o.length&&(r=!1,d.show({confirm:a,message:o}));r&&a()},_executeProxyAction:function(t,i,a){a=a||{};var o="unmarkAll"!==i.parameters.actionName?i.parameters.objectIDs:[],r={data:a};if("object"==typeof i.internalData.parameters)for(var s in i.internalData.parameters)i.internalData.parameters.hasOwnProperty(s)&&(r[s]=i.internalData.parameters[s]);e.api(this,{actionName:i.parameters.actionName,className:i.parameters.className,objectIDs:o,parameters:r},function(e){if("unmarkAll"!==i.actionName){var a=elData(t,"type");n.fire("com.woltlab.wcf.clipboard",a,{data:i,listItem:t,responseData:e})}this._loadMarkedItems()}.bind(this))},_unmarkAll:function(t){var i=elData(t.currentTarget,"type");e.api(this,{actionName:"unmarkAll",parameters:{objectType:i}})},_ajaxSetup:function(){return{data:{className:"wcf\\data\\clipboard\\item\\ClipboardItemAction"}}},_ajaxSuccess:function(e){if("unmarkAll"===e.actionName)return void p.forEach(function(t){if(elData(t.element,"type")===e.returnValues.objectType){for(var i=elByClass("jsMarked",t.element);i.length;)i[0].classList.remove("jsMarked");null!==t.markAll&&(t.markAll.checked=!1);for(var n=0,a=t.checkboxes.length;n<a;n++)t.checkboxes[n].checked=!1;h.remove("wcfClipboard-"+e.returnValues.objectType)}}.bind(this));v=new r,p.forEach(function(t){var i=elData(t.element,"type"),n=e.returnValues.markedItems&&e.returnValues.markedItems.hasOwnProperty(i)?e.returnValues.markedItems[i]:[];this._rebuildMarkings(t,n)}.bind(this));var t,i=[];if(e.returnValues&&e.returnValues.items)for(t in e.returnValues.items)e.returnValues.items.hasOwnProperty(t)&&i.push(t);if(f.forEach(function(e,t){-1===i.indexOf(t)&&(h.remove("wcfClipboard-"+t),m.get(t).innerHTML="")}),e.returnValues&&e.returnValues.items){var n,o,s,l,c,d,g,_,b,w,E;for(t in e.returnValues.items)if(e.returnValues.items.hasOwnProperty(t)){c=e.returnValues.items[t],o=!1,l=f.get(t),s=m.get(t),void 0===l?(o=!0,l=elCreate("a"),l.className="dropdownToggle",l.textContent=c.label,f.set(t,l),s=elCreate("ol"),s.className="dropdownMenu",m.set(t,s)):(l.textContent=c.label,s.innerHTML="");for(b in c.items)c.items.hasOwnProperty(b)&&(_=c.items[b],g=elCreate("li"),w=elCreate("span"),w.textContent=_.label,g.appendChild(w),s.appendChild(g),elData(g,"type",t),g.addEventListener(WCF_CLICK_EVENT,y),v.set(g,_));d=elCreate("li"),d.classList.add("dropdownDivider"),s.appendChild(d),E=elCreate("li"),elData(E,"type",t),w=elCreate("span"),w.textContent=a.get("wcf.clipboard.item.unmarkAll"),E.appendChild(w),E.addEventListener(WCF_CLICK_EVENT,C),s.appendChild(E),-1!==i.indexOf(t)&&(n="wcfClipboard-"+t,h.has(n)?h.show(n):h.add(n,l)),o&&(l.parentNode.classList.add("dropdown"),l.parentNode.appendChild(s),u.init(l))}}},_rebuildMarkings:function(e,t){for(var i=!0,n=0,a=e.checkboxes.length;n<a;n++){var o=e.checkboxes[n],r=l.parentByClass(o,"jsClipboardObject"),s=-1!==t.indexOf(~~elData(o,"object-id"));s||(i=!1),o.checked=s,r.classList[s?"add":"remove"]("jsMarked")}if(null!==e.markAll){e.markAll.checked=i;for(var c=e.markAll;c=c.parentNode;)if(c instanceof Element&&c.classList.contains("columnMark")){c=c.parentNode;break}c&&c.classList[i?"add":"remove"]("jsMarked")}},hideEditor:function(e){h.remove("wcfClipboard-"+e),E&&(E=!1,document.documentElement.classList.add("pageOverlayActive"))},showEditor:function(){this._loadMarkedItems(),document.documentElement.classList.contains("pageOverlayActive")&&(document.documentElement.classList.remove("pageOverlayActive"),E=!0)},unmark:function(e,t){this._saveState(e,t,!1)}}}),define("WoltLabSuite/Core/Language/Chooser",["Dictionary","Language","Dom/Traverse","Dom/Util","ObjectMap","Ui/SimpleDropdown"],function(e,t,i,n,a,o){"use strict";var r=new e,s=!1,l=new a,c=null;return{init:function(e,t,i,n,a,o){if(!r.has(t)){var s=elById(e);if(null===s)throw new Error("Expected a valid container id, cannot find '"+t+"'.");var l=elById(t);null===l&&(l=elCreate("input"),elAttr(l,"type","hidden"),elAttr(l,"id",t),elAttr(l,"name",t),elAttr(l,"value",i),s.appendChild(l)),this._initElement(t,l,i,n,a,o)}},_setup:function(){s||(s=!0,c=this._submit.bind(this))},_initElement:function(e,n,a,s,d,u){var h;"DD"===n.parentNode.nodeName?(h=elCreate("div"),h.className="dropdown",n.parentNode.insertBefore(h,n)):(h=n.parentNode,h.classList.add("dropdown")),elHide(n);var p=elCreate("a");p.className="dropdownToggle dropdownIndicator boxFlag box24 inputPrefix"+("DD"===n.parentNode.nodeName?" button":""),h.appendChild(p);var f=elCreate("ul");f.className="dropdownMenu",h.appendChild(f);var m,g,v,_,b=function(t){var n=~~elData(t.currentTarget,"language-id"),a=i.childByClass(f,"active");null!==a&&a.classList.remove("active"),n&&t.currentTarget.classList.add("active"),this._select(e,n,t.currentTarget)}.bind(this);for(var w in s)if(s.hasOwnProperty(w)){var y=s[w];v=elCreate("li"),v.className="boxFlag",v.addEventListener(WCF_CLICK_EVENT,b),elData(v,"language-id",w),void 0!==y.languageCode&&elData(v,"language-code",y.languageCode),f.appendChild(v),m=elCreate("a"),m.className="box24",v.appendChild(m),g=elCreate("img"),elAttr(g,"src",y.iconPath),elAttr(g,"alt",""),g.className="iconFlag",m.appendChild(g),_=elCreate("span"),_.textContent=y.languageName,m.appendChild(_),w==a&&(p.innerHTML=v.firstChild.innerHTML)}if(u)v=elCreate("li"),v.className="dropdownDivider",f.appendChild(v),v=elCreate("li"),elData(v,"language-id",0),v.addEventListener(WCF_CLICK_EVENT,b),f.appendChild(v),m=elCreate("a"),m.textContent=t.get("wcf.global.language.noSelection"),v.appendChild(m),0===a&&(p.innerHTML=v.firstChild.innerHTML),v.addEventListener(WCF_CLICK_EVENT,b);else if(0===a){p.innerHTML=null;var C=elCreate("div");p.appendChild(C),_=elCreate("span"),_.className="icon icon24 fa-question",C.appendChild(_),_=elCreate("span"),_.textContent=t.get("wcf.global.language.noSelection"),C.appendChild(_)}o.init(p),r.set(e,{callback:d,dropdownMenu:f,dropdownToggle:p,element:n});var E=i.parentByTag(n,"FORM");if(null!==E){E.addEventListener("submit",c);var L=l.get(E);void 0===L&&(L=[],l.set(E,L)),L.push(e)}},_select:function(e,t,i){var n=r.get(e);if(void 0===i){for(var a=n.dropdownMenu.childNodes,o=0,s=a.length;o<s;o++){var l=a[o];if(~~elData(l,"language-id")===t){i=l;break}}if(void 0===i)throw new Error("Cannot select unknown language id '"+t+"'")}n.element.value=t,n.dropdownToggle.innerHTML=i.firstChild.innerHTML,r.set(e,n),"function"==typeof n.callback&&n.callback(i)},_submit:function(e){for(var t,i=l.get(e.currentTarget),n=0,a=i.length;n<a;n++)t=elCreate("input"),t.type="hidden",t.name=i[n],t.value=this.getLanguageId(i[n]),e.currentTarget.appendChild(t)},getChooser:function(e){var t=r.get(e);if(void 0===t)throw new Error("Expected a valid language chooser input element, '"+e+"' is not i18n input field.");return t},getLanguageId:function(e){return~~this.getChooser(e).element.value},removeChooser:function(e){r.has(e)&&r.delete(e)},setLanguageId:function(e,t){if(void 0===r.get(e))throw new Error("Expected a valid input element, '"+e+"' is not i18n input field.");this._select(e,t)}}}),define("WoltLabSuite/Core/Language/Input",["Core","Dictionary","Language","ObjectMap","StringUtil","Dom/Traverse","Dom/Util","Ui/SimpleDropdown"],function(e,t,i,n,a,o,r,s){"use strict";var l=new t,c=!1,d=new n,u=new t,h=null,p=null;return{init:function(e,i,n,o){if(!u.has(e)){var r=elById(e);if(null===r)throw new Error("Expected a valid element id, cannot find '"+e+"'.");this._setup();var s=new t;for(var l in i)i.hasOwnProperty(l)&&s.set(~~l,a.unescapeHTML(i[l]));u.set(e,s),this._initElement(e,r,s,n,o)}},registerCallback:function(e,t,i){if(!u.has(e))throw new Error("Unknown element id '"+e+"'.");l.get(e).callbacks.set(t,i)},_setup:function(){c||(c=!0,h=this._dropdownToggle.bind(this),p=this._submit.bind(this))},_initElement:function(e,n,a,c,u){var f=n.parentNode;f.classList.contains("inputAddon")||(f=elCreate("div"),f.className="inputAddon"+("TEXTAREA"===n.nodeName?" inputAddonTextarea":""),elData(f,"input-id",e),n.parentNode.insertBefore(f,n),f.appendChild(n)),f.classList.add("dropdown");var m=elCreate("span");m.className="button dropdownToggle inputPrefix";var g=elCreate("span");g.textContent=i.get("wcf.global.button.disabledI18n"),m.appendChild(g),f.insertBefore(m,n);var v=elCreate("ul");v.className="dropdownMenu",r.insertAfter(v,m);var _,b=function(t,i){
-var n=~~elData(t.currentTarget,"language-id"),a=o.childByClass(v,"active");null!==a&&a.classList.remove("active"),n&&t.currentTarget.classList.add("active"),this._select(e,n,i||!1)}.bind(this);for(var w in c)c.hasOwnProperty(w)&&(_=elCreate("li"),elData(_,"language-id",w),g=elCreate("span"),g.textContent=c[w],_.appendChild(g),_.addEventListener(WCF_CLICK_EVENT,b),v.appendChild(_));!0!==u&&(_=elCreate("li"),_.className="dropdownDivider",v.appendChild(_),_=elCreate("li"),elData(_,"language-id",0),g=elCreate("span"),g.textContent=i.get("wcf.global.button.disabledI18n"),_.appendChild(g),_.addEventListener(WCF_CLICK_EVENT,b),v.appendChild(_));var y=null;if(!0===u||a.size)for(var C=0,E=v.childElementCount;C<E;C++)if(~~elData(v.children[C],"language-id")===LANGUAGE_ID){y=v.children[C];break}s.init(m),s.registerCallback(f.id,h),l.set(e,{buttonLabel:m.children[0],callbacks:new t,element:n,languageId:0,isEnabled:!0,forceSelection:u});var L=o.parentByTag(n,"FORM");if(null!==L){L.addEventListener("submit",p);var S=d.get(L);void 0===S&&(S=[],d.set(L,S)),S.push(e)}null!==y&&b({currentTarget:y},!0)},_select:function(e,i,n){for(var a,o=l.get(e),r=s.getDropdownMenu(o.element.closest(".inputAddon").id),c="",d=0,h=r.childElementCount;d<h;d++){a=r.children[d];var p=elData(a,"language-id");p.length&&i===~~p&&(c=a.children[0].textContent)}if(o.languageId!==i){var f=u.get(e);o.languageId&&f.set(o.languageId,o.element.value),0===i?u.set(e,new t):(o.buttonLabel.classList.contains("active")||!0===n)&&(o.element.value=f.has(i)?f.get(i):""),o.buttonLabel.textContent=c,o.buttonLabel.classList[i?"add":"remove"]("active"),o.languageId=i}n||(o.element.blur(),o.element.focus()),o.callbacks.has("select")&&o.callbacks.get("select")(o.element)},_dropdownToggle:function(e,t){if("open"===t)for(var i,n,a=s.getDropdownMenu(e),o=elData(elById(e),"input-id"),r=l.get(o),c=u.get(o),d=0,h=a.childElementCount;d<h;d++)if(i=a.children[d],n=~~elData(i,"language-id")){var p=!1;r.languageId&&(p=n===r.languageId?""===r.element.value.trim():!c.get(n)),i.classList[p?"add":"remove"]("missingValue")}},_submit:function(e){for(var t,i,n,a,o=d.get(e.currentTarget),r=0,s=o.length;r<s;r++)i=o[r],t=l.get(i),t.isEnabled&&(a=u.get(i),t.callbacks.has("submit")&&t.callbacks.get("submit")(t.element),t.languageId&&a.set(t.languageId,t.element.value),a.size&&(a.forEach(function(t,a){n=elCreate("input"),n.type="hidden",n.name=i+"_i18n["+a+"]",n.value=t,e.currentTarget.appendChild(n)}),t.element.removeAttribute("name")))},getValues:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");var i=u.get(e);return i.set(t.languageId,t.element.value),i},setValues:function(i,n){var a=l.get(i);if(void 0===a)throw new Error("Expected a valid i18n input element, '"+i+"' is not i18n input field.");if(e.isPlainObject(n)&&(n=t.fromObject(n)),a.element.value="",n.has(0))return a.element.value=n.get(0),n.delete(0),u.set(i,n),void this._select(i,0,!0);u.set(i,n),a.languageId=0,this._select(i,LANGUAGE_ID,!0)},disable:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid element, '"+e+"' is not an i18n input field.");if(t.isEnabled){t.isEnabled=!1,elHide(t.buttonLabel.parentNode);var i=t.buttonLabel.parentNode.parentNode;i.classList.remove("inputAddon"),i.classList.remove("dropdown")}},enable:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");if(!t.isEnabled){t.isEnabled=!0,elShow(t.buttonLabel.parentNode);var i=t.buttonLabel.parentNode.parentNode;i.classList.add("inputAddon"),i.classList.add("dropdown")}},isEnabled:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");return t.isEnabled},validate:function(e,t){var i=l.get(e);if(void 0===i)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");if(!i.isEnabled)return!0;var n=u.get(e),a=s.getDropdownMenu(i.element.parentNode.id);i.languageId&&n.set(i.languageId,i.element.value);for(var o,r,c=!1,d=!1,h=0,p=a.childElementCount;h<p;h++)if(o=a.children[h],r=~~elData(o,"language-id"))if(n.has(r)&&0!==n.get(r).length){if(c)return!1;d=!0}else{if(d)return!1;c=!0}return!c||t}}}),define("WoltLabSuite/Core/Language/Text",["Core","./Input"],function(e,t){"use strict";return{init:function(e,i,n,a){var o=elById(e);if(!o||"TEXTAREA"!==o.nodeName||!o.classList.contains("wysiwygTextarea"))throw new Error('Expected <textarea class="wysiwygTextarea" /> for id \''+e+"'.");t.init(e,i,n,a),t.registerCallback(e,"select",this._callbackSelect.bind(this)),t.registerCallback(e,"submit",this._callbackSubmit.bind(this))},_callbackSelect:function(e){void 0!==window.jQuery&&window.jQuery(e).redactor("code.set",e.value)},_callbackSubmit:function(e){void 0!==window.jQuery&&(e.value=window.jQuery(e).redactor("code.get"))}}}),define("WoltLabSuite/Core/Ui/Notification",["Language"],function(e){"use strict";var t=!1,i=null,n=null,a=null,o=null,r=null;return{show:function(s,l,c){t||(this._init(),i="function"==typeof l?l:null,n.className=c||"success",n.textContent=e.get(s||"wcf.global.success"),t=!0,a.classList.add("active"),o=setTimeout(r,2e3))},_init:function(){null===a&&(r=this._hide.bind(this),a=elCreate("div"),a.id="systemNotification",n=elCreate("p"),n.addEventListener(WCF_CLICK_EVENT,r),a.appendChild(n),document.body.appendChild(a))},_hide:function(){clearTimeout(o),a.classList.remove("active"),null!==i&&i(),t=!1}}}),define("WoltLabSuite/Core/Media/Editor",["Ajax","Core","Dictionary","Dom/ChangeListener","Dom/Traverse","Language","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Language/Chooser","WoltLabSuite/Core/Language/Input","EventKey"],function(e,t,i,n,a,o,r,s,l,c,d){"use strict";function u(e){if(this._callbackObject=e||{},this._callbackObject._editorClose&&"function"!=typeof this._callbackObject._editorClose)throw new TypeError("Callback object has no function '_editorClose'.");if(this._callbackObject._editorSuccess&&"function"!=typeof this._callbackObject._editorSuccess)throw new TypeError("Callback object has no function '_editorSuccess'.");this._media=null,this._availableLanguageCount=1,this._categoryIds=[],this._oldCategoryId=0,this._dialogs=new i}return u.prototype={_ajaxSetup:function(){return{data:{actionName:"update",className:"wcf\\data\\media\\MediaAction"}}},_ajaxSuccess:function(e){s.show(),this._callbackObject._editorSuccess&&(this._callbackObject._editorSuccess(this._media,this._oldCategoryId),this._oldCategoryId=0),r.close("mediaEditor_"+this._media.mediaID),this._media=null},_close:function(){this._media=null,this._callbackObject._editorClose&&this._callbackObject._editorClose()},_keyPress:function(e){d.Enter(e)&&(e.preventDefault(),this._saveData())},_saveData:function(){var t=r.getDialog("mediaEditor_"+this._media.mediaID).content,i=elBySel("select[name=categoryID]",t),n=elBySel("input[name=altText]",t),s=elBySel("textarea[name=caption]",t),d=elBySel("input[name=title]",t),u=!1,h=!!n&&a.childByClass(n.parentNode.parentNode,"innerError"),p=!!s&&a.childByClass(s.parentNode.parentNode,"innerError"),f=a.childByClass(d.parentNode.parentNode,"innerError");if(this._oldCategoryId=this._media.categoryID,this._categoryIds.length&&(this._media.categoryID=~~i.value,-1===this._categoryIds.indexOf(this._media.categoryID)&&(this._media.categoryID=0)),this._availableLanguageCount>1?(this._media.isMultilingual=~~elBySel("input[name=isMultilingual]",t).checked,this._media.languageID=this._media.isMultilingual?null:l.getLanguageId("mediaEditor_"+this._media.mediaID+"_languageID")):this._media.languageID=LANGUAGE_ID,this._media.altText={},this._media.caption={},this._media.title={},this._availableLanguageCount>1&&this._media.isMultilingual){if(elById("altText_"+this._media.mediaID)&&!c.validate("altText_"+this._media.mediaID,!0)&&(u=!0,!h)){var m=elCreate("small");m.className="innerError",m.textContent=o.get("wcf.global.form.error.multilingual"),n.parentNode.parentNode.appendChild(m)}if(elById("caption_"+this._media.mediaID)&&!c.validate("caption_"+this._media.mediaID,!0)&&(u=!0,!p)){var m=elCreate("small");m.className="innerError",m.textContent=o.get("wcf.global.form.error.multilingual"),s.parentNode.parentNode.appendChild(m)}if(!c.validate("title_"+this._media.mediaID,!0)&&(u=!0,!f)){var m=elCreate("small");m.className="innerError",m.textContent=o.get("wcf.global.form.error.multilingual"),d.parentNode.parentNode.appendChild(m)}this._media.altText=elById("altText_"+this._media.mediaID)?c.getValues("altText_"+this._media.mediaID).toObject():"",this._media.caption=elById("caption_"+this._media.mediaID)?c.getValues("caption_"+this._media.mediaID).toObject():"",this._media.title=c.getValues("title_"+this._media.mediaID).toObject()}else this._media.altText[this._media.languageID]=n?n.value:"",this._media.caption[this._media.languageID]=s?s.value:"",this._media.title[this._media.languageID]=d.value;for(var g={allowAll:~~elById("mediaEditor_"+this._media.mediaID+"_aclAllowAll").checked,group:[],user:[]},v=elBySelAll('input[name="aclValues[group][]"]',t),_=0,b=v.length;_<b;_++)g.group.push(~~v[_].value);for(var w=elBySelAll('input[name="aclValues[user][]"]',t),_=0,b=w.length;_<b;_++)g.user.push(~~w[_].value);u||(h&&elRemove(h),p&&elRemove(p),f&&elRemove(f),e.api(this,{actionName:"update",objectIDs:[this._media.mediaID],parameters:{aclValues:g,altText:this._media.altText,caption:this._media.caption,data:{categoryID:this._media.categoryID,isMultilingual:this._media.isMultilingual,languageID:this._media.languageID},title:this._media.title}}))},_updateLanguageFields:function(e,t){e&&(t=e.currentTarget);var i=elById("mediaEditor_"+this._media.mediaID+"_languageIDContainer").parentNode;t.checked?(c.enable("title_"+this._media.mediaID),elById("caption_"+this._media.mediaID)&&c.enable("caption_"+this._media.mediaID),elById("altText_"+this._media.mediaID)&&c.enable("altText_"+this._media.mediaID),elHide(i)):(c.disable("title_"+this._media.mediaID),elById("caption_"+this._media.mediaID)&&c.disable("caption_"+this._media.mediaID),elById("altText_"+this._media.mediaID)&&c.disable("altText_"+this._media.mediaID),elShow(i))},edit:function(e){if("object"!=typeof e&&(e={mediaID:~~e}),null!==this._media)throw new Error("Cannot edit media with id '"+e.mediaID+"' while editing media with id '"+this._media.mediaID+"'");this._media=e,this._dialogs.has("mediaEditor_"+e.mediaID)||this._dialogs.set("mediaEditor_"+e.mediaID,{_dialogSetup:function(){return{id:"mediaEditor_"+e.mediaID,options:{backdropCloseOnClick:!1,onClose:this._close.bind(this),title:o.get("wcf.media.edit")},source:{after:function(e,t){this._availableLanguageCount=~~t.returnValues.availableLanguageCount,this._categoryIds=t.returnValues.categoryIDs.map(function(e){return~~e});t.returnValues.mediaData&&(this._media=t.returnValues.mediaData),setTimeout(function(){this._availableLanguageCount>1&&l.setLanguageId("mediaEditor_"+this._media.mediaID+"_languageID",this._media.languageID||LANGUAGE_ID),this._categoryIds.length&&(elBySel("select[name=categoryID]",e).value=~~this._media.categoryID);var t=elBySel("input[name=title]",e),a=elBySel("input[name=altText]",e),o=elBySel("textarea[name=caption]",e);if(this._availableLanguageCount>1&&this._media.isMultilingual?(elById("altText_"+this._media.mediaID)&&c.setValues("altText_"+this._media.mediaID,i.fromObject(this._media.altText||{})),elById("caption_"+this._media.mediaID)&&c.setValues("caption_"+this._media.mediaID,i.fromObject(this._media.caption||{})),c.setValues("title_"+this._media.mediaID,i.fromObject(this._media.title||{}))):(t.value=this._media.title?this._media.title[this._media.languageID||LANGUAGE_ID]:"",a&&(a.value=this._media.altText?this._media.altText[this._media.languageID||LANGUAGE_ID]:""),o&&(o.value=this._media.caption?this._media.caption[this._media.languageID||LANGUAGE_ID]:"")),this._availableLanguageCount>1){var r=elBySel("input[name=isMultilingual]",e);r.addEventListener("change",this._updateLanguageFields.bind(this)),this._updateLanguageFields(null,r)}var s=this._keyPress.bind(this);a&&a.addEventListener("keypress",s),t.addEventListener("keypress",s),elBySel("button[data-type=submit]",e).addEventListener(WCF_CLICK_EVENT,this._saveData.bind(this)),document.activeElement.blur(),elById("mediaEditor_"+this._media.mediaID).parentNode.scrollTop=0,n.trigger()}.bind(this),200)}.bind(this),data:{actionName:"getEditorDialog",className:"wcf\\data\\media\\MediaAction",objectIDs:[e.mediaID]}}}}.bind(this)}),r.open(this._dialogs.get("mediaEditor_"+e.mediaID))}},u}),define("WoltLabSuite/Core/Media/Upload",["Core","DateUtil","Dom/ChangeListener","Dom/Traverse","Dom/Util","EventHandler","Language","Permission","Upload","User","WoltLabSuite/Core/FileUtil"],function(e,t,i,n,a,o,r,s,l,c,d){"use strict";function u(t,i,n){n=n||{},this._mediaManager=null,n.mediaManager&&(this._mediaManager=n.mediaManager,delete n.mediaManager),this._categoryId=null,l.call(this,t,i,e.extend({className:"wcf\\data\\media\\MediaAction",multiple:!!this._mediaManager,singleFileRequests:!0},n))}return e.inherit(u,l,{_createFileElement:function(e){var n;if("OL"===this._target.nodeName||"UL"===this._target.nodeName)n=elCreate("li");else{if("TBODY"===this._target.nodeName){var o=elByTag("TR",this._target)[0],s=this._target.parentNode.parentNode;"none"===s.style.getPropertyValue("display")?(n=o,s.style.removeProperty("display"),elRemove(elById(elData(this._target,"no-items-info")))):(n=o.cloneNode(!0),n.removeAttribute("id"),a.identify(n));for(var l,u=elByTag("TD",n),h=0,p=u.length;h<p;h++)if(l=u[h],l.classList.contains("columnMark"))elBySelAll("[data-object-id]",l,elHide);else if(l.classList.contains("columnIcon"))elBySelAll("[data-object-id]",l,elHide),elByClass("mediaEditButton",l)[0].classList.add("jsMediaEditButton"),elData(elByClass("jsDeleteButton",l)[0],"confirm-message-html",r.get("wcf.media.delete.confirmMessage",{title:e.name}));else if(l.classList.contains("columnFilename")){var f=elByTag("IMG",l);f.length||(f=elByClass("icon48",l));var m=elCreate("span");m.className="icon icon48 fa-spinner mediaThumbnail",a.replaceElement(f[0],m);var g=elBySelAll(".box48 > div > p",l);g[0].textContent=e.name;var v=elByTag("A",g[1])[0];v||(v=elCreate("a"),elByTag("SMALL",g[1])[0].appendChild(v)),v.setAttribute("href",c.getLink()),v.textContent=c.username}else l.classList.contains("columnUploadTime")?(l.innerHTML="",l.appendChild(t.getTimeElement(new Date))):l.classList.contains("columnDigits")?l.textContent=d.formatFilesize(e.size):l.innerHTML="";return a.prepend(n,this._target),n}n=elCreate("p")}var _=elCreate("div");_.className="mediaThumbnail",n.appendChild(_);var b=elCreate("span");b.className="icon icon144 fa-spinner",_.appendChild(b);var w=elCreate("div");w.className="mediaInformation",n.appendChild(w);var y=elCreate("p");y.className="mediaTitle",y.textContent=e.name,w.appendChild(y);var C=elCreate("progress");return elAttr(C,"max",100),w.appendChild(C),a.prepend(n,this._target),i.trigger(),n},_getParameters:function(){if(this._mediaManager){var t={imagesOnly:this._mediaManager.getOption("imagesOnly")},i=this._mediaManager.getCategoryId();return i&&(t.categoryID=i),e.extend(u._super.prototype._getParameters.call(this),t)}return u._super.prototype._getParameters.call(this)},_replaceFileIcon:function(e,t,i){if(t.tinyThumbnailType){var n=elCreate("img");elAttr(n,"src",t.tinyThumbnailLink),elAttr(n,"alt",""),n.style.setProperty("width",i+"px"),n.style.setProperty("height",i+"px"),a.replaceElement(e,n)}else{e.classList.remove("fa-spinner");var o=d.getIconNameByFilename(t.filename);o&&(o="-"+o),e.classList.add("fa-file"+o+"-o")}},_success:function(e,t){for(var a=this._fileElements[e],s=0,l=a.length;s<l;s++){var c=a[s],d=elData(c,"internal-file-id"),u=t.returnValues.media[d];if("TR"===c.tagName)if(u){for(var h=elBySelAll("[data-object-id]",c),s=0,l=h.length;s<l;s++)elData(h[s],"object-id",~~u.mediaID),elShow(h[s]);elByClass("columnMediaID",c)[0].textContent=u.mediaID;var p=elByClass("fa-spinner",c)[0];this._replaceFileIcon(p,u,48)}else{var f=t.returnValues.errors[d];f||(f={errorType:"uploadFailed",filename:elData(c,"filename")});var p=elByClass("fa-spinner",c)[0];p.classList.remove("fa-spinner"),p.classList.add("fa-remove"),p.classList.add("pointer"),p.classList.add("jsTooltip"),elAttr(p,"title",r.get("wcf.global.button.delete")),p.addEventListener(WCF_CLICK_EVENT,function(e){elRemove(e.currentTarget.parentNode.parentNode.parentNode),o.fire("com.woltlab.wcf.media.upload","removedErroneousUploadRow")}),c.classList.add("uploadFailed");var m=elBySelAll(".columnFilename .box48 > div > p",c)[1];elInnerError(m,r.get("wcf.media.upload.error."+f.errorType,{filename:f.filename})),elRemove(m)}else if(elRemove(n.childByTag(n.childByClass(c,"mediaInformation"),"PROGRESS")),u){var p=n.childByTag(n.childByClass(c,"mediaThumbnail"),"SPAN");this._replaceFileIcon(p,u,144),c.className="jsClipboardObject mediaFile",elData(c,"object-id",u.mediaID),this._mediaManager&&(this._mediaManager.setupMediaElement(u,c),this._mediaManager.addMedia(u,c))}else{var f=t.returnValues.errors[d];f||(f={errorType:"uploadFailed",filename:elData(c,"filename")});var p=n.childByTag(n.childByClass(c,"mediaThumbnail"),"SPAN");p.classList.remove("fa-spinner"),p.classList.add("fa-remove"),p.classList.add("pointer"),c.classList.add("uploadFailed"),c.classList.add("jsTooltip"),elAttr(c,"title",r.get("wcf.global.button.delete")),c.addEventListener(WCF_CLICK_EVENT,function(){elRemove(this)});var g=n.childByClass(n.childByClass(c,"mediaInformation"),"mediaTitle");g.innerText=r.get("wcf.media.upload.error."+f.errorType,{filename:f.filename})}i.trigger()}o.fire("com.woltlab.wcf.media.upload","success",{files:a,media:t.returnValues.media,upload:this,uploadId:e})},_uploadFiles:function(e,t){return u._super.prototype._uploadFiles.call(this,e,t)}}),u}),define("WoltLabSuite/Core/Notification/Handler",["Ajax","Core","EventHandler","StringUtil"],function(e,t,i,n){"use strict";if(!("Promise"in window&&"Notification"in window))return{setup:function(){}};var a=!1,o="",r=0,s=window.TIME_NOW,l=null,c=0;return{setup:function(e){if(e=t.extend({enableNotifications:!1,icon:"",sessionKeepAlive:0},e),o=e.icon,c=60*e.sessionKeepAlive,this._prepareNextRequest(),document.addEventListener("visibilitychange",this._onVisibilityChange.bind(this)),window.addEventListener("storage",this._onStorage.bind(this)),this._onVisibilityChange(null),e.enableNotifications)switch(window.Notification.permission){case"granted":a=!0;break;case"default":window.Notification.requestPermission(function(e){"granted"===e&&(a=!0)})}},_onVisibilityChange:function(e){if(null!==e&&!document.hidden){(Date.now()-r)/6e4>4&&(this._resetTimer(),this._dispatchRequest())}r=document.hidden?Date.now():0},_getNextDelay:function(){if(0===r)return 5;var e=~~((Date.now()-r)/6e4);return e<15?5:e<30?10:15},_resetTimer:function(){null!==l&&(window.clearTimeout(l),l=null)},_prepareNextRequest:function(){this._resetTimer();var e=Math.min(this._getNextDelay(),c);l=window.setTimeout(this._dispatchRequest.bind(this),6e4*e)},_dispatchRequest:function(){var t={};i.fire("com.woltlab.wcf.notification","beforePoll",t),t.lastRequestTimestamp=s,e.api(this,{parameters:t})},_onStorage:function(){this._prepareNextRequest();var e,n,a=!1;try{e=window.localStorage.getItem(t.getStoragePrefix()+"notification"),n=window.localStorage.getItem(t.getStoragePrefix()+"keepAliveData"),e=JSON.parse(e),n=JSON.parse(n)}catch(e){a=!0}a||i.fire("com.woltlab.wcf.notification","onStorage",{pollData:e,keepAliveData:n})},_ajaxSuccess:function(e){var n=!1,a=e.returnValues.keepAliveData,o=e.returnValues.pollData;window.WCF.System.PushNotification.executeCallbacks(a);try{window.localStorage.setItem(t.getStoragePrefix()+"notification",JSON.stringify(o)),window.localStorage.setItem(t.getStoragePrefix()+"keepAliveData",JSON.stringify(a))}catch(e){n=!0,window.console.log(e)}n||this._prepareNextRequest(),s=e.returnValues.lastRequestTimestamp,i.fire("com.woltlab.wcf.notification","afterPoll",o),this._showNotification(o)},_showNotification:function(e){if(a&&"object"==typeof e.notification&&"string"==typeof e.notification.message){var t=new window.Notification(e.notification.title,{body:n.unescapeHTML(e.notification.message),icon:o});t.onclick=function(){window.focus(),t.close(),window.location=e.notification.link}}},_ajaxSetup:function(){return{data:{actionName:"poll",className:"wcf\\data\\session\\SessionAction"},ignoreError:!window.ENABLE_DEBUG_MODE,silent:!window.ENABLE_DEBUG_MODE}}}}),define("WoltLabSuite/Core/Ui/Suggestion",["Ajax","Core","Ui/SimpleDropdown"],function(e,t,i){"use strict";function n(e,t){this.init(e,t)}return n.prototype={init:function(e,i){if(this._dropdownMenu=null,this._value="",this._element=elById(e),null===this._element)throw new Error("Expected a valid element id.");if(this._options=t.extend({ajax:{actionName:"getSearchResultList",className:"",interfaceName:"wcf\\data\\ISearchAction",parameters:{data:{}}},callbackSelect:null,excludedSearchValues:[],threshold:3},i),"function"!=typeof this._options.callbackSelect)throw new Error("Expected a valid callback for option 'callbackSelect'.");this._element.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation()}),this._element.addEventListener("keydown",this._keyDown.bind(this)),this._element.addEventListener("keyup",this._keyUp.bind(this))},addExcludedValue:function(e){-1===this._options.excludedSearchValues.indexOf(e)&&this._options.excludedSearchValues.push(e)},removeExcludedValue:function(e){var t=this._options.excludedSearchValues.indexOf(e);-1!==t&&this._options.excludedSearchValues.splice(t,1)},isActive:function(){return null!==this._dropdownMenu&&i.isOpen(this._element.id)},_keyDown:function(e){if(!this.isActive())return!0;if(13!==e.keyCode&&27!==e.keyCode&&38!==e.keyCode&&40!==e.keyCode)return!0;for(var t,n=0,a=this._dropdownMenu.childElementCount;n<a&&(t=this._dropdownMenu.children[n],!t.classList.contains("active"));)n++;if(13===e.keyCode)i.close(this._element.id),this._select(t);else if(27===e.keyCode){if(!i.isOpen(this._element.id))return!0;i.close(this._element.id)}else{var o=0;38===e.keyCode?o=(0===n?a:n)-1:40===e.keyCode&&(o=n+1)===a&&(o=0),o!==n&&(t.classList.remove("active"),this._dropdownMenu.children[o].classList.add("active"))}return e.preventDefault(),!1},_select:function(e){var t=e instanceof Event;t&&(e=e.currentTarget.parentNode),this._options.callbackSelect(this._element.id,{objectId:elData(e.children[0],"object-id"),value:e.textContent}),t&&this._element.focus()},_keyUp:function(t){var n=t.currentTarget.value.trim();if(this._value!==n){if(n.length<this._options.threshold)return null!==this._dropdownMenu&&i.close(this._element.id),void(this._value=n);this._value=n,e.api(this,{parameters:{data:{excludedSearchValues:this._options.excludedSearchValues,searchString:n}}})}},_ajaxSetup:function(){return{data:this._options.ajax}},_ajaxSuccess:function(e){if(null===this._dropdownMenu?(this._dropdownMenu=elCreate("div"),this._dropdownMenu.className="dropdownMenu",i.initFragment(this._element,this._dropdownMenu)):this._dropdownMenu.innerHTML="",e.returnValues.length){for(var t,n,a,o=0,r=e.returnValues.length;o<r;o++)n=e.returnValues[o],t=elCreate("a"),t.textContent=n.label,elData(t,"object-id",n.objectID),t.addEventListener(WCF_CLICK_EVENT,this._select.bind(this)),a=elCreate("li"),0===o&&(a.className="active"),a.appendChild(t),this._dropdownMenu.appendChild(a);i.open(this._element.id)}else i.close(this._element.id)}},n}),define("WoltLabSuite/Core/Ui/ItemList",["Core","Dictionary","Language","Dom/Traverse","EventKey","WoltLabSuite/Core/Ui/Suggestion","Ui/SimpleDropdown"],function(e,t,i,n,a,o,r){"use strict";var s="",l=new t,c=!1,d=null,u=null,h=null,p=null,f=null,m=null;return{init:function(t,i,a){var s=elById(t);if(null===s)throw new Error("Expected a valid element id, '"+t+"' is invalid.");if(l.has(t)){var c=l.get(t);for(var d in c)if(c.hasOwnProperty(d)){var u=c[d];u instanceof Element&&u.parentNode&&elRemove(u)}r.destroy(t),l.delete(t)}a=e.extend({ajax:{actionName:"getSearchResultList",className:"",data:{}},excludedSearchValues:[],maxItems:-1,maxLength:-1,restricted:!1,isCSV:!1,callbackChange:null,callbackSubmit:null,submitFieldName:""},a);var h=n.parentByTag(s,"FORM");if(null!==h&&!1===a.isCSV){if(!a.submitFieldName.length&&"function"!=typeof a.callbackSubmit)throw new Error("Expected a valid function for option 'callbackSubmit', a non-empty value for option 'submitFieldName' or enabling the option 'submitFieldCSV'.");h.addEventListener("submit",function(){var e=this.getValues(t);if(a.submitFieldName.length)for(var i,n=0,o=e.length;n<o;n++)i=elCreate("input"),i.type="hidden",i.name=a.submitFieldName.replace(/{$objectId}/,e[n].objectId),i.value=e[n].value,h.appendChild(i);else a.callbackSubmit(h,e)}.bind(this))}this._setup();var p=this._createUI(s,a),f=new o(t,{ajax:a.ajax,callbackSelect:this._addItem.bind(this),excludedSearchValues:a.excludedSearchValues});if(l.set(t,{dropdownMenu:null,element:p.element,list:p.list,listItem:p.element.parentNode,options:a,shadow:p.shadow,suggestion:f}),i=p.values.length?p.values:i,Array.isArray(i))for(var m,g=0,v=i.length;g<v;g++)m=i[g],"string"==typeof m&&(m={objectId:0,value:m}),this._addItem(t,m)},getValues:function(e){if(!l.has(e))throw new Error("Element id '"+e+"' is unknown.");var t=l.get(e),i=[];return elBySelAll(".item > span",t.list,function(e){i.push({objectId:~~elData(e,"object-id"),value:e.textContent})}),i},setValues:function(e,t){if(!l.has(e))throw new Error("Element id '"+e+"' is unknown.");var i,a,o=l.get(e),r=n.childrenByClass(o.list,"item");for(i=0,a=r.length;i<a;i++)this._removeItem(null,r[i],!0);for(i=0,a=t.length;i<a;i++)this._addItem(e,t[i])},_setup:function(){c||(c=!0,d=this._keyDown.bind(this),u=this._keyPress.bind(this),h=this._keyUp.bind(this),p=this._paste.bind(this),f=this._removeItem.bind(this),m=this._blur.bind(this))},_createUI:function(e,t){var i=elCreate("ol");i.className="inputItemList",elData(i,"element-id",e.id),i.addEventListener(WCF_CLICK_EVENT,function(t){t.target===i&&e.focus()});var n=elCreate("li");n.className="input",i.appendChild(n),e.addEventListener("keydown",d),e.addEventListener("keypress",u),e.addEventListener("keyup",h),e.addEventListener("paste",p),e.addEventListener("blur",m),e.parentNode.insertBefore(i,e),n.appendChild(e),-1!==t.maxLength&&elAttr(e,"maxLength",t.maxLength);var a=null,o=[];if(t.isCSV){a=elCreate("input"),a.className="itemListInputShadow",a.type="hidden",a.name=e.name,e.removeAttribute("name"),i.parentNode.insertBefore(a,i);for(var r,s=e.value.split(","),l=0,c=s.length;l<c;l++)r=s[l].trim(),r.length&&o.push(r);if("TEXTAREA"===e.nodeName){var f=elCreate("input");f.type="text",e.parentNode.insertBefore(f,e),f.id=e.id,elRemove(e),e=f}}return{element:e,list:i,shadow:a,values:o}},_acceptsNewItems:function(e){var t=l.get(e);return-1===t.options.maxItems||t.list.childElementCount-1<t.options.maxItems},_handleLimit:function(e){var t=l.get(e);this._acceptsNewItems(e)?t.element.disabled&&(t.element.disabled=!1,t.element.removeAttribute("placeholder")):t.element.disabled||(t.element.disabled=!0,elAttr(t.element,"placeholder",i.get("wcf.global.form.input.maxItems")))},_keyDown:function(e){var t=e.currentTarget,i=t.parentNode.previousElementSibling;s=t.id,8===e.keyCode?0===t.value.length&&null!==i&&(i.classList.contains("active")?this._removeItem(null,i):i.classList.add("active")):27===e.keyCode&&null!==i&&i.classList.contains("active")&&i.classList.remove("active")},_keyPress:function(e){if(a.Enter(e)||a.Comma(e)){if(e.preventDefault(),l.get(e.currentTarget.id).options.restricted)return;var t=e.currentTarget.value.trim();t.length&&this._addItem(e.currentTarget.id,{objectId:0,value:t})}},_paste:function(e){var t="";t="object"==typeof window.clipboardData?window.clipboardData.getData("Text"):e.clipboardData.getData("text/plain");var i=e.currentTarget,n=i.id,a=~~elAttr(i,"maxLength");t.split(/,/).forEach(function(e){e=e.trim(),a&&e.length>a&&(e=e.substr(0,a)),e.length>0&&this._acceptsNewItems(n)&&this._addItem(n,{objectId:0,value:e})}.bind(this)),e.preventDefault()},_keyUp:function(e){var t=e.currentTarget;if(t.value.length>0){var i=t.parentNode.previousElementSibling;null!==i&&i.classList.remove("active")}},_addItem:function(e,t){var i=l.get(e),n=elCreate("li");n.className="item";var a=elCreate("span");a.className="content",elData(a,"object-id",t.objectId),a.textContent=t.value;var o=elCreate("a");o.className="icon icon16 fa-times",o.addEventListener(WCF_CLICK_EVENT,f),n.appendChild(a),n.appendChild(o),i.list.insertBefore(n,i.listItem),i.suggestion.addExcludedValue(t.value),i.element.value="",this._handleLimit(e);var r=this._syncShadow(i);"function"==typeof i.options.callbackChange&&(null===r&&(r=this.getValues(e)),i.options.callbackChange(e,r))},_removeItem:function(e,t,i){t=null===e?t:e.currentTarget.parentNode;var n=t.parentNode,a=elData(n,"element-id"),o=l.get(a);o.suggestion.removeExcludedValue(t.children[0].textContent),n.removeChild(t),i||o.element.focus(),this._handleLimit(a);var r=this._syncShadow(o);"function"==typeof o.options.callbackChange&&(null===r&&(r=this.getValues(a)),o.options.callbackChange(a,r))},_syncShadow:function(e){if(!e.options.isCSV)return null;for(var t="",i=this.getValues(e.element.id),n=0,a=i.length;n<a;n++)t+=(t.length?",":"")+i[n].value;return e.shadow.value=t,i},_blur:function(e){var t=l.get(e.currentTarget.id);if(!t.options.restricted){var i=e.currentTarget;window.setTimeout(function(){var e=i.value.trim();e.length&&(t.suggestion&&t.suggestion.isActive()||this._addItem(i.id,{objectId:0,value:e}))}.bind(this),100)}}}}),define("WoltLabSuite/Core/Ui/Page/JumpTo",["Language","ObjectMap","Ui/Dialog"],function(e,t,i){"use strict";var n=null,a=null,o=null,r=new t,s=null;return{init:function(e,t){if(null===(t=t||null)){var i=elData(e,"link");t=i?function(e){window.location=i.replace(/pageNo=%d/,"pageNo="+e)}:function(){}}else if("function"!=typeof t)throw new TypeError("Expected a valid function for parameter 'callback'.");r.has(e)||elBySelAll(".jumpTo",e,function(i){i.addEventListener(WCF_CLICK_EVENT,this._click.bind(this,e)),r.set(e,{callback:t})}.bind(this))},_click:function(t,a){n=t,"object"==typeof a&&a.preventDefault(),i.open(this);var r=elData(t,"pages");s.value=r,s.setAttribute("max",r),s.select(),o.textContent=e.get("wcf.page.jumpTo.description").replace(/#pages#/,r)},_keyUp:function(e){if(13===e.which&&!1===a.disabled)return void this._submit();var t=~~s.value;t<1||t>~~elAttr(s,"max")?a.disabled=!0:a.disabled=!1},_submit:function(e){r.get(n).callback(~~s.value),i.close(this)},_dialogSetup:function(){var t='<dl><dt><label for="jsPaginationPageNo">'+e.get("wcf.page.jumpTo")+'</label></dt><dd><input type="number" id="jsPaginationPageNo" value="1" min="1" max="1" class="tiny"><small></small></dd></dl><div class="formSubmit"><button class="buttonPrimary">'+e.get("wcf.global.button.submit")+"</button></div>";return{id:"paginationOverlay",options:{onSetup:function(e){s=elByTag("input",e)[0],s.addEventListener("keyup",this._keyUp.bind(this)),o=elByTag("small",e)[0],a=elByTag("button",e)[0],a.addEventListener(WCF_CLICK_EVENT,this._submit.bind(this))}.bind(this),title:e.get("wcf.global.page.pagination")},source:t}}}}),define("WoltLabSuite/Core/Ui/Pagination",["Core","Language","ObjectMap","StringUtil","WoltLabSuite/Core/Ui/Page/JumpTo"],function(e,t,i,n,a){"use strict";function o(e,t){this.init(e,t)}return o.prototype={SHOW_LINKS:11,init:function(t,i){this._element=t,this._options=e.extend({activePage:1,maxPage:1,callbackShouldSwitch:null,callbackSwitch:null},i),"function"!=typeof this._options.callbackShouldSwitch&&(this._options.callbackShouldSwitch=null),"function"!=typeof this._options.callbackSwitch&&(this._options.callbackSwitch=null),this._element.classList.add("pagination"),this._rebuild(this._element)},_rebuild:function(){var e=!1;this._element.innerHTML=""
-;var i,n=elCreate("ul"),o=elCreate("li");o.className="skip",n.appendChild(o);var r="icon icon24 fa-chevron-left";this._options.activePage>1?(i=elCreate("a"),i.className=r+" jsTooltip",i.href="#",i.title=t.get("wcf.global.page.previous"),o.appendChild(i),i.addEventListener(WCF_CLICK_EVENT,this.switchPage.bind(this,this._options.activePage-1))):(o.innerHTML='<span class="'+r+'"></span>',o.classList.add("disabled")),n.appendChild(this._createLink(1));var s=this.SHOW_LINKS-4,l=this._options.activePage-2;l<0&&(l=0);var c=this._options.maxPage-(this._options.activePage+1);c<0&&(c=0),this._options.activePage>1&&this._options.activePage<this._options.maxPage&&s--;var d=s/2,u=this._options.activePage,h=this._options.activePage;u<1&&(u=1),h<1&&(h=1),h>this._options.maxPage-1&&(h=this._options.maxPage-1),l>=d?u-=d:(u-=l,h+=d-l),c>=d?h+=d:(h+=c,u-=d-c),h=Math.ceil(h),u=Math.ceil(u),u<1&&(u=1),h>this._options.maxPage&&(h=this._options.maxPage);var p='<a class="jsTooltip" title="'+t.get("wcf.page.jumpTo")+'">…</a>';u>1&&(u-1<2?n.appendChild(this._createLink(2)):(o=elCreate("li"),o.className="jumpTo",o.innerHTML=p,n.appendChild(o),e=!0));for(var f=u+1;f<h;f++)n.appendChild(this._createLink(f));h<this._options.maxPage&&(this._options.maxPage-h<2?n.appendChild(this._createLink(this._options.maxPage-1)):(o=elCreate("li"),o.className="jumpTo",o.innerHTML=p,n.appendChild(o),e=!0)),n.appendChild(this._createLink(this._options.maxPage)),o=elCreate("li"),o.className="skip",n.appendChild(o),r="icon icon24 fa-chevron-right",this._options.activePage<this._options.maxPage?(i=elCreate("a"),i.className=r+" jsTooltip",i.href="#",i.title=t.get("wcf.global.page.next"),o.appendChild(i),i.addEventListener(WCF_CLICK_EVENT,this.switchPage.bind(this,this._options.activePage+1))):(o.innerHTML='<span class="'+r+'"></span>',o.classList.add("disabled")),e&&(elData(n,"pages",this._options.maxPage),a.init(n,this.switchPage.bind(this))),this._element.appendChild(n)},_createLink:function(e){var i=elCreate("li");if(e!==this._options.activePage){var a=elCreate("a");a.textContent=n.addThousandsSeparator(e),a.addEventListener(WCF_CLICK_EVENT,this.switchPage.bind(this,e)),i.appendChild(a)}else i.classList.add("active"),i.innerHTML="<span>"+n.addThousandsSeparator(e)+'</span><span class="invisible">'+t.get("wcf.page.pagePosition",{pageNo:e,pages:this._options.maxPage})+"</span>";return i},getActivePage:function(){return this._options.activePage},getElement:function(){return this._element},getMaxPage:function(){return this._options.maxPage},switchPage:function(t,i){if("object"==typeof i&&(i.preventDefault(),i.currentTarget&&elData(i.currentTarget,"tooltip"))){var n=elById("balloonTooltip");n&&(e.triggerEvent(i.currentTarget,"mouseleave"),n.style.removeProperty("top"),n.style.removeProperty("bottom"))}if((t=~~t)>0&&this._options.activePage!==t&&t<=this._options.maxPage){if(null!==this._options.callbackShouldSwitch&&!0!==this._options.callbackShouldSwitch(t))return;this._options.activePage=t,this._rebuild(),null!==this._options.callbackSwitch&&this._options.callbackSwitch(t)}}},o}),define("WoltLabSuite/Core/Ui/Scroll",["Dom/Util"],function(e){"use strict";var t=null,i=null,n=null,a=null;return{element:function(a,o){if(!(a instanceof Element))throw new TypeError("Expected a valid DOM element.");if(void 0!==o&&"function"!=typeof o)throw new TypeError("Expected a valid callback function.");if(!document.body.contains(a))throw new Error("Element must be part of the visible DOM.");if(null!==t)throw new Error("Cannot scroll to element, a concurrent request is running.");o&&(t=o,null===i&&(i=this._onScroll.bind(this)),window.addEventListener("scroll",i));var r=e.offset(a).top;if(null===n){n=50;var s=elById("pageHeaderPanel");if(null!==s){var l=window.getComputedStyle(s).position;n="fixed"===l||"static"===l?s.offsetHeight:0}}n>0&&(r<=n?r=0:r-=n);var c=window.pageYOffset;window.scrollTo({left:0,top:r,behavior:"smooth"}),window.setTimeout(function(){c===window.pageYOffset&&this._onScroll()}.bind(this),100)},_onScroll:function(){null!==a&&window.clearTimeout(a),a=window.setTimeout(function(){null!==t&&t(),window.removeEventListener("scroll",i),t=null,a=null},100)}}}),define("WoltLabSuite/Core/Media/List/Upload",["Core","Dom/Util","../Upload"],function(e,t,i){"use strict";function n(e,t,n){i.call(this,e,t,n)}return e.inherit(n,i,{_createButton:function(){n._super.prototype._createButton.call(this);var e=elBySel("span",this._button),i=document.createTextNode(" ");t.prepend(i,e);var a=elCreate("span");a.className="icon icon16 fa-upload",t.prepend(a,e)},_getParameters:function(){return this._options.categoryId?e.extend(n._super.prototype._getParameters.call(this),{categoryID:this._options.categoryId}):n._super.prototype._getParameters.call(this)}}),n}),define("WoltLabSuite/Core/Controller/Media/List",["Dom/ChangeListener","EventHandler","WoltLabSuite/Core/Controller/Clipboard","WoltLabSuite/Core/Media/Editor","WoltLabSuite/Core/Media/List/Upload"],function(e,t,i,n,a){"use strict";var o,r=elById("mediaListTableBody");return{init:function(r){r=r||{},new a("uploadButton","mediaListTableBody",{categoryId:r.categoryId,multiple:!0}),i.setup({hasMarkedItems:r.hasMarkedItems||!1,pageClassName:"wcf\\acp\\page\\MediaListPage"}),t.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.media",this._clipboardAction.bind(this)),t.add("com.woltlab.wcf.media.upload","removedErroneousUploadRow",this._deleteCallback.bind(this)),new WCF.Action.Delete("wcf\\data\\media\\MediaAction",".jsMediaRow").setCallback(this._deleteCallback),o=new n({_editorSuccess:function(e,t){e.categoryID!=t&&window.setTimeout(function(){window.location.reload()},500)}}),this._addButtonEventListeners(),e.add("WoltLabSuite/Core/Controller/Media/List",this._addButtonEventListeners.bind(this))},_addButtonEventListeners:function(){for(var e,t=elByClass("jsMediaEditButton",r);t.length;)e=t[0],e.classList.remove("jsMediaEditButton"),e.addEventListener(WCF_CLICK_EVENT,this._edit.bind(this))},_deleteCallback:function(e){var t=elByTag("tr",r).length;void 0===e.length?t||window.location.reload():e.length===t?window.location.reload():i.reload.bind(i)},_clipboardAction:function(e){if(null!==e.responseData&&"com.woltlab.wcf.media.delete"===e.data.actionName){for(var t=e.responseData.objectIDs,i=elByClass("jsMediaRow"),n=0;n<i.length;n++){var a=i[n],o=~~elData(elByClass("jsClipboardItem",a)[0],"object-id");-1!==t.indexOf(o)&&(elRemove(a),n--)}i.length||window.location.reload()}},_edit:function(e){o.edit(elData(e.currentTarget,"object-id"))}}}),define("WoltLabSuite/Core/Controller/Notice/Dismiss",["Ajax"],function(e){"use strict";return{setup:function(){var e=elByClass("jsDismissNoticeButton");if(e.length)for(var t=this._click.bind(this),i=0,n=e.length;i<n;i++)e[i].addEventListener(WCF_CLICK_EVENT,t)},_click:function(t){var i=t.currentTarget;e.apiOnce({data:{actionName:"dismiss",className:"wcf\\data\\notice\\NoticeAction",objectIDs:[elData(i,"object-id")]},success:function(){elRemove(i.parentNode)}})}}}),define("WoltLabSuite/Core/Media/Manager/Search",["Ajax","Core","Dom/Traverse","Dom/Util","EventKey","Language","Ui/SimpleDropdown"],function(e,t,i,n,a,o,r){"use strict";function s(e){this._mediaManager=e,this._searchMode=!1,this._searchContainer=elByClass("mediaManagerSearch",e.getDialog())[0],this._input=elByClass("mediaManagerSearchField",e.getDialog())[0],this._input.addEventListener("keypress",this._keyPress.bind(this)),this._cancelButton=elByClass("mediaManagerSearchCancelButton",e.getDialog())[0],this._cancelButton.addEventListener(WCF_CLICK_EVENT,this._cancelSearch.bind(this))}return s.prototype={_ajaxSetup:function(){return{data:{actionName:"getSearchResultList",className:"wcf\\data\\media\\MediaAction",interfaceName:"wcf\\data\\ISearchAction"}}},_ajaxSuccess:function(e){this._mediaManager.setMedia(e.returnValues.media||{},e.returnValues.template||"",{pageCount:e.returnValues.pageCount||0,pageNo:e.returnValues.pageNo||0}),elByClass("dialogContent",this._mediaManager.getDialog())[0].scrollTop=0},_cancelSearch:function(){this._searchMode&&(this._searchMode=!1,this.resetSearch(),this._mediaManager.resetMedia())},_hideStringThresholdError:function(){var e=i.childByClass(this._input.parentNode.parentNode,"innerInfo");e&&elHide(e)},_keyPress:function(e){a.Enter(e)&&(e.preventDefault(),this._input.value.length>=this._mediaManager.getOption("minSearchLength")?(this._hideStringThresholdError(),this.search()):this._showStringThresholdError())},_showStringThresholdError:function(){var e=i.childByClass(this._input.parentNode.parentNode,"innerInfo");e?elShow(e):(e=elCreate("p"),e.className="innerInfo",e.textContent=o.get("wcf.media.search.info.searchStringThreshold",{minSearchLength:this._mediaManager.getOption("minSearchLength")}),n.insertAfter(e,this._input.parentNode))},hideSearch:function(){elHide(this._searchContainer)},resetSearch:function(){this._input.value=""},showSearch:function(){elShow(this._searchContainer)},search:function(t){"number"!=typeof t&&(t=1);var i=this._input.value;i&&this._input.value.length<this._mediaManager.getOption("minSearchLength")?(this._showStringThresholdError(),i=""):this._hideStringThresholdError(),this._searchMode=!0,e.api(this,{parameters:{categoryID:this._mediaManager.getCategoryId(),imagesOnly:this._mediaManager.getOption("imagesOnly"),mode:this._mediaManager.getMode(),pageNo:t,searchString:i}})}},s}),define("WoltLabSuite/Core/Media/Manager/Base",["Core","Dictionary","Dom/ChangeListener","Dom/Traverse","Dom/Util","EventHandler","Language","List","Permission","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Controller/Clipboard","WoltLabSuite/Core/Media/Editor","WoltLabSuite/Core/Media/Upload","WoltLabSuite/Core/Media/Manager/Search","StringUtil","WoltLabSuite/Core/Ui/Pagination"],function(e,t,i,n,a,o,r,s,l,c,d,u,h,p,f,m,g){"use strict";function v(n){this._options=e.extend({dialogTitle:r.get("wcf.media.manager"),imagesOnly:!1,minSearchLength:3},n),this._id="mediaManager"+_++,this._listItems=new t,this._media=new t,this._mediaManagerMediaList=null,this._search=null,this._upload=null,this._forceClipboard=!1,this._hadInitiallyMarkedItems=!1,this._pagination=null,l.get("admin.content.cms.canManageMedia")&&(this._mediaEditor=new h(this)),i.add("WoltLabSuite/Core/Media/Manager",this._addButtonEventListeners.bind(this))}var _=0;return v.prototype={_addButtonEventListeners:function(){if(this._mediaManagerMediaList)for(var e=n.childrenByTag(this._mediaManagerMediaList,"LI"),t=0,i=e.length;t<i;t++){var a=e[t];if(l.get("admin.content.cms.canManageMedia")){var o=elByClass("jsMediaEditButton",a)[0];o&&(o.classList.remove("jsMediaEditButton"),o.addEventListener(WCF_CLICK_EVENT,this._editMedia.bind(this)))}}},_categoryChange:function(){this._search.search()},_click:function(e){e.preventDefault(),c.open(this)},_clipboardAction:function(e){if("com.woltlab.wcf.media.delete"===e.data.actionName&&null!==e.responseData){for(var t=e.responseData.objectIDs,i=0,n=t.length;i<n;i++)this.removeMedia(~~t[i],!0);d.show()}},_dialogClose:function(){(l.get("admin.content.cms.canManageMedia")||this._forceClipboard)&&u.hideEditor("com.woltlab.wcf.media")},_dialogInit:function(e,t){var i=t.returnValues.media||{};for(var n in i)objOwns(i,n)&&this._media.set(~~n,i[n]);this._initPagination(~~t.returnValues.pageCount),this._hadInitiallyMarkedItems=t.returnValues.hasMarkedItems},_dialogSetup:function(){return{id:this._id,options:{onClose:this._dialogClose.bind(this),onShow:this._dialogShow.bind(this),title:this._options.dialogTitle},source:{after:this._dialogInit.bind(this),data:{actionName:"getManagementDialog",className:"wcf\\data\\media\\MediaAction",parameters:{mode:this.getMode(),imagesOnly:this._options.imagesOnly}}}}},_dialogShow:function(){if(!this._mediaManagerMediaList){var e=this.getDialog();this._mediaManagerMediaList=elByClass("mediaManagerMediaList",e)[0],this._mediaCategorySelect=elBySel(".mediaManagerCategoryList > select",e),this._mediaCategorySelect&&this._mediaCategorySelect.addEventListener("change",this._categoryChange.bind(this));for(var t=n.childrenByTag(this._mediaManagerMediaList,"LI"),i=0,r=t.length;i<r;i++){var s=t[i];this._listItems.set(~~elData(s,"object-id"),s)}if(l.get("admin.content.cms.canManageMedia")){var d=elByClass("mediaManagerMediaUploadButton",c.getDialog(this).dialog)[0];this._upload=new p(a.identify(d),a.identify(this._mediaManagerMediaList),{mediaManager:this});new WCF.Action.Delete("wcf\\data\\media\\MediaAction",".mediaFile")._didTriggerEffect=function(e){this.removeMedia(elData(e[0],"object-id"))}.bind(this)}l.get("admin.content.cms.canManageMedia")||this._forceClipboard?(o.add("com.woltlab.wcf.clipboard","com.woltlab.wcf.media",this._clipboardAction.bind(this)),u.setup({hasMarkedItems:!!this._hadInitiallyMarkedItems,pageClassName:"menuManagerDialog-"+this.getMode()})):this._removeClipboardCheckboxes(),this._search=new f(this),t.length||this._search.hideSearch()}(l.get("admin.content.cms.canManageMedia")||this._forceClipboard)&&u.showEditor("com.woltlab.wcf.media")},_editMedia:function(e){if(!l.get("admin.content.cms.canManageMedia"))throw new Error("You are not allowed to edit media files.");c.close(this),this._mediaEditor.edit(this._media.get(~~elData(e.currentTarget,"object-id")))},_editorClose:function(){c.open(this)},_editorSuccess:function(e,t){if(this._mediaCategorySelect){var i=~~this._mediaCategorySelect.value;if(i){var n=~~e.categoryID;t==n||t!=i&&n!=i||this._search.search()}}c.open(this),this._media.set(~~e.mediaID,e);var a=this._listItems.get(~~e.mediaID),o=elByClass("mediaTitle",a)[0];e.isMultilingual?o.textContent=e.title[LANGUAGE_ID]||e.filename:o.textContent=e.title[e.languageID]||e.filename},_initPagination:function(e,t){if(void 0===t&&(t=1),e>1){var i=elCreate("div");i.className="paginationBottom jsPagination",a.replaceElement(elBySel(".jsPagination",c.getDialog(this).content),i),this._pagination=new g(i,{activePage:t,callbackSwitch:this._search.search.bind(this._search),maxPage:e})}else this._pagination&&elHide(this._pagination.getElement())},_removeClipboardCheckboxes:function(){for(var e=elByClass("mediaCheckbox",this._mediaManagerMediaList);e.length;)elRemove(e[0])},_setMedia:function(o){e.isPlainObject(o)?this._media=t.fromObject(o):this._media=o;var s=n.nextByClass(this._mediaManagerMediaList,"info");this._media.size?s&&elHide(s):(null===s&&(s=elCreate("p"),s.className="info",s.textContent=r.get("wcf.media.search.noResults")),elShow(s),a.insertAfter(s,this._mediaManagerMediaList));for(var c=n.childrenByTag(this._mediaManagerMediaList,"LI"),d=0,h=c.length;d<h;d++){var p=c[d];this._media.has(elData(p,"object-id"))?elShow(p):elHide(p)}i.trigger(),l.get("admin.content.cms.canManageMedia")||this._forceClipboard?u.reload():this._removeClipboardCheckboxes()},addMedia:function(e,t){e.languageID||(e.isMultilingual=1),this._media.set(~~e.mediaID,e),this._listItems.set(~~e.mediaID,t),1===this._listItems.size&&this._search.showSearch()},getCategoryId:function(){return this._mediaCategorySelect?this._mediaCategorySelect.value:0},getDialog:function(){return c.getDialog(this).dialog},getMode:function(){return""},getOption:function(e){return this._options[e]?this._options[e]:null},removeMedia:function(e){if(this._listItems.has(e)){try{elRemove(this._listItems.get(e))}catch(e){}this._listItems.delete(e),this._media.delete(e)}},resetMedia:function(){this._search.search()},setMedia:function(e,t,i){var a=!1;for(var o in e)objOwns(e,o)&&(a=!0);if(a){var r=elCreate("ul");r.innerHTML=t;for(var s=n.childrenByTag(r,"LI"),l=0,c=s.length;l<c;l++){var d=s[l];this._listItems.has(~~elData(d,"object-id"))||(this._listItems.set(elData(d,"object-id"),d),this._mediaManagerMediaList.appendChild(d))}}this._initPagination(i.pageCount,i.pageNo),this._setMedia(e)},setupMediaElement:function(t,i){var a=n.childByClass(i,"mediaInformation"),o=elCreate("nav");o.className="jsMobileNavigation buttonGroupNavigation",a.parentNode.appendChild(o);var s=elCreate("ul");s.className="buttonList iconList",o.appendChild(s);var c=elCreate("li");c.className="mediaCheckbox",s.appendChild(c);var d=elCreate("a");c.appendChild(d);var u=elCreate("label");d.appendChild(u);var h=elCreate("input");if(h.className="jsClipboardItem",elAttr(h,"type","checkbox"),elData(h,"object-id",t.mediaID),u.appendChild(h),l.get("admin.content.cms.canManageMedia")){c=elCreate("li"),c.className="jsMediaEditButton",elData(c,"object-id",t.mediaID),s.appendChild(c),c.innerHTML='<a><span class="icon icon16 fa-pencil jsTooltip" title="'+r.get("wcf.global.button.edit")+'"></span> <span class="invisible">'+r.get("wcf.global.button.edit")+"</span></a>",c=elCreate("li"),c.className="jsDeleteButton",elData(c,"object-id",t.mediaID);var p=e.getUuid();elData(c,"confirm-message-html",m.unescapeHTML(r.get("wcf.media.delete.confirmMessage",{title:p})).replace(p,m.escapeHTML(t.filename))),s.appendChild(c),c.innerHTML='<a><span class="icon icon16 fa-times jsTooltip" title="'+r.get("wcf.global.button.delete")+'"></span> <span class="invisible">'+r.get("wcf.global.button.delete")+"</span></a>"}}},v}),define("WoltLabSuite/Core/Media/Manager/Editor",["Core","Dictionary","Dom/Traverse","EventHandler","Language","Permission","Ui/Dialog","WoltLabSuite/Core/Controller/Clipboard","WoltLabSuite/Core/Media/Manager/Base"],function(e,t,i,n,a,o,r,s,l){"use strict";function c(i){i=e.extend({callbackInsert:null},i),l.call(this,i),this._forceClipboard=!0,this._activeButton=null;var a=this._options.editor?this._options.editor.core.toolbar()[0]:void 0;this._buttons=elByClass(this._options.buttonClass||"jsMediaEditorButton",a);for(var o=0,r=this._buttons.length;o<r;o++)this._buttons[o].addEventListener(WCF_CLICK_EVENT,this._click.bind(this));if(this._mediaToInsert=new t,this._mediaToInsertByClipboard=!1,this._uploadData=null,this._uploadId=null,this._options.editor&&!this._options.editor.opts.woltlab.attachments){var s=elData(this._options.editor.$editor[0],"element-id"),c=n.add("com.woltlab.wcf.redactor2","dragAndDrop_"+s,this._editorUpload.bind(this)),d=n.add("com.woltlab.wcf.redactor2","pasteFromClipboard_"+s,this._editorUpload.bind(this));n.add("com.woltlab.wcf.redactor2","destory_"+s,function(){n.remove("com.woltlab.wcf.redactor2","dragAndDrop_"+s,c),n.remove("com.woltlab.wcf.redactor2","dragAndDrop_"+s,d)}),n.add("com.woltlab.wcf.media.upload","success",this._mediaUploaded.bind(this))}}return e.inherit(c,l,{_addButtonEventListeners:function(){if(c._super.prototype._addButtonEventListeners.call(this),this._mediaManagerMediaList)for(var e=i.childrenByTag(this._mediaManagerMediaList,"LI"),t=0,n=e.length;t<n;t++){var a=e[t],o=elByClass("jsMediaInsertButton",a)[0];o&&(o.classList.remove("jsMediaInsertButton"),o.addEventListener(WCF_CLICK_EVENT,this._openInsertDialog.bind(this)))}},_buildInsertDialog:function(){for(var e="",t=this._getThumbnailSizes(),i=0,n=t.length;i<n;i++)e+='<option value="'+t[i]+'">'+a.get("wcf.media.insert.imageSize."+t[i])+"</option>";e+='<option value="original">'+a.get("wcf.media.insert.imageSize.original")+"</option>";var o='<div class="section"><dl class="thumbnailSizeSelection"><dt>'+a.get("wcf.media.insert.imageSize")+'</dt><dd><select name="thumbnailSize">'+e+'</select></dd></dl></div><div class="formSubmit"><button class="buttonPrimary">'+a.get("wcf.global.button.insert")+"</button></div>";r.open({_dialogSetup:function(){return{id:this._getInsertDialogId(),options:{onClose:this._editorClose.bind(this),onSetup:function(e){elByClass("buttonPrimary",e)[0].addEventListener(WCF_CLICK_EVENT,this._insertMedia.bind(this));var t=elBySel(".thumbnailSizeSelection",e);elShow(t)}.bind(this),title:a.get("wcf.media.insert")},source:o}}.bind(this)})},_click:function(e){this._activeButton=e.currentTarget,c._super.prototype._click.call(this,e)},_clipboardAction:function(e){c._super.prototype._clipboardAction.call(this,e),"com.woltlab.wcf.media.insert"===e.data.actionName&&this.insertMedia(e.data.parameters.objectIDs,!0)},_dialogShow:function(){c._super.prototype._dialogShow.call(this),this._uploadData&&(this._uploadData.file?this._upload.uploadFile(this._uploadData.file):this._uploadId=this._upload.uploadBlob(this._uploadData.blob),this._uploadData=null)},_editorUpload:function(e){this._uploadData=e,r.open(this)},_getInsertDialogId:function(){var e="mediaInsert";return this._mediaToInsert.forEach(function(t,i){e+="-"+i}),e},_getThumbnailSizes:function(){for(var e,t,i=[],n=["small","medium","large"],a=0,o=n.length;a<o;a++)e=n[a],t=!0,this._mediaToInsert.forEach(function(i){i[e+"ThumbnailType"]||(t=!1)}),t&&i.push(e);return i},_insertMedia:function(e,i,n){void 0===n&&(n=!0);if(e){r.close(this._getInsertDialogId());var a=e.currentTarget.closest(".dialogContent");i=elBySel("select[name=thumbnailSize]",a).value}if(null!==this._options.callbackInsert?this._options.callbackInsert(this._mediaToInsert,"separate",i):(this._options.editor.buffer.set(),this._mediaToInsert.forEach(this._insertMediaItem.bind(this,i))),this._mediaToInsertByClipboard){var o=[];this._mediaToInsert.forEach(function(e){o.push(e.mediaID)}),s.unmark("com.woltlab.wcf.media",o)}this._mediaToInsert=new t,this._mediaToInsertByClipboard=!1,n&&r.close(this)},_insertMediaGallery:function(){var e=[];this._mediaToInsert.forEach(function(t){e.push(t.mediaID)}),this._options.editor.buffer.set(),this._options.editor.insert.text("[wsmg='"+e.join(",")+"'][/wsmg]")},_insertMediaItem:function(e,t){if(t.isImage){for(var i,n=["small","medium","large","original"],a="",o=0;o<4&&(i=n[o],0==t[i+"ThumbnailHeight"]||(a=i,e!=i));o++);e=a,e||(e="original");var r=t.link;"original"!==e&&(r=t[e+"ThumbnailLink"]),this._options.editor.insert.html('<img src="'+r+'" class="woltlabSuiteMedia" data-media-id="'+t.mediaID+'" data-media-size="'+e+'">')}else this._options.editor.insert.text("[wsm='"+t.mediaID+"'][/wsm]")},_mediaUploaded:function(e){null!==this._uploadId&&this._upload===e.upload&&(this._uploadId===e.uploadId||Array.isArray(this._uploadId)&&-1!==this._uploadId.indexOf(e.uploadId))&&(this._mediaToInsert=t.fromObject(e.media),this._insertMedia(null,"medium",!1),this._uploadId=null)},_openInsertDialog:function(e){this.insertMedia([~~elData(e.currentTarget,"object-id")])},insertMedia:function(e,i){this._mediaToInsert=new t,this._mediaToInsertByClipboard=i||!1;for(var n,a=!0,o=0,s=e.length;o<s;o++)n=this._media.get(e[o]),this._mediaToInsert.set(n.mediaID,n),n.isImage||(a=!1);if(a){if(this._getThumbnailSizes().length){r.close(this);var l=this._getInsertDialogId();r.getDialog(l)?r.openStatic(l):this._buildInsertDialog()}else this._insertMedia(void 0,"original")}else this._insertMedia()},getMode:function(){return"editor"},setupMediaElement:function(e,t){c._super.prototype.setupMediaElement.call(this,e,t);var i=elBySel("nav.buttonGroupNavigation > ul",t),n=elCreate("li");n.className="jsMediaInsertButton",elData(n,"object-id",e.mediaID),i.appendChild(n),n.innerHTML='<a><span class="icon icon16 fa-plus jsTooltip" title="'+a.get("wcf.media.button.insert")+'"></span> <span class="invisible">'+a.get("wcf.media.button.insert")+"</span></a>"}}),c}),define("WoltLabSuite/Core/Media/Manager/Select",["Core","Dom/Traverse","Dom/Util","Language","ObjectMap","Ui/Dialog","WoltLabSuite/Core/FileUtil","WoltLabSuite/Core/Media/Manager/Base"],function(e,t,i,n,a,o,r,s){"use strict";function l(e){s.call(this,e),this._activeButton=null,this._buttons=elByClass(this._options.buttonClass||"jsMediaSelectButton"),this._storeElements=new a;for(var t=0,n=this._buttons.length;t<n;t++){var o=this._buttons[t],r=elData(o,"store");if(r){var l=elById(r);if(l&&"INPUT"===l.tagName){this._buttons[t].addEventListener(WCF_CLICK_EVENT,this._click.bind(this)),this._storeElements.set(o,l);var c=elCreate("p");c.className="button",i.insertAfter(c,o);var d=elCreate("span");d.className="icon icon16 fa-times",c.appendChild(d),l.value||elHide(c),c.addEventListener(WCF_CLICK_EVENT,this._removeMedia.bind(this))}}}}return e.inherit(l,s,{_addButtonEventListeners:function(){if(l._super.prototype._addButtonEventListeners.call(this),this._mediaManagerMediaList)for(var e=t.childrenByTag(this._mediaManagerMediaList,"LI"),i=0,n=e.length;i<n;i++){var a=e[i],o=elByClass("jsMediaSelectButton",a)[0];o&&(o.classList.remove("jsMediaSelectButton"),o.addEventListener(WCF_CLICK_EVENT,this._chooseMedia.bind(this)))}},_chooseMedia:function(e){if(null===this._activeButton)throw new Error("Media cannot be chosen if no button is active.");var t=this._media.get(~~elData(e.currentTarget,"object-id"));elById(elData(this._activeButton,"store")).value=t.mediaID;var i=elData(this._activeButton,"display");if(i){var n=elById(i);if(n)if(t.isImage)n.innerHTML='<img src="'+(t.smallThumbnailLink?t.smallThumbnailLink:t.link)+'" alt="'+(t.altText&&t.altText[LANGUAGE_ID]?t.altText[LANGUAGE_ID]:"")+'" />';else{var a=r.getIconNameByFilename(t.filename);a&&(a="-"+a),n.innerHTML='<div class="box48" style="margin-bottom: 10px;"><span class="icon icon48 fa-file'+a+'-o"></span><div class="containerHeadline"><h3>'+t.filename+"</h3><p>"+t.formattedFilesize+"</p></div></div>"}}elShow(this._activeButton.nextElementSibling),o.close(this)},_click:function(e){if(e.preventDefault(),this._activeButton=e.currentTarget,l._super.prototype._click.call(this,e),this._mediaManagerMediaList)for(var i,n=this._storeElements.get(this._activeButton),a=t.childrenByTag(this._mediaManagerMediaList,"LI"),o=0,r=a.length;o<r;o++)i=a[o],n.value&&n.value==elData(i,"object-id")?i.classList.add("jsSelected"):i.classList.remove("jsSelected")},getMode:function(){return"select"},setupMediaElement:function(e,t){l._super.prototype.setupMediaElement.call(this,e,t);var i=elBySel("nav.buttonGroupNavigation > ul",t),a=elCreate("li");a.className="jsMediaSelectButton",elData(a,"object-id",e.mediaID),i.appendChild(a),a.innerHTML='<a><span class="icon icon16 fa-check jsTooltip" title="'+n.get("wcf.media.button.select")+'"></span> <span class="invisible">'+n.get("wcf.media.button.select")+"</span></a>"},_removeMedia:function(e){e.preventDefault();var t=e.currentTarget;elHide(t);var i=t.previousElementSibling;elById(elData(i,"store")).value=0;var n=elData(i,"display");if(n){var a=elById(n);a&&(a.innerHTML="")}}}),l}),define("WoltLabSuite/Core/Ui/Search/Input",["Ajax","Core","EventKey","Dom/Util","Ui/SimpleDropdown"],function(e,t,i,n,a){"use strict";function o(e,t){this.init(e,t)}return o.prototype={init:function(e,i){if(this._element=e,!(this._element instanceof Element))throw new TypeError("Expected a valid DOM element.");if("INPUT"!==this._element.nodeName||"search"!==this._element.type&&"text"!==this._element.type)throw new Error('Expected an input[type="text"].');this._activeItem=null,this._dropdownContainerId="",this._lastValue="",this._list=null,this._request=null,this._timerDelay=null,this._options=t.extend({ajax:{actionName:"getSearchResultList",className:"",interfaceName:"wcf\\data\\ISearchAction"},callbackDropdownInit:null,callbackSelect:null,delay:500,excludedSearchValues:[],minLength:3,noResultPlaceholder:"",preventSubmit:!1},i),elAttr(this._element,"autocomplete","off"),this._element.addEventListener("keydown",this._keydown.bind(this)),this._element.addEventListener("keyup",this._keyup.bind(this))},addExcludedSearchValues:function(e){-1===this._options.excludedSearchValues.indexOf(e)&&this._options.excludedSearchValues.push(e)},removeExcludedSearchValues:function(e){var t=this._options.excludedSearchValues.indexOf(e);-1!==t&&this._options.excludedSearchValues.splice(t,1)},_keydown:function(e){(null!==this._activeItem&&a.isOpen(this._dropdownContainerId)||this._options.preventSubmit)&&i.Enter(e)&&e.preventDefault(),(i.ArrowUp(e)||i.ArrowDown(e)||i.Escape(e))&&e.preventDefault()},_keyup:function(e){if(null!==this._activeItem)if(a.isOpen(this._dropdownContainerId)){if(i.ArrowUp(e))return e.preventDefault(),this._keyboardPreviousItem();if(i.ArrowDown(e))return e.preventDefault(),this._keyboardNextItem();if(i.Enter(e))return e.preventDefault(),this._keyboardSelectItem()}else this._activeItem=null;if(i.Escape(e))return void a.close(this._dropdownContainerId);var t=this._element.value.trim();if(this._lastValue!==t){if(this._lastValue=t,t.length<this._options.minLength)return void(this._dropdownContainerId&&(a.close(this._dropdownContainerId),this._activeItem=null));this._options.delay?(null!==this._timerDelay&&window.clearTimeout(this._timerDelay),this._timerDelay=window.setTimeout(function(){this._search(t)}.bind(this),this._options.delay)):this._search(t)}},_search:function(t){this._request&&this._request.abortPrevious(),this._request=e.api(this,this._getParameters(t))},_getParameters:function(e){return{parameters:{data:{excludedSearchValues:this._options.excludedSearchValues,searchString:e}}}},_keyboardNextItem:function(){this._activeItem.classList.remove("active"),this._activeItem.nextElementSibling?this._activeItem=this._activeItem.nextElementSibling:this._activeItem=this._list.children[0],this._activeItem.classList.add("active")},_keyboardPreviousItem:function(){this._activeItem.classList.remove("active"),this._activeItem.previousElementSibling?this._activeItem=this._activeItem.previousElementSibling:this._activeItem=this._list.children[this._list.childElementCount-1],this._activeItem.classList.add("active")},_keyboardSelectItem:function(){this._selectItem(this._activeItem)},_clickSelectItem:function(e){this._selectItem(e.currentTarget)},_selectItem:function(e){this._options.callbackSelect&&!1===this._options.callbackSelect(e)?this._element.value="":this._element.value=elData(e,"label"),this._activeItem=null,a.close(this._dropdownContainerId)},_ajaxSuccess:function(e){var t=!1;if(null===this._list?(this._list=elCreate("ul"),this._list.className="dropdownMenu",t=!0,"function"==typeof this._options.callbackDropdownInit&&this._options.callbackDropdownInit(this._list)):this._list.innerHTML="","object"==typeof e.returnValues){var i,o=this._clickSelectItem.bind(this);for(var r in e.returnValues)e.returnValues.hasOwnProperty(r)&&(i=this._createListItem(e.returnValues[r]),i.addEventListener(WCF_CLICK_EVENT,o),this._list.appendChild(i))}t&&(n.insertAfter(this._list,this._element),a.initFragment(this._element.parentNode,this._list),this._dropdownContainerId=n.identify(this._element.parentNode)),this._dropdownContainerId&&(this._activeItem=null,this._list.childElementCount||!1!==this._handleEmptyResult()?(a.open(this._dropdownContainerId),this._list.childElementCount&&~~elData(this._list.children[0],"object-id")&&(this._activeItem=this._list.children[0],this._activeItem.classList.add("active"))):a.close(this._dropdownContainerId))},_handleEmptyResult:function(){if(!this._options.noResultPlaceholder)return!1;var e=elCreate("li");e.className="dropdownText";var t=elCreate("span");return t.textContent=this._options.noResultPlaceholder,e.appendChild(t),this._list.appendChild(e),!0},_createListItem:function(e){var t=elCreate("li");elData(t,"object-id",e.objectID),elData(t,"label",e.label);var i=elCreate("span");return i.textContent=e.label,t.appendChild(i),t},_ajaxSetup:function(){return{data:this._options.ajax}}},o}),define("WoltLabSuite/Core/Ui/User/Search/Input",["Core","WoltLabSuite/Core/Ui/Search/Input"],function(e,t){"use strict";function i(e,t){this.init(e,t)}return e.inherit(i,t,{init:function(t,n){var a=e.isPlainObject(n)&&!0===n.includeUserGroups;n=e.extend({ajax:{className:"wcf\\data\\user\\UserAction",parameters:{data:{includeUserGroups:a?1:0}}}},n),i._super.prototype.init.call(this,t,n)},_createListItem:function(e){var t=i._super.prototype._createListItem.call(this,e);elData(t,"type",e.type);var n=elCreate("div");return n.className="box16",n.innerHTML="group"===e.type?'<span class="icon icon16 fa-users"></span>':e.icon,n.appendChild(t.children[0]),t.appendChild(n),t}}),i}),define("WoltLabSuite/Core/Ui/Acl/Simple",["Language","StringUtil","Dom/ChangeListener","WoltLabSuite/Core/Ui/User/Search/Input"],function(e,t,i,n){"use strict";function a(e){this.init(e)}return a.prototype={init:function(e){this._prefix=e||"",this._build()},_build:function(){
-var e=elById(this._prefix+"aclInputContainer");elById(this._prefix+"aclAllowAll").addEventListener("change",function(){elHide(e)}),elById(this._prefix+"aclAllowAll_no").addEventListener("change",function(){elShow(e)}),this._list=elById(this._prefix+"aclAccessList"),this._list.addEventListener(WCF_CLICK_EVENT,this._removeItem.bind(this));var t=[];elBySelAll(".aclLabel",this._list,function(e){t.push(e.textContent)}),this._searchInput=new n(elById(this._prefix+"aclSearchInput"),{callbackSelect:this._select.bind(this),includeUserGroups:!0,excludedSearchValues:t,preventSubmit:!0}),this._aclListContainer=elById(this._prefix+"aclListContainer"),i.trigger()},_select:function(n){var a=elData(n,"type"),o=elData(n,"label"),r='<span class="icon icon16 fa-'+("group"===a?"users":"user")+'"></span>';r+='<span class="aclLabel">'+t.escapeHTML(o)+"</span>",r+='<span class="icon icon16 fa-times pointer jsTooltip" title="'+e.get("wcf.global.button.delete")+'"></span>',r+='<input type="hidden" name="aclValues['+a+'][]" value="'+elData(n,"object-id")+'">';var s=elCreate("li");s.innerHTML=r;var l=elBySel(".fa-user",this._list);return null===l?this._list.appendChild(s):this._list.insertBefore(s,l.parentNode),elShow(this._aclListContainer),this._searchInput.addExcludedSearchValues(o),i.trigger(),!1},_removeItem:function(e){if(e.target.classList.contains("fa-times")){var t=elBySel(".aclLabel",e.target.parentNode);this._searchInput.removeExcludedSearchValues(t.textContent),elRemove(e.target.parentNode),0===this._list.childElementCount&&elHide(this._aclListContainer)}}},a}),define("WoltLabSuite/Core/Ui/Article/MarkAllAsRead",["Ajax"],function(e){"use strict";return{init:function(){elBySelAll(".markAllAsReadButton",void 0,function(e){e.addEventListener(WCF_CLICK_EVENT,this._click.bind(this))}.bind(this))},_click:function(t){t.preventDefault(),e.api(this)},_ajaxSuccess:function(){var e=elBySel(".mainMenu .active .badge");e&&elRemove(e),elBySelAll(".articleList .newMessageBadge",void 0,elRemove)},_ajaxSetup:function(){return{data:{actionName:"markAllAsRead",className:"wcf\\data\\article\\ArticleAction"}}}}}),define("WoltLabSuite/Core/Ui/Color/Picker",["Core"],function(e){"use strict";function t(e,t){this.init(e,t)}var i=function(e,t){if("object"==typeof window.WCF&&"function"==typeof window.WCF.ColorPicker)return(i=function(e,t){var i=new window.WCF.ColorPicker(e);return"function"==typeof t.callbackSubmit&&i.setCallbackSubmit(t.callbackSubmit),i})(e,t);0===n.length&&(window.__wcf_bc_colorPickerInit=function(){n.forEach(function(e){i(e[0],e[1])}),window.__wcf_bc_colorPickerInit=void 0,n=[]}),n.push([e,t])},n=[];return t.prototype={init:function(t,n){if(!(t instanceof Element))throw new TypeError("Expected a valid DOM element, use `UiColorPicker.fromSelector()` if you want to use a CSS selector.");this._options=e.extend({callbackSubmit:null},n),i(t,n)}},t.fromSelector=function(e){elBySelAll(e,void 0,function(e){new t(e)})},t}),define("WoltLabSuite/Core/Ui/Comment/Add",["Ajax","Core","EventHandler","Language","Dom/ChangeListener","Dom/Util","Dom/Traverse","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Ui/Scroll","EventKey","User","WoltLabSuite/Core/Controller/Captcha"],function(e,t,i,n,a,o,r,s,l,c,d,u,h){"use strict";function p(e){this.init(e)}return p.prototype={init:function(e){this._container=e,this._content=elBySel(".jsOuterEditorContainer",this._container),this._textarea=elBySel(".wysiwygTextarea",this._container),this._editor=null,this._loadingOverlay=null,this._content.addEventListener(WCF_CLICK_EVENT,function(e){this._content.classList.contains("collapsed")&&(e.preventDefault(),this._content.classList.remove("collapsed"),this._focusEditor())}.bind(this)),elBySel('button[data-type="save"]',this._container).addEventListener(WCF_CLICK_EVENT,this._submit.bind(this))},_focusEditor:function(){c.element(this._container,function(){window.jQuery(this._textarea).redactor("WoltLabCaret.endOfEditor")}.bind(this))},_submitGuestDialog:function(e){if("keypress"!==e.type||d.Enter(e)){var i=elBySel("input[name=username]",e.currentTarget.closest(".dialogContent"));if(""===i.value)return elInnerError(i,n.get("wcf.global.form.error.empty")),void i.closest("dl").classList.add("formError");var a={parameters:{data:{username:i.value}}};if(h.has("commentAdd")){var o=h.getData("commentAdd");o instanceof Promise?o.then(function(e){a=t.extend(a,e),this._submit(void 0,a)}.bind(this)):(a=t.extend(a,o),this._submit(void 0,a))}else this._submit(void 0,a)}},_submit:function(n,a){if(n&&n.preventDefault(),this._validate()){this._showLoadingOverlay();var o=this._getParameters();i.fire("com.woltlab.wcf.redactor2","submit_text",o.data),u.userId||a||(o.requireGuestDialog=!0),e.api(this,t.extend({parameters:o},a))}},_getParameters:function(){var e=this._container.closest(".commentList");return{data:{message:this._getEditor().code.get(),objectID:~~elData(e,"object-id"),objectTypeID:~~elData(e,"object-type-id")}}},_validate:function(){if(elBySelAll(".innerError",this._container,elRemove),this._getEditor().utils.isEmpty())return this.throwError(this._textarea,n.get("wcf.global.form.error.empty")),!1;var e={api:this,editor:this._getEditor(),message:this._getEditor().code.get(),valid:!0};return i.fire("com.woltlab.wcf.redactor2","validate_text",e),!1!==e.valid},throwError:function(e,t){elInnerError(e,"empty"===t?n.get("wcf.global.form.error.empty"):t)},_showLoadingOverlay:function(){null===this._loadingOverlay&&(this._loadingOverlay=elCreate("div"),this._loadingOverlay.className="commentLoadingOverlay",this._loadingOverlay.innerHTML='<span class="icon icon96 fa-spinner"></span>'),this._content.classList.add("loading"),this._content.appendChild(this._loadingOverlay)},_hideLoadingOverlay:function(){this._content.classList.remove("loading");var e=elBySel(".commentLoadingOverlay",this._content);null!==e&&e.parentNode.removeChild(e)},_reset:function(){this._getEditor().code.set("<p></p>"),i.fire("com.woltlab.wcf.redactor2","reset_text"),document.activeElement&&document.activeElement.blur(),this._content.classList.add("collapsed")},_handleError:function(e){this.throwError(this._textarea,e.returnValues.errorType)},_getEditor:function(){if(null===this._editor){if("function"!=typeof window.jQuery)throw new Error("Unable to access editor, jQuery has not been loaded yet.");this._editor=window.jQuery(this._textarea).data("redactor")}return this._editor},_insertMessage:function(e){return o.insertHtml(e.returnValues.template,this._container,"after"),l.show(n.get("wcf.global.success.add")),a.trigger(),this._container.nextElementSibling},_ajaxSuccess:function(e){if(!u.userId&&e.returnValues.guestDialog){s.openStatic("jsDialogGuestComment",e.returnValues.guestDialog,{closable:!1,title:n.get("wcf.global.confirmation.title")});var t=s.getDialog("jsDialogGuestComment");elBySel("input[type=submit]",t.content).addEventListener(WCF_CLICK_EVENT,this._submitGuestDialog.bind(this)),elBySel('button[data-type="cancel"]',t.content).addEventListener(WCF_CLICK_EVENT,this._cancelGuestDialog.bind(this)),elBySel("input[type=text]",t.content).addEventListener("keypress",this._submitGuestDialog.bind(this))}else{var i=this._insertMessage(e);u.userId||s.close("jsDialogGuestComment"),this._reset(),this._hideLoadingOverlay(),window.setTimeout(function(){c.element(i)}.bind(this),100)}},_ajaxFailure:function(e){return this._hideLoadingOverlay(),null===e||void 0===e.returnValues||void 0===e.returnValues.errorType||(this._handleError(e),!1)},_ajaxSetup:function(){return{data:{actionName:"addComment",className:"wcf\\data\\comment\\CommentAction"},silent:!0}},_cancelGuestDialog:function(){s.close("jsDialogGuestComment"),this._hideLoadingOverlay()}},p}),define("WoltLabSuite/Core/Ui/Comment/Edit",["Ajax","Core","Dictionary","Environment","EventHandler","Language","List","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Notification","Ui/ReusableDropdown","WoltLabSuite/Core/Ui/Scroll"],function(e,t,i,n,a,o,r,s,l,c,d,u,h){"use strict";function p(e){this.init(e)}return p.prototype={init:function(e){this._activeElement=null,this._callbackClick=null,this._comments=new r,this._container=e,this._editorContainer=null,this.rebuild(),s.add("Ui/Comment/Edit_"+c.identify(this._container),this.rebuild.bind(this))},rebuild:function(){elBySelAll(".comment",this._container,function(e){if(!this._comments.has(e)){if(elDataBool(e,"can-edit")){var t=elBySel(".jsCommentEditButton",e);null!==t&&(null===this._callbackClick&&(this._callbackClick=this._click.bind(this)),t.addEventListener(WCF_CLICK_EVENT,this._callbackClick))}this._comments.add(e)}}.bind(this))},_click:function(t){t.preventDefault(),null===this._activeElement?(this._activeElement=t.currentTarget.closest(".comment"),this._prepare(),e.api(this,{actionName:"beginEdit",objectIDs:[this._getObjectId(this._activeElement)]})):d.show("wcf.message.error.editorAlreadyInUse",null,"warning")},_prepare:function(){this._editorContainer=elCreate("div"),this._editorContainer.className="commentEditorContainer",this._editorContainer.innerHTML='<span class="icon icon48 fa-spinner"></span>';var e=elBySel(".commentContentContainer",this._activeElement);e.insertBefore(this._editorContainer,e.firstChild)},_showEditor:function(e){var t=this._getEditorId(),i=elBySel(".icon",this._editorContainer);elRemove(i);var o=elCreate("div");o.className="editorContainer",c.setInnerHtml(o,e.returnValues.template),this._editorContainer.appendChild(o);var r=elBySel(".formSubmit",o);elBySel('button[data-type="save"]',r).addEventListener(WCF_CLICK_EVENT,this._save.bind(this)),elBySel('button[data-type="cancel"]',r).addEventListener(WCF_CLICK_EVENT,this._restoreMessage.bind(this)),a.add("com.woltlab.wcf.redactor","submitEditor_"+t,function(e){e.cancel=!0,this._save()}.bind(this));var s=elById(t);"redactor"===n.editor()?window.setTimeout(function(){h.element(this._activeElement)}.bind(this),250):s.focus()},_restoreMessage:function(){this._destroyEditor(),elRemove(this._editorContainer),this._activeElement=null},_save:function(){var t={data:{message:""}},i=this._getEditorId();a.fire("com.woltlab.wcf.redactor2","getText_"+i,t.data),this._validate(t)&&(a.fire("com.woltlab.wcf.redactor2","submit_"+i,t),e.api(this,{actionName:"save",objectIDs:[this._getObjectId(this._activeElement)],parameters:t}),this._hideEditor())},_validate:function(e){elBySelAll(".innerError",this._activeElement,elRemove);var t=elById(this._getEditorId());if(window.jQuery(t).data("redactor").utils.isEmpty())return this.throwError(t,o.get("wcf.global.form.error.empty")),!1;var i={api:this,parameters:e,valid:!0};return a.fire("com.woltlab.wcf.redactor2","validate_"+this._getEditorId(),i),!1!==i.valid},throwError:function(e,t){elInnerError(e,t)},_showMessage:function(e){c.setInnerHtml(elBySel(".commentContent .userMessage",this._editorContainer.parentNode),e.returnValues.message),this._restoreMessage(),d.show()},_hideEditor:function(){elHide(elBySel(".editorContainer",this._editorContainer));var e=elCreate("span");e.className="icon icon48 fa-spinner",this._editorContainer.appendChild(e)},_restoreEditor:function(){var e=elBySel(".fa-spinner",this._editorContainer);elRemove(e);var t=elBySel(".editorContainer",this._editorContainer);null!==t&&elShow(t)},_destroyEditor:function(){a.fire("com.woltlab.wcf.redactor2","autosaveDestroy_"+this._getEditorId()),a.fire("com.woltlab.wcf.redactor2","destroy_"+this._getEditorId())},_getEditorId:function(){return"commentEditor"+this._getObjectId(this._activeElement)},_getObjectId:function(e){return~~elData(e,"object-id")},_ajaxFailure:function(e){var t=elBySel(".redactor-layer",this._editorContainer);return null===t?(this._restoreMessage(),!0):(this._restoreEditor(),!e||void 0===e.returnValues||void 0===e.returnValues.errorType||(elInnerError(t,e.returnValues.errorType),!1))},_ajaxSuccess:function(e){switch(e.actionName){case"beginEdit":this._showEditor(e);break;case"save":this._showMessage(e)}},_ajaxSetup:function(){return{data:{className:"wcf\\data\\comment\\CommentAction",parameters:{data:{objectTypeID:~~elData(this._container,"object-type-id")}}},silent:!0}}},p}),define("WoltLabSuite/Core/Ui/ItemList/Filter",["Core","EventKey","Language","List","StringUtil","Dom/Util","Ui/SimpleDropdown"],function(e,t,i,n,a,o,r){"use strict";function s(e,t){this.init(e,t)}return s.prototype={init:function(n,a){this._value="",this._options=e.extend({callbackPrepareItem:void 0,enableVisibilityFilter:!0},a);var o=elById(n);if(null===o)throw new Error("Expected a valid element id, '"+n+"' does not match anything.");if(!o.classList.contains("scrollableCheckboxList")&&"function"!=typeof this._options.callbackPrepareItem)throw new Error("Filter only works with elements with the CSS class 'scrollableCheckboxList'.");elData(o,"filter","showAll");var r=elCreate("div");r.className="itemListFilter",o.parentNode.insertBefore(r,o),r.appendChild(o);var s=elCreate("div");s.className="inputAddon";var l=elCreate("input");l.className="long",l.type="text",l.placeholder=i.get("wcf.global.filter.placeholder"),l.addEventListener("keydown",function(e){t.Enter(e)&&e.preventDefault()}),l.addEventListener("keyup",this._keyup.bind(this));var c=elCreate("a");if(c.href="#",c.className="button inputSuffix jsTooltip",c.title=i.get("wcf.global.filter.button.clear"),c.innerHTML='<span class="icon icon16 fa-times"></span>',c.addEventListener("click",function(e){e.preventDefault(),this.reset()}.bind(this)),s.appendChild(l),s.appendChild(c),this._options.enableVisibilityFilter){var d=elCreate("a");d.href="#",d.className="button inputSuffix jsTooltip",d.title=i.get("wcf.global.filter.button.visibility"),d.innerHTML='<span class="icon icon16 fa-eye"></span>',d.addEventListener(WCF_CLICK_EVENT,this._toggleVisibility.bind(this)),s.appendChild(d)}r.appendChild(s),this._container=r,this._dropdown=null,this._dropdownId="",this._element=o,this._input=l,this._items=null,this._fragment=null},reset:function(){this._input.value="",this._keyup()},_buildItems:function(){this._items=new n;for(var e="function"==typeof this._options.callbackPrepareItem?this._options.callbackPrepareItem:this._prepareItem.bind(this),t=0,i=this._element.childElementCount;t<i;t++)this._items.add(e(this._element.children[t]))},_prepareItem:function(e){for(var t=e.children[0],i=t.textContent.trim(),n=t.children[0];n.nextSibling;)t.removeChild(n.nextSibling);t.appendChild(document.createTextNode(" "));var a=elCreate("span");return a.textContent=i,t.appendChild(a),{item:e,span:a,text:i}},_keyup:function(){var e=this._input.value.trim();if(this._value!==e){null===this._fragment&&(this._fragment=document.createDocumentFragment(),this._element.style.setProperty("height",this._element.offsetHeight+"px","")),this._fragment.appendChild(this._element),null===this._items&&this._buildItems();var t=new RegExp("("+a.escapeRegExp(e)+")","i"),n=""===e;this._items.forEach(function(i){""===e?(i.span.textContent=i.text,elShow(i.item)):t.test(i.text)?(i.span.innerHTML=i.text.replace(t,"<u>$1</u>"),elShow(i.item),n=!0):elHide(i.item)}),this._container.insertBefore(this._fragment.firstChild,this._container.firstChild),this._value=e,elInnerError(this._container,!n&&i.get("wcf.global.filter.error.noMatches"))}},_toggleVisibility:function(e){e.preventDefault(),e.stopPropagation();var t=e.currentTarget;if(null===this._dropdown){var n=elCreate("ul");n.className="dropdownMenu",["activeOnly","highlightActive","showAll"].forEach(function(e){var t=elCreate("a");elData(t,"type",e),t.href="#",t.textContent=i.get("wcf.global.filter.visibility."+e),t.addEventListener(WCF_CLICK_EVENT,this._setVisibility.bind(this));var a=elCreate("li");if(a.appendChild(t),"showAll"===e){a.className="active";var o=elCreate("li");o.className="dropdownDivider",n.appendChild(o)}n.appendChild(a)}.bind(this)),r.initFragment(t,n),this._setupVisibilityFilter(),this._dropdown=n,this._dropdownId=t.id}r.toggleDropdown(t.id,t)},_setupVisibilityFilter:function(){var e=this._element.nextSibling,t=this._element.parentNode,i=this._element.scrollTop;document.createDocumentFragment().appendChild(this._element),elBySelAll("li",this._element,function(e){var t=elBySel('input[type="checkbox"]',e);t.checked&&e.classList.add("active"),t.addEventListener("change",function(){e.classList[t.checked?"add":"remove"]("active")})}),t.insertBefore(this._element,e),this._element.scrollTop=i},_setVisibility:function(e){e.preventDefault();var t=e.currentTarget,i=elData(t,"type");if(r.close(this._dropdownId),elData(this._element,"filter")!==i){elData(this._element,"filter",i),elBySel(".active",this._dropdown).classList.remove("active"),t.parentNode.classList.add("active");var n=elById(this._dropdownId);n.classList["showAll"===i?"remove":"add"]("active");var a=elBySel(".icon",n);a.classList["showAll"===i?"add":"remove"]("fa-eye"),a.classList["showAll"===i?"remove":"add"]("fa-eye-slash")}}},s}),define("WoltLabSuite/Core/Ui/ItemList/User",["WoltLabSuite/Core/Ui/ItemList"],function(e){"use strict";return{init:function(t,i){e.init(t,[],{ajax:{className:"wcf\\data\\user\\UserAction",parameters:{data:{includeUserGroups:~~i.includeUserGroups}}},callbackChange:"function"==typeof i.callbackChange?i.callbackChange:null,excludedSearchValues:Array.isArray(i.excludedSearchValues)?i.excludedSearchValues:[],isCSV:!0,maxItems:~~i.maxItems||-1,restricted:!0})},getValues:function(t){return e.getValues(t)}}}),define("WoltLabSuite/Core/Ui/User/List",["Ajax","Core","Dictionary","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ui/Pagination"],function(e,t,i,n,a,o){"use strict";function r(e){this.init(e)}return r.prototype={init:function(e){this._cache=new i,this._pageCount=0,this._pageNo=1,this._options=t.extend({className:"",dialogTitle:"",parameters:{}},e)},open:function(){this._pageNo=1,this._showPage()},_showPage:function(t){if("number"==typeof t&&(this._pageNo=~~t),0!==this._pageCount&&(this._pageNo<1||this._pageNo>this._pageCount))throw new RangeError("pageNo must be between 1 and "+this._pageCount+" ("+this._pageNo+" given).");if(this._cache.has(this._pageNo)){var i=a.open(this,this._cache.get(this._pageNo));if(this._pageCount>1){var n=elBySel(".jsPagination",i.content);null!==n&&new o(n,{activePage:this._pageNo,maxPage:this._pageCount,callbackSwitch:this._showPage.bind(this)});var r=i.content.parentNode;r.scrollTop>0&&(r.scrollTop=0)}}else this._options.parameters.pageNo=this._pageNo,e.api(this,{parameters:this._options.parameters})},_ajaxSuccess:function(e){void 0!==e.returnValues.pageCount&&(this._pageCount=~~e.returnValues.pageCount),this._cache.set(this._pageNo,e.returnValues.template),this._showPage()},_ajaxSetup:function(){return{data:{actionName:"getGroupedUserList",className:this._options.className,interfaceName:"wcf\\data\\IGroupedUserListAction"}}},_dialogSetup:function(){return{id:n.getUniqueId(),options:{title:this._options.dialogTitle},source:null}}},r}),define("WoltLabSuite/Core/Ui/Like/Handler",["Ajax","Core","Dictionary","Language","ObjectMap","StringUtil","Dom/ChangeListener","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ui/User/List","User"],function(e,t,i,n,a,o,r,s,l,c,d){"use strict";function u(e,t){this.init(e,t)}var h=!1;return u.prototype={init:function(e,i){if(""===i.containerSelector)throw new Error("[WoltLabSuite/Core/Ui/Like/Handler] Expected a non-empty string for option 'containerSelector'.");this._containers=new a,this._details=new a,this._objectType=e,this._options=t.extend({badgeClassNames:"",isSingleItem:!1,markListItemAsActive:!1,renderAsButton:!0,summaryPrepend:!0,summaryUseIcon:!0,canDislike:!1,canLike:!1,canLikeOwnContent:!1,canViewSummary:!1,badgeContainerSelector:".messageHeader .messageStatus",buttonAppendToSelector:".messageFooter .messageFooterButtons",buttonBeforeSelector:"",containerSelector:"",summarySelector:".messageFooterGroup"},i),this.initContainers(i,e),r.add("WoltLabSuite/Core/Ui/Like/Handler-"+e,this.initContainers.bind(this))},initContainers:function(){for(var e,t,i=elBySelAll(this._options.containerSelector),n=!1,a=0,o=i.length;a<o;a++)e=i[a],this._containers.has(e)||(t={badge:null,dislikeButton:null,likeButton:null,summary:null,dislikes:~~elData(e,"like-dislikes"),liked:~~elData(e,"like-liked"),likes:~~elData(e,"like-likes"),objectId:~~elData(e,"object-id"),users:JSON.parse(elData(e,"like-users"))},this._containers.set(e,t),this._buildWidget(e,t),n=!0);n&&r.trigger()},_buildWidget:function(e,t){if(this._options.canViewSummary){var i,n,a,o=this._options.isSingleItem?elBySel(this._options.summarySelector):elBySel(this._options.summarySelector,e);null!==o&&(i=elCreate("div"),i.className="likesSummary",this._options.summaryUseIcon&&(a=elCreate("span"),a.className="icon icon16 fa-thumbs-o-up",i.appendChild(a)),n=elCreate("span"),n.className="likesSummaryContent",n.addEventListener(WCF_CLICK_EVENT,this._showSummary.bind(this,e)),i.appendChild(n),this._options.summaryPrepend?s.prepend(i,o):o.appendChild(i),t.summary=n,this._updateSummary(e))}var r,l,c=this._options.isSingleItem?elBySel(this._options.badgeContainerSelector):elBySel(this._options.badgeContainerSelector,e);if(null!==c&&(r=elCreate("a"),r.href="#",r.className="wcfLikeCounter jsTooltip"+(this._options.badgeClassNames?" "+this._options.badgeClassNames:""),r.addEventListener(WCF_CLICK_EVENT,this._showSummary.bind(this,e)),"OL"===c.nodeName||"UL"===c.nodeName?(l=elCreate("li"),l.appendChild(r),c.appendChild(l)):c.appendChild(r),t.badge=r,this._updateBadge(e)),this._options.canLike&&(d.userId!=elData(e,"user-id")||this._options.canLikeOwnContent)){var u=this._options.buttonAppendToSelector?this._options.isSingleItem?elBySel(this._options.buttonAppendToSelector):elBySel(this._options.buttonAppendToSelector,e):null,h=this._options.buttonBeforeSelector?this._options.isSingleItem?elBySel(this._options.buttonBeforeSelector):elBySel(this._options.buttonBeforeSelector,e):null;if(null===h&&null===u)throw new Error("Unable to find insert location for like/dislike buttons.");t.likeButton=this._createButton(e,!0,h,u),this._options.canDislike&&(t.dislikeButton=this._createButton(e,!1,h,u)),this._updateActiveState(e)}},_createButton:function(e,t,i,a){var o=n.get("wcf.like.button."+(t?"like":"dislike")),r=elCreate("li");r.className="wcf"+(t?"Like":"Dislike")+"Button";var s=elCreate("a");return s.className="jsTooltip"+(this._options.renderAsButton?" button":""),s.href="#",s.title=o,s.innerHTML='<span class="icon icon16 fa-thumbs-o-'+(t?"up":"down")+'"></span> <span class="invisible">'+o+"</span>",s.addEventListener(WCF_CLICK_EVENT,this._like.bind(this,e)),elData(s,"type",t?"like":"dislike"),r.appendChild(s),i?i.parentNode.insertBefore(r,i):a.appendChild(r),s},_showSummary:function(e,t){t.preventDefault(),this._details.has(e)||this._details.set(e,new c({className:"wcf\\data\\like\\LikeAction",dialogTitle:n.get("wcf.like.details"),parameters:{data:{containerID:s.identify(e),objectID:this._containers.get(e).objectId,objectType:this._objectType}}})),this._details.get(e).open()},_updateBadge:function(e){var t=this._containers.get(e);if(0===t.likes&&0===t.dislikes)elHide(t.badge);else{elShow(t.badge),t.badge.classList.remove("likeCounterLiked","likeCounterDisliked");var i=t.likes-t.dislikes,a='<span class="icon icon16 fa-thumbs-o-'+(i<0?"down":"up")+'"></span><span class="wcfLikeValue">';i>0?(a+="+"+o.addThousandsSeparator(i),t.badge.classList.add("likeCounterLiked")):i<0?(a+="−"+o.addThousandsSeparator(Math.abs(i)),t.badge.classList.add("likeCounterDisliked")):a+="±0",t.badge.innerHTML=a+"</span>",t.badge.setAttribute("data-tooltip",n.get("wcf.like.tooltip",{dislikes:t.dislikes,likes:t.likes}))}},_updateSummary:function(e){var t=this._containers.get(e);if(t.likes){elShow(t.summary.parentNode);for(var i=[],a=Object.keys(t.users),o=0,r=a.length;o<r;o++)i.push(t.users[a[o]]);var s=t.likes-i.length;t.summary.innerHTML=n.get("wcf.like.summary",{users:i,others:s})}else elHide(t.summary.parentNode)},_updateActiveState:function(e){var t=this._containers.get(e),i=this._options.markListItemAsActive?t.likeButton.parentNode:t.likeButton;if(i.classList.remove("active"),1===t.liked&&i.classList.add("active"),this._options.canDislike){var n=this._options.markListItemAsActive?t.dislikeButton.parentNode:t.dislikeButton;n.classList.remove("active"),-1===t.liked&&n.classList.add("active")}},_like:function(t,i){i.preventDefault(),h||(h=!0,e.api(this,{actionName:elData(i.currentTarget,"type"),parameters:{data:{containerID:s.identify(t),objectID:this._containers.get(t).objectId,objectType:this._objectType}}}))},_ajaxSuccess:function(e){var t=elById(e.returnValues.containerID),i=this._containers.get(t);if(void 0!==i){i.dislikes=~~e.returnValues.dislikes,i.likes=~~e.returnValues.likes;var n=e.returnValues.users;i.users=[];for(var a=Object.keys(n),r=0,s=a.length;r<s;r++)i.users.push(o.escapeHTML(n[a[r]].username));1==e.returnValues.isLiked?i.liked=1:1==e.returnValues.isDisliked?i.liked=-1:i.liked=0,this._updateBadge(t),this._options.canViewSummary&&this._updateSummary(t),this._updateActiveState(t),this._details.delete(t),h=!1}},_ajaxSetup:function(){return{data:{className:"wcf\\data\\like\\LikeAction"}}}},u}),define("WoltLabSuite/Core/Ui/Message/InlineEditor",["Ajax","Core","Dictionary","Environment","EventHandler","Language","ObjectMap","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Notification","Ui/ReusableDropdown","WoltLabSuite/Core/Ui/Scroll"],function(e,t,i,n,a,o,r,s,l,c,d,u,h){"use strict";function p(e){this.init(e)}return p.prototype={init:function(e){this._activeDropdownElement=null,this._activeElement=null,this._dropdownMenu=null,this._elements=new r,this._options=t.extend({canEditInline:!1,className:"",containerId:0,dropdownIdentifier:"",editorPrefix:"messageEditor",messageSelector:".jsMessage",quoteManager:null},e),this.rebuild(),s.add("Ui/Message/InlineEdit_"+this._options.className,this.rebuild.bind(this))},rebuild:function(){for(var e,t,i,n=elBySelAll(this._options.messageSelector),a=0,o=n.length;a<o;a++)if(i=n[a],!this._elements.has(i)){e=elBySel(".jsMessageEditButton",i),null!==e&&(t=elDataBool(i,"can-edit"),this._options.canEditInline||elDataBool(i,"can-edit-inline")?(e.addEventListener(WCF_CLICK_EVENT,this._clickDropdown.bind(this,i)),e.classList.add("jsDropdownEnabled"),t&&e.addEventListener("dblclick",this._click.bind(this,i))):t&&e.addEventListener(WCF_CLICK_EVENT,this._click.bind(this,i)));var r=elBySel(".messageBody",i),s=elBySel(".messageFooter",i),l=elBySel(".messageHeader",i);this._elements.set(i,{button:e,messageBody:r,messageBodyEditor:null,messageFooter:s,messageFooterButtons:elBySel(".messageFooterButtons",s),messageHeader:l,messageText:elBySel(".messageText",r)})}},_click:function(t,i){null===t&&(t=this._activeDropdownElement),i&&i.preventDefault(),null===this._activeElement?(this._activeElement=t,this._prepare(),e.api(this,{actionName:"beginEdit",parameters:{containerID:this._options.containerId,objectID:this._getObjectId(t)}})):d.show("wcf.message.error.editorAlreadyInUse",null,"warning")},_clickDropdown:function(e,i){i.preventDefault();var n=i.currentTarget;if(!n.classList.contains("dropdownToggle")){if(n.classList.add("dropdownToggle"),n.parentNode.classList.add("dropdown"),function(e,t){e.addEventListener(WCF_CLICK_EVENT,function(i){i.preventDefault(),i.stopPropagation(),this._activeDropdownElement=t,u.toggleDropdown(this._options.dropdownIdentifier,e)}.bind(this))}.bind(this)(n,e),null===this._dropdownMenu){this._dropdownMenu=elCreate("ul"),this._dropdownMenu.className="dropdownMenu";var o=this._dropdownGetItems();a.fire("com.woltlab.wcf.inlineEditor","dropdownInit_"+this._options.dropdownIdentifier,{items:o}),this._dropdownBuild(o),u.init(this._options.dropdownIdentifier,this._dropdownMenu),u.registerCallback(this._options.dropdownIdentifier,this._dropdownToggle.bind(this))}setTimeout(function(){t.triggerEvent(n,WCF_CLICK_EVENT)},10)}},_dropdownBuild:function(e){for(var t,i,n,a=this._clickDropdownItem.bind(this),r=0,s=e.length;r<s;r++)t=e[r],n=elCreate("li"),elData(n,"item",t.item),"divider"===t.item?n.className="dropdownDivider":(i=elCreate("span"),i.textContent=o.get(t.label),n.appendChild(i),"editItem"===t.item?n.addEventListener(WCF_CLICK_EVENT,this._click.bind(this,null)):n.addEventListener(WCF_CLICK_EVENT,a)),this._dropdownMenu.appendChild(n)},_dropdownToggle:function(e,t){var i=this._elements.get(this._activeDropdownElement);if(i.button.parentNode.classList["open"===t?"add":"remove"]("dropdownOpen"),i.messageFooterButtons.classList["open"===t?"add":"remove"]("forceVisible"),"open"===t){var n=this._dropdownOpen();a.fire("com.woltlab.wcf.inlineEditor","dropdownOpen_"+this._options.dropdownIdentifier,{element:this._activeDropdownElement,visibility:n});for(var o,r,s=!1,l=0;l<this._dropdownMenu.childElementCount;l++)r=this._dropdownMenu.children[l],o=elData(r,"item"),"divider"===o?s?(elShow(r),s=!1):elHide(r):objOwns(n,o)&&!1===n[o]?(elHide(r),l>0&&l+1===this._dropdownMenu.childElementCount&&"divider"===elData(r.previousElementSibling,"item")&&elHide(r.previousElementSibling)):(elShow(r),s=!0)}},_dropdownGetItems:function(){},_dropdownOpen:function(){},_dropdownSelect:function(e){},_clickDropdownItem:function(e){e.preventDefault();var t=elData(e.currentTarget,"item"),i={cancel:!1,element:this._activeDropdownElement,item:t};a.fire("com.woltlab.wcf.inlineEditor","dropdownItemClick_"+this._options.dropdownIdentifier,i),!0===i.cancel?e.preventDefault():this._dropdownSelect(t)},_prepare:function(){var e=this._elements.get(this._activeElement),t=elCreate("div");t.className="messageBody editor",e.messageBodyEditor=t;var i=elCreate("span");i.className="icon icon48 fa-spinner",t.appendChild(i),c.insertAfter(t,e.messageBody),elHide(e.messageBody)},_showEditor:function(e){var t=this._getEditorId(),i=this._elements.get(this._activeElement);this._activeElement.classList.add("jsInvalidQuoteTarget");var o=l.childByClass(i.messageBodyEditor,"icon");elRemove(o);var r=i.messageBodyEditor,s=elCreate("div");s.className="editorContainer",c.setInnerHtml(s,e.returnValues.template),r.appendChild(s);var d=elBySel(".formSubmit",s);elBySel('button[data-type="save"]',d).addEventListener(WCF_CLICK_EVENT,this._save.bind(this)),elBySel('button[data-type="cancel"]',d).addEventListener(WCF_CLICK_EVENT,this._restoreMessage.bind(this)),a.add("com.woltlab.wcf.redactor","submitEditor_"+t,function(e){e.cancel=!0,this._save()}.bind(this)),elHide(i.messageHeader),elHide(i.messageFooter);var u=elById(t);"redactor"===n.editor()?window.setTimeout(function(){this._options.quoteManager&&this._options.quoteManager.setAlternativeEditor(t),h.element(this._activeElement)}.bind(this),250):u.focus()},_restoreMessage:function(){var e=this._elements.get(this._activeElement);this._destroyEditor(),elRemove(e.messageBodyEditor),e.messageBodyEditor=null,elShow(e.messageBody),elShow(e.messageFooter),elShow(e.messageHeader),this._activeElement.classList.remove("jsInvalidQuoteTarget"),this._activeElement=null,this._options.quoteManager&&this._options.quoteManager.clearAlternativeEditor()},_save:function(){var t={containerID:this._options.containerId,data:{message:""},objectID:this._getObjectId(this._activeElement),removeQuoteIDs:this._options.quoteManager?this._options.quoteManager.getQuotesMarkedForRemoval():[]},i=this._getEditorId(),n=elById("settings_"+i);n&&elBySelAll("input, select, textarea",n,function(e){if("INPUT"!==e.nodeName||"checkbox"!==e.type&&"radio"!==e.type||e.checked){var i=e.name;if(t.hasOwnProperty(i))throw new Error("Variable overshadowing, key '"+i+"' is already present.");t[i]=e.value.trim()}}),a.fire("com.woltlab.wcf.redactor2","getText_"+i,t.data),this._validate(t)&&(a.fire("com.woltlab.wcf.redactor2","submit_"+i,t),e.api(this,{actionName:"save",parameters:t}),this._hideEditor())},_validate:function(e){elBySelAll(".innerError",this._activeElement,elRemove);var t={api:this,parameters:e,valid:!0};return a.fire("com.woltlab.wcf.redactor2","validate_"+this._getEditorId(),t),!1!==t.valid},throwError:function(e,t){elInnerError(e,t)},_showMessage:function(e){
-var t=this._activeElement,i=this._getEditorId(),n=this._elements.get(t),o=elBySelAll(".attachmentThumbnailList, .attachmentFileList",n.messageFooter);if(c.setInnerHtml(l.childByClass(n.messageBody,"messageText"),e.returnValues.message),"string"==typeof e.returnValues.attachmentList){for(var r=0,s=o.length;r<s;r++)elRemove(o[r]);var u=elCreate("div");c.setInnerHtml(u,e.returnValues.attachmentList);for(var h;u.childNodes.length;)h=u.childNodes[u.childNodes.length-1],n.messageFooter.insertBefore(h,n.messageFooter.firstChild)}if("string"==typeof e.returnValues.poll){var p=elBySel(".pollContainer",n.messageBody);null!==p&&elRemove(p.parentNode);var f=elCreate("div");f.className="jsInlineEditorHideContent",c.setInnerHtml(f,e.returnValues.poll),c.prepend(f,n.messageBody)}this._restoreMessage(),this._updateHistory(this._getHash(this._getObjectId(t))),a.fire("com.woltlab.wcf.redactor","autosaveDestroy_"+i),d.show(),this._options.quoteManager&&(this._options.quoteManager.clearAlternativeEditor(),this._options.quoteManager.countQuotes())},_hideEditor:function(){var e=this._elements.get(this._activeElement);elHide(l.childByClass(e.messageBodyEditor,"editorContainer"));var t=elCreate("span");t.className="icon icon48 fa-spinner",e.messageBodyEditor.appendChild(t)},_restoreEditor:function(){var e=this._elements.get(this._activeElement),t=elBySel(".fa-spinner",e.messageBodyEditor);elRemove(t);var i=l.childByClass(e.messageBodyEditor,"editorContainer");null!==i&&elShow(i)},_destroyEditor:function(){a.fire("com.woltlab.wcf.redactor2","autosaveDestroy_"+this._getEditorId()),a.fire("com.woltlab.wcf.redactor2","destroy_"+this._getEditorId())},_getHash:function(e){return"#message"+e},_updateHistory:function(e){window.location.hash=e},_getEditorId:function(){return this._options.editorPrefix+this._getObjectId(this._activeElement)},_getObjectId:function(e){return~~elData(e,"object-id")},_ajaxFailure:function(e){var t=this._elements.get(this._activeElement),i=elBySel(".redactor-layer",t.messageBodyEditor);return null===i?(this._restoreMessage(),!0):(this._restoreEditor(),!e||void 0===e.returnValues||void 0===e.returnValues.realErrorMessage||(elInnerError(i,e.returnValues.realErrorMessage),!1))},_ajaxSuccess:function(e){switch(e.actionName){case"beginEdit":this._showEditor(e);break;case"save":this._showMessage(e)}},_ajaxSetup:function(){return{data:{className:this._options.className,interfaceName:"wcf\\data\\IMessageInlineEditorAction"},silent:!0}},legacyEdit:function(e){this._click(elById(e),null)}},p}),define("WoltLabSuite/Core/Ui/Message/Manager",["Ajax","Core","Dictionary","Language","Dom/ChangeListener","Dom/Util"],function(e,t,i,n,a,o){"use strict";function r(e){this.init(e)}return r.prototype={init:function(e){this._elements=null,this._options=t.extend({className:"",selector:""},e),this.rebuild(),a.add("Ui/Message/Manager"+this._options.className,this.rebuild.bind(this))},rebuild:function(){this._elements=new i;for(var e,t=elBySelAll(this._options.selector),n=0,a=t.length;n<a;n++)e=t[n],this._elements.set(elData(e,"object-id"),e)},getPermission:function(e,t){t="can-"+this._getAttributeName(t);var i=this._elements.get(e);if(void 0===i)throw new Error("Unknown object id '"+e+"' for selector '"+this._options.selector+"'");return elDataBool(i,t)},getPropertyValue:function(e,t,i){var n=this._elements.get(e);if(void 0===n)throw new Error("Unknown object id '"+e+"' for selector '"+this._options.selector+"'");return window[i?"elDataBool":"elData"](n,this._getAttributeName(t))},update:function(t,i,n){e.api(this,{actionName:i,parameters:n||{},objectIDs:[t]})},updateItems:function(e,t){Array.isArray(e)||(e=[e]);for(var i,n=0,a=e.length;n<a;n++)if(void 0!==(i=this._elements.get(e[n])))for(var o in t)t.hasOwnProperty(o)&&this._update(i,o,t[o])},updateAllItems:function(e){var t=[];this._elements.forEach(function(e,i){t.push(i)}.bind(this)),this.updateItems(t,e)},setNote:function(e,t,i){var n=this._elements.get(e);if(void 0===n)throw new Error("Unknown object id '"+e+"' for selector '"+this._options.selector+"'");var a=elBySel(".messageFooterNotes",n),o=elBySel("."+t,a);i?(null===o&&(o=elCreate("p"),o.className="messageFooterNote "+t,a.appendChild(o)),o.innerHTML=i):null!==o&&elRemove(o)},_update:function(e,t,i){elData(e,this._getAttributeName(t),i);var n=1==i||!0===i||"true"===i;this._updateState(e,t,i,n)},_updateState:function(e,t,i,n){switch(t){case"isDeleted":e.classList[n?"add":"remove"]("messageDeleted"),this._toggleMessageStatus(e,"jsIconDeleted","wcf.message.status.deleted","red",n);break;case"isDisabled":e.classList[n?"add":"remove"]("messageDisabled"),this._toggleMessageStatus(e,"jsIconDisabled","wcf.message.status.disabled","green",n)}},_toggleMessageStatus:function(e,t,i,a,r){var s=elBySel(".messageStatus",e);if(null===s){var l=elBySel(".messageHeaderMetaData",e);if(null===l)return;s=elCreate("ul"),s.className="messageStatus",o.insertAfter(s,l)}var c=elBySel("."+t,s);if(r){if(null!==c)return;c=elCreate("span"),c.className="badge label "+a+" "+t,c.textContent=n.get(i);var d=elCreate("li");d.appendChild(c),s.appendChild(d)}else{if(null===c)return;elRemove(c.parentNode)}},_getAttributeName:function(e){if(-1!==e.indexOf("-"))return e;for(var t,i="",n=e.split(/([A-Z][a-z]+)/),a=0,o=n.length;a<o;a++)t=n[a],t.length&&(i.length&&(i+="-"),i+=t.toLowerCase());return i},_ajaxSuccess:function(){throw new Error("Method _ajaxSuccess() must be implemented by deriving functions.")},_ajaxSetup:function(){return{data:{className:this._options.className}}}},r}),define("WoltLabSuite/Core/Ui/Message/Reply",["Ajax","Core","EventHandler","Language","Dom/ChangeListener","Dom/Util","Dom/Traverse","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Ui/Scroll","EventKey","User","WoltLabSuite/Core/Controller/Captcha"],function(e,t,i,n,a,o,r,s,l,c,d,u,h){"use strict";function p(e){this.init(e)}return p.prototype={init:function(e){this._options=t.extend({ajax:{className:""},quoteManager:null,successMessage:"wcf.global.success.add"},e),this._container=elById("messageQuickReply"),this._content=elBySel(".messageContent",this._container),this._textarea=elById("text"),this._editor=null,this._guestDialogId="",this._loadingOverlay=null,elBySel(".message",this._container).classList.add("jsInvalidQuoteTarget");var i=this._submit.bind(this);elBySel('button[data-type="save"]').addEventListener(WCF_CLICK_EVENT,i);for(var n=elBySelAll(".jsQuickReply"),a=0,o=n.length;a<o;a++)n[a].addEventListener(WCF_CLICK_EVENT,function(e){e.preventDefault(),this._getEditor().WoltLabReply.showEditor(),c.element(this._container,function(){this._getEditor().WoltLabCaret.endOfEditor()}.bind(this))}.bind(this))},_submitGuestDialog:function(e){if("keypress"!==e.type||d.Enter(e)){var i=elBySel("input[name=username]",e.currentTarget.closest(".dialogContent"));if(""===i.value)return elInnerError(i,n.get("wcf.global.form.error.empty")),void i.closest("dl").classList.add("formError");var a={parameters:{data:{username:i.value}}},o=elData(e.currentTarget,"captcha-id");if(h.has(o)){var r=h.getData(o);r instanceof Promise?r.then(function(e){a=t.extend(a,e),this._submit(void 0,a)}.bind(this)):(a=t.extend(a,h.getData(o)),this._submit(void 0,a))}else this._submit(void 0,a)}},_submit:function(n,a){if(n&&n.preventDefault(),(!this._content.classList.contains("loading")||this._guestDialogId&&s.isOpen(this._guestDialogId))&&this._validate()){this._showLoadingOverlay();var r=o.getDataAttributes(this._container,"data-",!0,!0);r.data={message:this._getEditor().code.get()},r.removeQuoteIDs=this._options.quoteManager?this._options.quoteManager.getQuotesMarkedForRemoval():[];var l=elById("settings_text");l&&elBySelAll("input, select, textarea",l,function(e){if("INPUT"!==e.nodeName||"checkbox"!==e.type&&"radio"!==e.type||e.checked){var t=e.name;if(r.hasOwnProperty(t))throw new Error("Variable overshadowing, key '"+t+"' is already present.");r[t]=e.value.trim()}}),i.fire("com.woltlab.wcf.redactor2","submit_text",r.data),u.userId||a||(r.requireGuestDialog=!0),e.api(this,t.extend({parameters:r},a))}},_validate:function(){if(elBySelAll(".innerError",this._container,elRemove),this._getEditor().utils.isEmpty())return this.throwError(this._textarea,n.get("wcf.global.form.error.empty")),!1;var e={api:this,editor:this._getEditor(),message:this._getEditor().code.get(),valid:!0};return i.fire("com.woltlab.wcf.redactor2","validate_text",e),!1!==e.valid},throwError:function(e,t){elInnerError(e,"empty"===t?n.get("wcf.global.form.error.empty"):t)},_showLoadingOverlay:function(){null===this._loadingOverlay&&(this._loadingOverlay=elCreate("div"),this._loadingOverlay.className="messageContentLoadingOverlay",this._loadingOverlay.innerHTML='<span class="icon icon96 fa-spinner"></span>'),this._content.classList.add("loading"),this._content.appendChild(this._loadingOverlay)},_hideLoadingOverlay:function(){this._content.classList.remove("loading");var e=elBySel(".messageContentLoadingOverlay",this._content);null!==e&&e.parentNode.removeChild(e)},_reset:function(){this._getEditor().code.set("<p></p>"),i.fire("com.woltlab.wcf.redactor2","reset_text")},_handleError:function(e){var t={api:this,cancel:!1,returnValues:e.returnValues};i.fire("com.woltlab.wcf.redactor2","handleError_text",t),!0!==t.cancel&&this.throwError(this._textarea,e.returnValues.realErrorMessage)},_getEditor:function(){if(null===this._editor){if("function"!=typeof window.jQuery)throw new Error("Unable to access editor, jQuery has not been loaded yet.");this._editor=window.jQuery(this._textarea).data("redactor")}return this._editor},_insertMessage:function(e){if(this._getEditor().WoltLabAutosave.reset(),e.returnValues.url)window.location=e.returnValues.url;else{if(e.returnValues.template){var t;if("DESC"===elData(this._container,"sort-order"))o.insertHtml(e.returnValues.template,this._container,"after"),t=o.identify(this._container.nextElementSibling);else{var i=this._container;i.previousElementSibling&&i.previousElementSibling.classList.contains("messageListPagination")&&(i=i.previousElementSibling),o.insertHtml(e.returnValues.template,i,"before"),t=o.identify(i.previousElementSibling)}elData(this._container,"last-post-time",e.returnValues.lastPostTime),window.history.replaceState(void 0,"","#"+t),c.element(elById(t))}l.show(n.get(this._options.successMessage)),this._options.quoteManager&&this._options.quoteManager.countQuotes(),a.trigger()}},_ajaxSuccess:function(e){if(!u.userId&&!e.returnValues.guestDialogID)throw new Error("Missing 'guestDialogID' return value for guest.");if(!u.userId&&e.returnValues.guestDialog){s.openStatic(e.returnValues.guestDialogID,e.returnValues.guestDialog,{closable:!1,title:n.get("wcf.global.confirmation.title")});var t=s.getDialog(e.returnValues.guestDialogID);elBySel("input[type=submit]",t.content).addEventListener(WCF_CLICK_EVENT,this._submitGuestDialog.bind(this)),elBySel("input[type=text]",t.content).addEventListener("keypress",this._submitGuestDialog.bind(this)),this._guestDialogId=e.returnValues.guestDialogID}else this._insertMessage(e),u.userId||s.close(e.returnValues.guestDialogID),this._reset(),this._hideLoadingOverlay()},_ajaxFailure:function(e){return this._hideLoadingOverlay(),null===e||void 0===e.returnValues||void 0===e.returnValues.realErrorMessage||(this._handleError(e),!1)},_ajaxSetup:function(){return{data:{actionName:"quickReply",className:this._options.ajax.className,interfaceName:"wcf\\data\\IMessageQuickReplyAction"},silent:!0}}},p}),define("WoltLabSuite/Core/Ui/Message/Share",["EventHandler"],function(e){"use strict";return{_pageDescription:"",_pageUrl:"",init:function(){var t=elBySel('meta[property="og:title"]');null!==t&&(this._pageDescription=encodeURIComponent(t.content));var i=elBySel('meta[property="og:url"]');null!==i&&(this._pageUrl=encodeURIComponent(i.content)),elBySelAll(".jsMessageShareButtons",null,function(t){t.classList.remove("jsMessageShareButtons");var i={facebook:{link:elBySel(".jsShareFacebook",t),share:function(){this._share("facebook","https://www.facebook.com/sharer.php?u={pageURL}&t={text}",!0)}.bind(this)},google:{link:elBySel(".jsShareGoogle",t),share:function(){this._share("google","https://plus.google.com/share?url={pageURL}",!1)}.bind(this)},reddit:{link:elBySel(".jsShareReddit",t),share:function(){this._share("reddit","https://ssl.reddit.com/submit?url={pageURL}",!1)}.bind(this)},twitter:{link:elBySel(".jsShareTwitter",t),share:function(){this._share("twitter","https://twitter.com/share?url={pageURL}&text={text}",!1)}.bind(this)},linkedIn:{link:elBySel(".jsShareLinkedIn",t),share:function(){this._share("linkedIn","https://www.linkedin.com/cws/share?url={pageURL}",!1)}.bind(this)},pinterest:{link:elBySel(".jsSharePinterest",t),share:function(){this._share("pinterest","https://www.pinterest.com/pin/create/link/?url={pageURL}&description={text}",!1)}.bind(this)},xing:{link:elBySel(".jsShareXing",t),share:function(){this._share("xing","https://www.xing.com/social_plugins/share?url={pageURL}",!1)}.bind(this)},whatsApp:{link:elBySel(".jsShareWhatsApp",t),share:function(){window.location.href="whatsapp://send?text="+this._pageDescription+"%20"+this._pageUrl}.bind(this)}};e.fire("com.woltlab.wcf.message.share","shareProvider",{container:t,providers:i,pageDescription:this._pageDescription,pageUrl:this._pageUrl});for(var n in i)i.hasOwnProperty(n)&&null!==i[n].link&&i[n].link.addEventListener(WCF_CLICK_EVENT,i[n].share)}.bind(this))},_share:function(e,t,i){window.open(t.replace(/\{pageURL}/,this._pageUrl).replace(/\{text}/,this._pageDescription+(i?"%20"+this._pageUrl:"")),e,"height=600,width=600")}}}),define("WoltLabSuite/Core/Ui/Page/Search",["Ajax","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog"],function(e,t,i,n,a,o){"use strict";var r,s,l,c=null;return{open:function(e){r=e,o.open(this)},_search:function(t){t.preventDefault();var n=c.parentNode,a=c.value.trim();if(a.length<3)return void elInnerError(n,i.get("wcf.page.search.error.tooShort"));elInnerError(n,!1),e.api(this,{parameters:{searchString:a}})},_click:function(e){e.preventDefault(),r(elData(e.currentTarget,"page-id")),o.close(this)},_ajaxSuccess:function(e){for(var t,a="",o=0,r=e.returnValues.length;o<r;o++)t=e.returnValues[o],a+='<li><div class="containerHeadline pointer" data-page-id="'+t.pageID+'"><h3>'+n.escapeHTML(t.name)+"</h3><small>"+n.escapeHTML(t.displayLink)+"</small></div></li>";l.innerHTML=a,window[a?"elShow":"elHide"](s),a?elBySelAll(".containerHeadline",l,function(e){e.addEventListener(WCF_CLICK_EVENT,this._click.bind(this))}.bind(this)):elInnerError(c.parentNode,i.get("wcf.page.search.error.noResults"))},_ajaxSetup:function(){return{data:{actionName:"search",className:"wcf\\data\\page\\PageAction"}}},_dialogSetup:function(){return{id:"wcfUiPageSearch",options:{onSetup:function(){var e=this._search.bind(this);c=elById("wcfUiPageSearchInput"),c.addEventListener("keydown",function(i){t.Enter(i)&&e(i)}),c.nextElementSibling.addEventListener(WCF_CLICK_EVENT,e),s=elById("wcfUiPageSearchResultContainer"),l=elById("wcfUiPageSearchResultList")}.bind(this),onShow:function(){c.focus()},title:i.get("wcf.page.search")},source:'<div class="section"><dl><dt><label for="wcfUiPageSearchInput">'+i.get("wcf.page.search.name")+'</label></dt><dd><div class="inputAddon"><input type="text" id="wcfUiPageSearchInput" class="long"><a href="#" class="inputSuffix"><span class="icon icon16 fa-search"></span></a></div></dd></dl></div><section id="wcfUiPageSearchResultContainer" class="section" style="display: none;"><header class="sectionHeader"><h2 class="sectionTitle">'+i.get("wcf.page.search.results")+'</h2></header><ol id="wcfUiPageSearchResultList" class="containerList"></ol></section>'}}}}),define("WoltLabSuite/Core/Ui/Redactor/Metacode",["EventHandler","Dom/Util"],function(e,t){"use strict";return{convert:function(e){e.textContent=this.convertFromHtml(e.textContent)},convertFromHtml:function(i,n){var a=elCreate("div");a.innerHTML=n;for(var o,r,s,l,c,d,u=elByTag("woltlab-metacode",a);u.length;)s=u[0],l=elData(s,"name"),o=this._parseAttributes(elData(s,"attributes")),r={attributes:o,cancel:!1,metacode:s},e.fire("com.woltlab.wcf.redactor2","metacode_"+l+"_"+i,r),!0!==r.cancel&&(d=this._getOpeningTag(l,o),c=this._getClosingTag(l),s.parentNode===a?(t.prepend(d,this._getFirstParagraph(s)),this._getLastParagraph(s).appendChild(c)):(t.prepend(d,s),s.appendChild(c)),t.unwrapChildNodes(s));for(var h,p=elByTag("kbd",a);p.length;)h=p[0],h.insertBefore(document.createTextNode("[tt]"),h.firstChild),h.appendChild(document.createTextNode("[/tt]")),t.unwrapChildNodes(h);return a.innerHTML},_getOpeningTag:function(e,t){var i="["+e;if(t.length){i+="=";for(var n=0,a=t.length;n<a;n++)n>0&&(i+=","),i+="'"+t[n]+"'"}return document.createTextNode(i+"]")},_getClosingTag:function(e){return document.createTextNode("[/"+e+"]")},_getFirstParagraph:function(e){var t,i;return 0===e.childElementCount?(i=elCreate("p"),e.appendChild(i)):(t=e.children[0],"P"===t.nodeName?i=t:(i=elCreate("p"),e.insertBefore(i,t))),i},_getLastParagraph:function(e){var t,i,n=e.childElementCount;return 0===n?(i=elCreate("p"),e.appendChild(i)):(t=e.children[n-1],"P"===t.nodeName?i=t:(i=elCreate("p"),e.appendChild(i))),i},_parseAttributes:function(e){try{e=JSON.parse(atob(e))}catch(e){}if(!Array.isArray(e))return[];for(var t,i=[],n=0,a=e.length;n<a;n++)t=e[n],"string"==typeof t&&(t=t.replace(/^'(.*)'$/,"$1")),i.push(t);return i}}}),define("WoltLabSuite/Core/Ui/Redactor/Autosave",["Core","Devtools","EventHandler","Language","Dom/Traverse","./Metacode"],function(e,t,i,n,a,o){"use strict";function r(e){this.init(e)}return r.prototype={init:function(t){this._container=null,this._metaData={},this._editor=null,this._element=t,this._isActive=!0,this._isPending=!1,this._key=e.getStoragePrefix()+elData(this._element,"autosave"),this._lastMessage="",this._originalMessage="",this._overlay=null,this._restored=!1,this._timer=null,this._cleanup(),this._element.removeAttribute("data-autosave");var n=a.parentByTag(this._element,"FORM");null!==n&&n.addEventListener("submit",this.destroy.bind(this)),i.add("com.woltlab.wcf.redactor2","getMetaData_"+this._element.id,function(e){for(var t in this._metaData)this._metaData.hasOwnProperty(t)&&(e[t]=this._metaData[t])}.bind(this)),i.add("com.woltlab.wcf.redactor2","reset_"+this._element.id,this.hideOverlay.bind(this)),document.addEventListener("visibilitychange",this._onVisibilityChange.bind(this))},_onVisibilityChange:function(){document.hidden?(this._isActive=!1,this._isPending=!0):(this._isActive=!0,this._isPending=!1)},getInitialValue:function(){if(window.ENABLE_DEVELOPER_TOOLS&&!1===t._internal_.editorAutosave())return this._element.value;var e="";try{e=window.localStorage.getItem(this._key)}catch(e){window.console.warn("Unable to access local storage: "+e.message)}try{e=JSON.parse(e)}catch(t){e=""}if(null!==e&&"object"==typeof e&&e.content){if(1e3*~~elData(this._element,"autosave-last-edit-time")<=e.timestamp)return this._originalMessage=this._element.value,this._restored=!0,this._metaData=e.meta||{},e.content}return this._element.value},getMetaData:function(){return this._metaData},watch:function(e){if(this._editor=e,null!==this._timer)throw new Error("Autosave timer is already active.");this._timer=window.setInterval(this._saveToStorage.bind(this),15e3),this._saveToStorage(),this._isPending=!1},destroy:function(){this.clear(),this._editor=null,window.clearInterval(this._timer),this._timer=null,this._isPending=!1},clear:function(){this._metaData={},this._lastMessage="";try{window.localStorage.removeItem(this._key)}catch(e){window.console.warn("Unable to remove from local storage: "+e.message)}},createOverlay:function(){if(this._restored){var e=elCreate("div");e.className="redactorAutosaveRestored active";var t=elCreate("span");t.textContent=n.get("wcf.editor.autosave.restored"),e.appendChild(t);var i=elCreate("a");i.className="jsTooltip",i.href="#",i.title=n.get("wcf.editor.autosave.keep"),i.innerHTML='<span class="icon icon16 fa-check green"></span>',i.addEventListener(WCF_CLICK_EVENT,function(e){e.preventDefault(),this.hideOverlay()}.bind(this)),e.appendChild(i),i=elCreate("a"),i.className="jsTooltip",i.href="#",i.title=n.get("wcf.editor.autosave.discard"),i.innerHTML='<span class="icon icon16 fa-times red"></span>',i.addEventListener(WCF_CLICK_EVENT,function(e){e.preventDefault(),this.clear();var t=o.convertFromHtml(this._editor.core.element()[0].id,this._originalMessage);this._editor.code.start(t),this._editor.core.textarea().val(this._editor.clean.onSync(this._editor.$editor.html())),this.hideOverlay()}.bind(this)),e.appendChild(i),this._editor.core.box()[0].appendChild(e);var a=function(){this._editor.core.editor()[0].removeEventListener(WCF_CLICK_EVENT,a),this.hideOverlay()}.bind(this);this._editor.core.editor()[0].addEventListener(WCF_CLICK_EVENT,a),this._container=e}},hideOverlay:function(){null!==this._container&&(this._container.classList.remove("active"),window.setTimeout(function(){null!==this._container&&elRemove(this._container),this._container=null,this._originalMessage=""}.bind(this),1e3))},_saveToStorage:function(){if(!this._isActive){if(!this._isPending)return;this._isPending=!1}if(!window.ENABLE_DEVELOPER_TOOLS||!1!==t._internal_.editorAutosave()){var e=this._editor.code.get();if(this._editor.utils.isEmpty(e)&&(e=""),this._lastMessage!==e){if(""===e)return this.clear();try{i.fire("com.woltlab.wcf.redactor2","autosaveMetaData_"+this._element.id,this._metaData),window.localStorage.setItem(this._key,JSON.stringify({content:e,meta:this._metaData,timestamp:Date.now()})),this._lastMessage=e}catch(e){window.console.warn("Unable to write to local storage: "+e.message)}}}},_cleanup:function(){var t,i,n,a,o=Date.now()-6048e5,r=[];for(t=0,n=window.localStorage.length;t<n;t++)if(i=window.localStorage.key(t),0===i.indexOf(e.getStoragePrefix())){try{a=window.localStorage.getItem(i)}catch(e){window.console.warn("Unable to access local storage: "+e.message)}try{a=JSON.parse(a)}catch(e){a={timestamp:0}}(!a||a.timestamp<o)&&r.push(i)}for(t=0,n=r.length;t<n;t++)try{window.localStorage.removeItem(r[t])}catch(e){window.console.warn("Unable to remove from local storage: "+e.message)}}},r}),define("WoltLabSuite/Core/Ui/Redactor/PseudoHeader",[],function(){"use strict";return{getHeight:function(e){var t=~~window.getComputedStyle(e).paddingTop.replace(/px$/,""),i=window.getComputedStyle(e,"::before");t+=~~i.paddingTop.replace(/px$/,""),t+=~~i.paddingBottom.replace(/px$/,"");var n=~~i.height.replace(/px$/,"");return 0===n&&(n=e.scrollHeight,e.classList.add("redactorCalcHeight"),n-=e.scrollHeight,e.classList.remove("redactorCalcHeight")),t+=n}}}),define("WoltLabSuite/Core/Ui/Redactor/Code",["EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./PseudoHeader"],function(e,t,i,n,a,o,r){"use strict";function s(e){this.init(e)}var l=0;return s.prototype={init:function(t){this._editor=t,this._elementId=this._editor.$element[0].id,this._pre=null,e.add("com.woltlab.wcf.redactor2","bbcode_code_"+this._elementId,this._bbcodeCode.bind(this)),e.add("com.woltlab.wcf.redactor2","observe_load_"+this._elementId,this._observeLoad.bind(this)),this._editor.opts.activeButtonsStates.pre="code",this._callbackEdit=this._edit.bind(this),this._observeLoad()},_bbcodeCode:function(e){e.cancel=!0;var t=this._editor.selection.block();t&&"PRE"===t.nodeName&&t.classList.contains("woltlabHtml")||(this._editor.button.toggle({},"pre","func","block.format"),(t=this._editor.selection.block())&&"PRE"===t.nodeName&&!t.classList.contains("woltlabHtml")&&(1===t.childElementCount&&"BR"===t.children[0].nodeName&&t.removeChild(t.children[0]),this._setTitle(t),t.addEventListener(WCF_CLICK_EVENT,this._callbackEdit),this._editor.caret.end(t)))},_observeLoad:function(){elBySelAll("pre:not(.woltlabHtml)",this._editor.$editor[0],function(e){e.addEventListener("mousedown",this._callbackEdit),this._setTitle(e)}.bind(this))},_edit:function(e){var t=e.currentTarget;0===l&&(l=r.getHeight(t));var i=a.offset(t);e.pageY>i.top&&e.pageY<i.top+l&&(e.preventDefault(),this._editor.selection.save(),this._pre=t,o.open(this))},_dialogSubmit:function(){var e="redactor-code-"+this._elementId;["file","highlighter","line"].forEach(function(t){elData(this._pre,t,elById(e+"-"+t).value)}.bind(this)),this._setTitle(this._pre),this._editor.caret.after(this._pre),o.close(this)},_setTitle:function(e){var t=elData(e,"file"),n=elData(e,"highlighter");n=this._editor.opts.woltlab.highlighters.hasOwnProperty(n)?this._editor.opts.woltlab.highlighters[n]:"";var a=i.get("wcf.editor.code.title",{file:t,highlighter:n});elData(e,"title")!==a&&elData(e,"title",a)},_delete:function(e){e.preventDefault();var t=this._pre.nextElementSibling||this._pre.previousElementSibling;null===t&&this._pre.parentNode!==this._editor.core.editor()[0]&&(t=this._pre.parentNode),null===t?(this._editor.code.set(""),this._editor.focus.end()):(elRemove(this._pre),this._editor.caret.end(t)),o.close(this)},_dialogSetup:function(){var e="redactor-code-"+this._elementId,t=e+"-button-delete",a=e+"-button-save",r=e+"-file",s=e+"-highlighter",l=e+"-line";return{id:e,options:{onClose:function(){this._editor.selection.restore(),o.destroy(this)}.bind(this),onSetup:function(){elById(t).addEventListener(WCF_CLICK_EVENT,this._delete.bind(this));var e='<option value="">'+i.get("wcf.editor.code.highlighter.detect")+"</option>",a=[];for(var o in this._editor.opts.woltlab.highlighters)this._editor.opts.woltlab.highlighters.hasOwnProperty(o)&&a.push([o,this._editor.opts.woltlab.highlighters[o]]);a.sort(function(e,t){return e[1]<t[1]?-1:e[1]>t[1]?1:0}),a.forEach(function(t){e+='<option value="'+t[0]+'">'+n.escapeHTML(t[1])+"</option>"}.bind(this)),elById(s).innerHTML=e}.bind(this),onShow:function(){elById(s).value=elData(this._pre,"highlighter");var e=elData(this._pre,"line");elById(l).value=""===e?1:~~e,elById(r).value=elData(this._pre,"file")}.bind(this),title:i.get("wcf.editor.code.edit")},source:'<div class="section"><dl><dt><label for="'+s+'">'+i.get("wcf.editor.code.highlighter")+'</label></dt><dd><select id="'+s+'"></select><small>'+i.get("wcf.editor.code.highlighter.description")+'</small></dd></dl><dl><dt><label for="'+l+'">'+i.get("wcf.editor.code.line")+'</label></dt><dd><input type="number" id="'+l+'" min="0" value="1" class="long" data-dialog-submit-on-enter="true"><small>'+i.get("wcf.editor.code.line.description")+'</small></dd></dl><dl><dt><label for="'+r+'">'+i.get("wcf.editor.code.file")+'</label></dt><dd><input type="text" id="'+r+'" class="long" data-dialog-submit-on-enter="true"><small>'+i.get("wcf.editor.code.file.description")+'</small></dd></dl></div><div class="formSubmit"><button id="'+a+'" class="buttonPrimary" data-type="submit">'+i.get("wcf.global.button.save")+'</button><button id="'+t+'">'+i.get("wcf.global.button.delete")+"</button></div>"}}},s}),define("WoltLabSuite/Core/Ui/Redactor/DragAndDrop",["Dictionary","EventHandler","Language"],function(e,t,i){"use strict";var n=!1,a=new e,o=!1,r=!1,s=null;return{init:function(e){n||this._setup(),a.set(e.uuid,{editor:e,element:null})},_dragOver:function(e){if(e.preventDefault(),e.dataTransfer&&e.dataTransfer.types){var t=!1;for(var n in e.dataTransfer)if(e.dataTransfer.hasOwnProperty(n)&&n.match(/^moz/)){t=!0;break}if(r=!1,t)"application/x-moz-file"===e.dataTransfer.types[0]&&(r=!0);else for(var s=0;s<e.dataTransfer.types.length;s++)if("Files"===e.dataTransfer.types[s]){r=!0;break}r&&(o||(o=!0,a.forEach(function(e,t){var n=e.editor.$editor[0];if(!n.parentNode)return void a.delete(t);var o=e.element;null===o&&(o=elCreate("div"),o.className="redactorDropArea",elData(o,"element-id",e.editor.$element[0].id),elData(o,"drop-here",i.get("wcf.attachment.dragAndDrop.dropHere")),elData(o,"drop-now",i.get("wcf.attachment.dragAndDrop.dropNow")),o.addEventListener("dragover",function(){o.classList.add("active")}),o.addEventListener("dragleave",function(){o.classList.remove("active")}),o.addEventListener("drop",this._drop.bind(this)),e.element=o),n.parentNode.insertBefore(o,n),o.style.setProperty("top",n.offsetTop+"px","")}.bind(this))))}},_drop:function(e){if(r&&e.dataTransfer&&e.dataTransfer.files.length){e.preventDefault();for(var i=elData(e.currentTarget,"element-id"),n=0,a=e.dataTransfer.files.length;n<a;n++)t.fire("com.woltlab.wcf.redactor2","dragAndDrop_"+i,{file:e.dataTransfer.files[n]});this._dragLeave()}},_dragLeave:function(){o&&r&&(null!==s&&window.clearTimeout(s),s=window.setTimeout(function(){o||a.forEach(function(e){e.element&&e.element.parentNode&&(e.element.classList.remove("active"),elRemove(e.element))}),s=null},100),o=!1)},_globalDrop:function(e){null===e.target.closest(".redactor-layer")&&e.preventDefault(),this._dragLeave(e)},_setup:function(){window.addEventListener("dragend",function(e){e.preventDefault()}),window.addEventListener("dragover",this._dragOver.bind(this)),window.addEventListener("dragleave",this._dragLeave.bind(this)),window.addEventListener("drop",this._globalDrop.bind(this)),n=!0}}}),define("WoltLabSuite/Core/Ui/Redactor/Format",["Dom/Util"],function(e){"use strict";var t=function(e){for(var t=window.getSelection().anchorNode;t;){if(t===e)return!0;t=t.parentNode}return!1};return{format:function(i,n,a){var o=window.getSelection();if(o.rangeCount){if(!t(i))return void console.error("Invalid selection, range exists outside of the editor:",o.anchorNode);var r=o.getRangeAt(0),s=null,l=null,c=null;if(r.collapsed)c=elCreate("strike"),c.textContent="",r.insertNode(c),r=document.createRange(),r.selectNodeContents(c),o.removeAllRanges(),o.addRange(r);else{s=elCreate("mark"),l=elCreate("mark");var d=r.cloneRange();d.collapse(!0),d.insertNode(s),d=r.cloneRange(),d.collapse(!1),d.insertNode(l),r=document.createRange(),r.setStartAfter(s),r.setEndBefore(l),o.removeAllRanges(),o.addRange(r),this.removeFormat(i,n),r=document.createRange(),r.setStartAfter(s),r.setEndBefore(l),o.removeAllRanges(),o.addRange(r)}var u=["strike","strikethrough"];null===c&&(u=this._getSelectionMarker(i,o),document.execCommand(u[1]));for(var h,p,f=elBySelAll(u[0],i),m=[],g=0,v=f.length;g<v;g++)p=f[g],h=elCreate("span"),elAttr(h,"style",n+": "+a),e.replaceElement(p,h),m.push(h);var _=m.length;if(_){var b=m[0],w=m[_-1];if(null===c&&b.parentNode===w.parentNode){var y=b.parentNode;"SPAN"===y.nodeName&&""!==y.style.getPropertyValue(n)&&this._isBoundaryElement(b,y,"previous")&&this._isBoundaryElement(w,y,"next")&&e.unwrapChildNodes(y)}r=document.createRange(),r.setStart(b,0),r.setEnd(w,w.childNodes.length),o.removeAllRanges(),o.addRange(r)}null!==s&&(elRemove(s),elRemove(l))}},removeFormat:function(i,n){var a=window.getSelection();if(a.rangeCount){if(!t(i))return void console.error("Invalid selection, range exists outside of the editor:",a.anchorNode);for(var o=elByTag("strike",i);o.length;)e.unwrapChildNodes(o[0]);var r=this._getSelectionMarker(i,window.getSelection());document.execCommand(r[1]),"strike"!==r[0]&&(o=elByTag(r[0],i));for(var s,l;o.length;)l=o[0],s=this._getLastMatchingParent(l,i,n),null!==s&&this._handleParentNodes(l,s,n),elBySelAll("span",l,function(t){t.style.getPropertyValue(n)&&e.unwrapChildNodes(t)}),e.unwrapChildNodes(l);elBySelAll("span",i,function(e){e.parentNode&&!e.textContent.length&&""!==e.style.getPropertyValue(n)&&(1===e.childElementCount&&"MARK"===e.children[0].nodeName&&e.parentNode.insertBefore(e.children[0],e),0===e.childElementCount&&elRemove(e))})}},_handleParentNodes:function(t,i,n){var a;if(!e.isAtNodeStart(t,i)){a=document.createRange(),a.setStartBefore(i),a.setEndBefore(t);var o=a.extractContents();i.parentNode.insertBefore(o,i)}e.isAtNodeEnd(t,i)||(a=document.createRange(),a.setStartAfter(t),a.setEndAfter(i),o=a.extractContents(),i.parentNode.insertBefore(o,i.nextSibling)),elBySelAll("span",i,function(t){t.style.getPropertyValue(n)&&e.unwrapChildNodes(t)}),e.unwrapChildNodes(i)},_getLastMatchingParent:function(e,t,i){for(var n=e.parentNode,a=null;n!==t;)"SPAN"===n.nodeName&&""!==n.style.getPropertyValue(i)&&(a=n),n=n.parentNode;return a},
-_isBoundaryElement:function(e,t,i){for(var n=e;n=n[i+"Sibling"];)if(n.nodeType!==Node.TEXT_NODE||""!==n.textContent.replace(/\u200B/,""))return!1;return!0},_getSelectionMarker:function(e,t){for(var i,n,a,o=["DEL","SUB","SUP"],r=0,s=o.length;r<s;r++){if(a=o[r],n=elClosest(t.anchorNode),!(i=null!==elBySel(a.toLowerCase(),n)))for(;n&&n!==e;){if(n.nodeName===a){i=!0;break}n=n.parentNode}if(!i)break;a=void 0}return"DEL"===a||void 0===a?["strike","strikethrough"]:[a.toLowerCase(),a.toLowerCase()+"script"]}}}),define("WoltLabSuite/Core/Ui/Redactor/Html",["EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./PseudoHeader"],function(e,t,i,n,a,o,r){"use strict";function s(e){this.init(e)}var l=0;return s.prototype={init:function(t){this._editor=t,this._elementId=this._editor.$element[0].id,this._pre=null,e.add("com.woltlab.wcf.redactor2","bbcode_woltlabHtml_"+this._elementId,this._bbcodeCode.bind(this)),e.add("com.woltlab.wcf.redactor2","observe_load_"+this._elementId,this._observeLoad.bind(this)),this._editor.opts.activeButtonsStates["woltlab-html"]="woltlabHtml",this._callbackEdit=this._edit.bind(this),this._observeLoad()},_bbcodeCode:function(e){e.cancel=!0;var t=this._editor.selection.block();t&&"PRE"===t.nodeName&&!t.classList.contains("woltlabHtml")||(this._editor.button.toggle({},"pre","func","block.format"),(t=this._editor.selection.block())&&"PRE"===t.nodeName&&(t.classList.add("woltlabHtml"),1===t.childElementCount&&"BR"===t.children[0].nodeName&&t.removeChild(t.children[0]),this._setTitle(t),t.addEventListener(WCF_CLICK_EVENT,this._callbackEdit),this._editor.caret.end(t)))},_observeLoad:function(){elBySelAll("pre.woltlabHtml",this._editor.$editor[0],function(e){e.addEventListener("mousedown",this._callbackEdit),this._setTitle(e)}.bind(this))},_edit:function(e){var t=e.currentTarget;0===l&&(l=r.getHeight(t));var i=a.offset(t);e.pageY>i.top&&e.pageY<i.top+l&&(e.preventDefault(),this._editor.selection.save(),this._pre=t,console.warn("should edit"))},_setTitle:function(e){["title","description"].forEach(function(t){var n=i.get("wcf.editor.html."+t);elData(e,t)!==n&&elData(e,t,n)})},_delete:function(e){console.warn("should delete"),e.preventDefault();var t=this._pre.nextElementSibling||this._pre.previousElementSibling;null===t&&this._pre.parentNode!==this._editor.core.editor()[0]&&(t=this._pre.parentNode),null===t?(this._editor.code.set(""),this._editor.focus.end()):(elRemove(this._pre),this._editor.caret.end(t)),o.close(this)}},s}),define("WoltLabSuite/Core/Ui/Redactor/Link",["Core","EventKey","Language","Ui/Dialog"],function(e,t,i,n){"use strict";var a=!1,o=null;return{showDialog:function(e){n.open(this),n.setTitle(this,i.get("wcf.editor.link."+(e.insert?"add":"edit")));var t=elById("redactor-modal-button-action");t.textContent=i.get("wcf.global.button."+(e.insert?"insert":"save")),o=e.submitCallback,a||(a=!0,t.addEventListener(WCF_CLICK_EVENT,this._submit.bind(this)))},_submit:function(){if(o())n.close(this);else{var e=elById("redactor-link-url");elInnerError(e,i.get(""===e.value.trim()?"wcf.global.form.error.empty":"wcf.editor.link.error.invalid"))}},_dialogSetup:function(){return{id:"redactorDialogLink",options:{onClose:function(){var e=elById("redactor-link-url"),t=e.nextElementSibling&&"SMALL"===e.nextElementSibling.nodeName?e.nextElementSibling:null;null!==t&&elRemove(t)},onSetup:function(i){var n=elBySel(".formSubmit > .buttonPrimary",i);null!==n&&elBySelAll('input[type="url"], input[type="text"]',i,function(i){i.addEventListener("keyup",function(i){t.Enter(i)&&e.triggerEvent(n,"click")})})},onShow:function(){elById("redactor-link-url").focus()}},source:'<dl><dt><label for="redactor-link-url">'+i.get("wcf.editor.link.url")+'</label></dt><dd><input type="url" id="redactor-link-url" class="long"></dd></dl><dl><dt><label for="redactor-link-url-text">'+i.get("wcf.editor.link.text")+'</label></dt><dd><input type="text" id="redactor-link-url-text" class="long"></dd></dl><div class="formSubmit"><button id="redactor-modal-button-action" class="buttonPrimary"></button></div>'}}}}),define("WoltLabSuite/Core/Ui/Redactor/Mention",["Ajax","Environment","StringUtil","Ui/CloseOverlay"],function(e,t,i,n){"use strict";function a(e){this.init(e)}var o=null;return a.prototype={init:function(e){this._active=!1,this._dropdownActive=!1,this._dropdownMenu=null,this._itemIndex=0,this._lineHeight=null,this._mentionStart="",this._redactor=e,this._timer=null,e.WoltLabEvent.register("keydown",this._keyDown.bind(this)),e.WoltLabEvent.register("keyup",this._keyUp.bind(this)),n.add("UiRedactorMention-"+e.core.element()[0].id,this._hideDropdown.bind(this))},_keyDown:function(e){if(this._dropdownActive){var t=e.event;switch(t.which){case 13:this._setUsername(null,this._dropdownMenu.children[this._itemIndex].children[0]);break;case 38:this._selectItem(-1);break;case 40:this._selectItem(1);break;default:return void this._hideDropdown()}t.preventDefault(),e.cancel=!0}},_keyUp:function(t){var i=t.event;if(13===i.which)return void(this._active=!1);if(!this._dropdownActive||(t.cancel=!0,38!==i.which&&40!==i.which)){var n=this._getTextLineInFrontOfCaret();if(n.length>0&&n.length<25){var a=n.match(/@([^,]{3,})$/);a?a.index&&!n[a.index-1].match(/\s/)||(this._mentionStart=a[1],null!==this._timer&&(window.clearTimeout(this._timer),this._timer=null),this._timer=window.setTimeout(function(){e.api(this,{parameters:{data:{searchString:this._mentionStart}}}),this._timer=null}.bind(this),500)):this._hideDropdown()}else this._hideDropdown()}},_getTextLineInFrontOfCaret:function(){var e=this._selectMention(!1);return null!==e?e.range.cloneContents().textContent.replace(/\u200B/g,"").replace(/\u00A0/g," ").trim():""},_getDropdownMenuPosition:function(){var e=this._selectMention();if(null===e)return null;this._redactor.selection.save(),e.selection.removeAllRanges(),e.selection.addRange(e.range);var t=e.selection.getRangeAt(0).getBoundingClientRect(),i={top:Math.round(t.bottom)+(window.scrollY||window.pageYOffset),left:Math.round(t.left)+document.body.scrollLeft};return null===this._lineHeight&&(this._lineHeight=Math.round(t.bottom-t.top)),this._redactor.selection.restore(),i},_setUsername:function(e,t){e&&(e.preventDefault(),t=e.currentTarget);var i=this._selectMention();if(null===i)return void this._hideDropdown();this._redactor.buffer.set(),i.selection.removeAllRanges(),i.selection.addRange(i.range);var n=getSelection().getRangeAt(0);n.deleteContents(),n.collapse(!0);var a=document.createTextNode("@"+elData(t,"username")+" ");n.insertNode(a),n=document.createRange(),n.selectNode(a),n.collapse(!1),i.selection.removeAllRanges(),i.selection.addRange(n),this._hideDropdown()},_selectMention:function(e){var t=window.getSelection();if(!t.rangeCount||!t.isCollapsed)return null;var i=t.anchorNode;if(i.nodeType===Node.TEXT_NODE&&(i=i.parentNode),-1===i.textContent.indexOf("@"))return null;for(var n=this._redactor.core.editor()[0];i&&i!==n;){if(-1!==["PRE","WOLTLAB-QUOTE"].indexOf(i.nodeName))return null;i=i.parentNode}for(var a=t.getRangeAt(0),o=a.startContainer,r=a.startOffset;o.nodeType===Node.ELEMENT_NODE;){if(0===r&&0===o.childNodes.length)return null;o=o.childNodes[r?r-1:0],r>0&&(r=o.nodeType===Node.TEXT_NODE?o.textContent.length:o.childNodes.length)}for(var s=o,l=-1;null!==s;){if(s.nodeType!==Node.TEXT_NODE)return null;if(-1!==s.textContent.indexOf("@")){l=s.textContent.lastIndexOf("@");break}s=s.previousSibling}if(-1===l)return null;try{a=document.createRange(),a.setStart(s,l),a.setEnd(o,r)}catch(e){return window.console.debug(e),null}if(!1===e){var c="";for(l&&(c=s.textContent.substr(0,l));(s=s.previousSibling)&&s.nodeType===Node.TEXT_NODE;)c=s.textContent+c;if(c.replace(/\u200B/g,"").match(/\S$/))return null}else if(a.cloneContents().textContent.replace(/\u200B/g,"").replace(/\u00A0/g,"").trim().replace(/^@/,"")!==this._mentionStart)return null;return{range:a,selection:t}},_updateDropdownPosition:function(){var e=this._getDropdownMenuPosition();if(null===e)return void this._hideDropdown();e.top+=7,this._dropdownMenu.style.setProperty("left",e.left+"px",""),this._dropdownMenu.style.setProperty("top",e.top+"px",""),this._selectItem(0),e.top+this._dropdownMenu.offsetHeight+10>window.innerHeight+(window.scrollY||window.pageYOffset)&&this._dropdownMenu.style.setProperty("top",e.top-this._dropdownMenu.offsetHeight-2*this._lineHeight+7+"px","")},_selectItem:function(e){var t=elBySel(".active",this._dropdownMenu);null!==t&&t.classList.remove("active"),this._itemIndex+=e,this._itemIndex<0?this._itemIndex=this._dropdownMenu.childElementCount-1:this._itemIndex>=this._dropdownMenu.childElementCount&&(this._itemIndex=0),this._dropdownMenu.children[this._itemIndex].classList.add("active")},_hideDropdown:function(){null!==this._dropdownMenu&&this._dropdownMenu.classList.remove("dropdownOpen"),this._dropdownActive=!1,this._itemIndex=0},_ajaxSetup:function(){return{data:{actionName:"getSearchResultList",className:"wcf\\data\\user\\UserAction",interfaceName:"wcf\\data\\ISearchAction",parameters:{data:{includeUserGroups:!1}}},silent:!0}},_ajaxSuccess:function(e){if(!Array.isArray(e.returnValues)||!e.returnValues.length)return void this._hideDropdown();null===this._dropdownMenu&&(this._dropdownMenu=elCreate("ol"),this._dropdownMenu.className="dropdownMenu",null===o&&(o=elCreate("div"),o.className="dropdownMenuContainer",document.body.appendChild(o)),o.appendChild(this._dropdownMenu)),this._dropdownMenu.innerHTML="";for(var t,n,a,r=this._setUsername.bind(this),s=0,l=e.returnValues.length;s<l;s++)a=e.returnValues[s],n=elCreate("li"),t=elCreate("a"),t.addEventListener("mousedown",r),t.className="box16",t.innerHTML="<span>"+a.icon+"</span> <span>"+i.escapeHTML(a.label)+"</span>",elData(t,"user-id",a.objectID),elData(t,"username",a.label),n.appendChild(t),this._dropdownMenu.appendChild(n);this._dropdownMenu.classList.add("dropdownOpen"),this._dropdownActive=!0,this._updateDropdownPosition()}},a}),define("WoltLabSuite/Core/Ui/Redactor/Page",["WoltLabSuite/Core/Ui/Page/Search"],function(e){"use strict";function t(e,t){this.init(e,t)}return t.prototype={init:function(e,t){this._editor=e,t.addEventListener(WCF_CLICK_EVENT,this._click.bind(this))},_click:function(t){t.preventDefault(),e.open(this._insert.bind(this))},_insert:function(e){this._editor.buffer.set(),this._editor.insert.text("[wsp='"+e+"'][/wsp]")}},t}),define("WoltLabSuite/Core/Ui/Redactor/Quote",["Core","EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./Metacode","./PseudoHeader"],function(e,t,i,n,a,o,r,s,l){"use strict";function c(e,t){this.init(e,t)}var d=0;return c.prototype={init:function(e,i){this._quote=null,this._quotes=elByTag("woltlab-quote",e.$editor[0]),this._editor=e,this._elementId=this._editor.$element[0].id,t.add("com.woltlab.wcf.redactor2","observe_load_"+this._elementId,this._observeLoad.bind(this)),this._editor.button.addCallback(i,this._click.bind(this)),this._callbackEdit=this._edit.bind(this),this._observeLoad(),t.add("com.woltlab.wcf.redactor2","insertQuote_"+this._elementId,this._insertQuote.bind(this))},_insertQuote:function(e){if(!this._editor.WoltLabSource.isActive()){t.fire("com.woltlab.wcf.redactor2","showEditor");var i=this._editor.core.editor()[0];this._editor.selection.restore(),this._editor.buffer.set();var n=this._editor.selection.block();for(!1===n&&(this._editor.focus.end(),n=this._editor.selection.block());n&&n.parentNode!==i;)n=n.parentNode;var o=elCreate("woltlab-quote");elData(o,"author",e.author),elData(o,"link",e.link);var r=e.content;e.isText?(r=a.escapeHTML(r),r="<p>"+r+"</p>",r=r.replace(/\n\n/g,"</p><p>"),r=r.replace(/\n/g,"<br>")):r=s.convertFromHtml(this._editor.$element[0].id,r),o.innerHTML=r,n.parentNode.insertBefore(o,n.nextSibling),"P"!==n.nodeName||"<br>"!==n.innerHTML&&""!==n.innerHTML.replace(/\u200B/g,"")||n.parentNode.removeChild(n);var l=o.previousElementSibling;l&&"P"!==l.nodeName&&(l=elCreate("p"),l.textContent="",o.parentNode.insertBefore(l,o)),this._editor.WoltLabCaret.paragraphAfterBlock(o),this._editor.buffer.set()}},_click:function(){this._editor.button.toggle({},"woltlab-quote","func","block.format");var e=this._editor.selection.block();e&&"WOLTLAB-QUOTE"===e.nodeName&&(this._setTitle(e),e.addEventListener(WCF_CLICK_EVENT,this._callbackEdit),this._editor.caret.end(e))},_observeLoad:function(){for(var e,t=0,i=this._quotes.length;t<i;t++)e=this._quotes[t],e.addEventListener("mousedown",this._callbackEdit),this._setTitle(e)},_edit:function(e){var t=e.currentTarget;0===d&&(d=l.getHeight(t));var i=o.offset(t);e.pageY>i.top&&e.pageY<i.top+d&&(e.preventDefault(),this._editor.selection.save(),this._quote=t,r.open(this))},_dialogSubmit:function(){var e="redactor-quote-"+this._elementId,t=elById(e+"-url"),i=t.value.replace(/\u200B/g,"").trim();if(i.length&&!/^https?:\/\/[^\/]+/.test(i))return void elInnerError(t,n.get("wcf.editor.quote.url.error.invalid"));elInnerError(t,!1),elData(this._quote,"author",elById(e+"-author").value),elData(this._quote,"link",i),this._setTitle(this._quote),this._editor.caret.after(this._quote),r.close(this)},_setTitle:function(e){var t=n.get("wcf.editor.quote.title",{author:elData(e,"author"),url:elData(e,"url")});elData(e,"title")!==t&&elData(e,"title",t)},_delete:function(e){e.preventDefault();var t=this._quote.nextElementSibling||this._quote.previousElementSibling;null===t&&this._quote.parentNode!==this._editor.core.editor()[0]&&(t=this._quote.parentNode),null===t?(this._editor.code.set(""),this._editor.focus.end()):(elRemove(this._quote),this._editor.caret.end(t)),r.close(this)},_dialogSetup:function(){var e="redactor-quote-"+this._elementId,t=e+"-author",i=e+"-button-delete",a=e+"-button-save",o=e+"-url";return{id:e,options:{onClose:function(){this._editor.selection.restore(),r.destroy(this)}.bind(this),onSetup:function(){elById(i).addEventListener(WCF_CLICK_EVENT,this._delete.bind(this))}.bind(this),onShow:function(){elById(t).value=elData(this._quote,"author"),elById(o).value=elData(this._quote,"link")}.bind(this),title:n.get("wcf.editor.quote.edit")},source:'<div class="section"><dl><dt><label for="'+t+'">'+n.get("wcf.editor.quote.author")+'</label></dt><dd><input type="text" id="'+t+'" class="long" data-dialog-submit-on-enter="true"></dd></dl><dl><dt><label for="'+o+'">'+n.get("wcf.editor.quote.url")+'</label></dt><dd><input type="text" id="'+o+'" class="long" data-dialog-submit-on-enter="true"><small>'+n.get("wcf.editor.quote.url.description")+'</small></dd></dl></div><div class="formSubmit"><button id="'+a+'" class="buttonPrimary" data-type="submit">'+n.get("wcf.global.button.save")+'</button><button id="'+i+'">'+n.get("wcf.global.button.delete")+"</button></div>"}}},c}),define("WoltLabSuite/Core/Ui/Redactor/Spoiler",["EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./PseudoHeader"],function(e,t,i,n,a,o,r){"use strict";function s(e){this.init(e)}var l=0;return s.prototype={init:function(t){this._editor=t,this._elementId=this._editor.$element[0].id,this._spoiler=null,e.add("com.woltlab.wcf.redactor2","bbcode_spoiler_"+this._elementId,this._bbcodeSpoiler.bind(this)),e.add("com.woltlab.wcf.redactor2","observe_load_"+this._elementId,this._observeLoad.bind(this)),this._callbackEdit=this._edit.bind(this),this._observeLoad()},_bbcodeSpoiler:function(e){e.cancel=!0,this._editor.button.toggle({},"woltlab-spoiler","func","block.format");var t=this._editor.selection.block();t&&"WOLTLAB-SPOILER"===t.nodeName&&(this._setTitle(t),t.addEventListener(WCF_CLICK_EVENT,this._callbackEdit),this._editor.caret.end(t))},_observeLoad:function(){elBySelAll("woltlab-spoiler",this._editor.$editor[0],function(e){e.addEventListener("mousedown",this._callbackEdit),this._setTitle(e)}.bind(this))},_edit:function(e){var t=e.currentTarget;0===l&&(l=r.getHeight(t));var i=a.offset(t);e.pageY>i.top&&e.pageY<i.top+l&&(e.preventDefault(),this._editor.selection.save(),this._spoiler=t,o.open(this))},_dialogSubmit:function(){elData(this._spoiler,"label",elById("redactor-spoiler-"+this._elementId+"-label").value),this._setTitle(this._spoiler),this._editor.caret.after(this._spoiler),o.close(this)},_setTitle:function(e){var t=i.get("wcf.editor.spoiler.title",{label:elData(e,"label")});elData(e,"title")!==t&&elData(e,"title",t)},_delete:function(e){e.preventDefault();var t=this._spoiler.nextElementSibling||this._spoiler.previousElementSibling;null===t&&this._spoiler.parentNode!==this._editor.core.editor()[0]&&(t=this._spoiler.parentNode),null===t?(this._editor.code.set(""),this._editor.focus.end()):(elRemove(this._spoiler),this._editor.caret.end(t)),o.close(this)},_dialogSetup:function(){var e="redactor-spoiler-"+this._elementId,t=e+"-button-delete",n=e+"-button-save",a=e+"-label";return{id:e,options:{onClose:function(){this._editor.selection.restore(),o.destroy(this)}.bind(this),onSetup:function(){elById(t).addEventListener(WCF_CLICK_EVENT,this._delete.bind(this))}.bind(this),onShow:function(){elById(a).value=elData(this._spoiler,"label")}.bind(this),title:i.get("wcf.editor.spoiler.edit")},source:'<div class="section"><dl><dt><label for="'+a+'">'+i.get("wcf.editor.spoiler.label")+'</label></dt><dd><input type="text" id="'+a+'" class="long" data-dialog-submit-on-enter="true"><small>'+i.get("wcf.editor.spoiler.label.description")+'</small></dd></dl></div><div class="formSubmit"><button id="'+n+'" class="buttonPrimary" data-type="submit">'+i.get("wcf.global.button.save")+'</button><button id="'+t+'">'+i.get("wcf.global.button.delete")+"</button></div>"}}},s}),define("WoltLabSuite/Core/Ui/Redactor/Table",["Language","Ui/Dialog"],function(e,t){"use strict";var i=null;return{showDialog:function(e){t.open(this),i=e.submitCallback},_dialogSubmit:function(){var e=!0;["rows","cols"].forEach(function(t){var i=elById("redactor-table-"+t);(i.value<1||i.value>100)&&(e=!1)}),e&&(i(),t.close(this))},_dialogSetup:function(){return{id:"redactorDialogTable",options:{onShow:function(){elById("redactor-table-rows").value=2,elById("redactor-table-cols").value=3},title:e.get("wcf.editor.table.insertTable")},source:'<dl><dt><label for="redactor-table-rows">'+e.get("wcf.editor.table.rows")+'</label></dt><dd><input type="number" id="redactor-table-rows" class="small" min="1" max="100" value="2" data-dialog-submit-on-enter="true"></dd></dl><dl><dt><label for="redactor-table-cols">'+e.get("wcf.editor.table.cols")+'</label></dt><dd><input type="number" id="redactor-table-cols" class="small" min="1" max="100" value="3" data-dialog-submit-on-enter="true"></dd></dl><div class="formSubmit"><button id="redactor-modal-button-action" class="buttonPrimary" data-type="submit">'+e.get("wcf.global.button.insert")+"</button></div>"}}}}),define("WoltLabSuite/Core/Ui/Search/Page",["Core","Dom/Traverse","Dom/Util","Ui/Screen","Ui/SimpleDropdown","./Input"],function(e,t,i,n,a,o){"use strict";return{init:function(r){var s=elById("pageHeaderSearchInput");new o(s,{ajax:{className:"wcf\\data\\search\\keyword\\SearchKeywordAction"},callbackDropdownInit:function(e){if(e.classList.add("dropdownMenuPageSearch"),n.is("screen-lg")){elData(e,"dropdown-alignment-horizontal","right");var t=s.clientWidth;e.style.setProperty("min-width",t+"px","");var a=s.parentNode,o=i.offset(a).left+a.clientWidth-(i.offset(s).left+t),r=i.styleAsInt(window.getComputedStyle(a),"padding-bottom");e.style.setProperty("transform","translateX(-"+Math.ceil(o)+"px) translateY(-"+r+"px)","")}},callbackSelect:function(){return setTimeout(function(){t.parentByTag(s,"FORM").submit()},1),!0}});var l=a.getDropdownMenu(i.identify(elBySel(".pageHeaderSearchType"))),c=this._click.bind(this);elBySelAll("a[data-object-type]",l,function(e){e.addEventListener(WCF_CLICK_EVENT,c)});var d=elBySel('a[data-object-type="'+r+'"]',l);e.triggerEvent(d,WCF_CLICK_EVENT)},_click:function(e){e.preventDefault();var t=elById("pageHeader");t.classList.add("searchBarForceOpen"),window.setTimeout(function(){t.classList.remove("searchBarForceOpen")},10);var i=elData(e.currentTarget,"object-type"),n=elById("pageHeaderSearchParameters");n.innerHTML="";var a=elData(e.currentTarget,"extended-link");a&&(elBySel(".pageHeaderSearchExtendedLink").href=a);var o=elData(e.currentTarget,"parameters");o=o?JSON.parse(o):{},i&&(o["types[]"]=i);for(var r in o)if(o.hasOwnProperty(r)){var s=elCreate("input");s.type="hidden",s.name=r,s.value=o[r],n.appendChild(s)}elBySel(".pageHeaderSearchType > .button",elById("pageHeaderSearchInputContainer")).textContent=e.currentTarget.textContent}}}),define("WoltLabSuite/Core/Ui/Sortable/List",["Core","Ui/Screen"],function(e,t){"use strict";function i(e){this.init(e)}return i.prototype={init:function(i){this._options=e.extend({containerId:"",className:"",offset:0,options:{},isSimpleSorting:!1,additionalParameters:{}},i),t.on("screen-sm-md",{match:this._enable.bind(this,!0),unmatch:this._disable.bind(this),setup:this._enable.bind(this,!0)}),t.on("screen-lg",{match:this._enable.bind(this,!1),unmatch:this._disable.bind(this),setup:this._enable.bind(this,!1)})},_enable:function(e){var t=this._options.options;e&&(t.handle=".sortableNodeHandle"),new window.WCF.Sortable.List(this._options.containerId,this._options.className,this._options.offset,t,this._options.isSimpleSorting,this._options.additionalParameters)},_disable:function(){window.jQuery("#"+this._options.containerId+" .sortableList")[this._options.isSimpleSorting?"sortable":"nestedSortable"]("destroy")}},i}),define("WoltLabSuite/Core/Ui/Style/FontAwesome",["Language","Ui/Dialog","WoltLabSuite/Core/Ui/ItemList/Filter"],function(e,t,i){"use strict";var n,a,o,r=[];return{setup:function(e){r=e},open:function(e){if(0===r.length)throw new Error("Missing icon data, please include the template before calling this method using `{include file='fontAwesomeJavaScript'}`.");n=e,t.open(this)},_click:function(e){e.preventDefault();var i=e.target.closest("li"),a=elBySel("small",i).textContent.trim();t.close(this),n(a)},_dialogSetup:function(){return{id:"fontAwesomeSelection",options:{onSetup:function(){a=elById("fontAwesomeIcons");for(var e,t="",n=0,s=r.length;n<s;n++)e=r[n],t+='<li><span class="icon icon48 fa-'+e+'"></span><small>'+e+"</small></li>";a.innerHTML=t,a.addEventListener(WCF_CLICK_EVENT,this._click.bind(this)),o=new i("fontAwesomeIcons",{callbackPrepareItem:function(e){var t=elBySel("small",e);return{item:e,span:t,text:t.textContent.trim()}},enableVisibilityFilter:!1})}.bind(this),onShow:function(){o.reset()},title:e.get("wcf.global.fontAwesome.selectIcon")},source:'<ul class="fontAwesomeIcons" id="fontAwesomeIcons"></ul>'}}}}),define("WoltLabSuite/Core/Ui/Toggle/Input",["Core"],function(e){"use strict";function t(e,t){this.init(e,t)}return t.prototype={init:function(t,i){if(this._element=elBySel(t),null===this._element)throw new Error("Unable to find element by selector '"+t+"'.");var n="INPUT"===this._element.nodeName?elAttr(this._element,"type"):"";if("checkbox"!==n&&"radio"!==n)throw new Error("Illegal element, expected input[type='checkbox'] or input[type='radio'].");this._options=e.extend({hide:[],show:[]},i),["hide","show"].forEach(function(e){var t,i,n;for(i=0,n=this._options[e].length;i<n;i++)if("string"!=typeof(t=this._options[e][i])&&!(t instanceof Element))throw new TypeError("The array '"+e+"' may only contain string selectors or DOM elements.")}.bind(this)),this._element.addEventListener("change",this._change.bind(this)),this._handleElements(this._options.show,this._element.checked),this._handleElements(this._options.hide,!this._element.checked)},_change:function(e){var t=e.currentTarget.checked;this._handleElements(this._options.show,t),this._handleElements(this._options.hide,!t)},_handleElements:function(e,t){for(var i,n,a=0,o=e.length;a<o;a++){if("string"==typeof(i=e[a])){if(null===(n=elBySel(i)))throw new Error("Unable to find element by selector '"+i+"'.");e[a]=i=n}window[t?"elShow":"elHide"](i)}}},t}),define("WoltLabSuite/Core/Ui/User/Editor",["Ajax","Language","StringUtil","Dom/Util","Ui/Dialog","Ui/Notification"],function(e,t,i,n,a,o){"use strict";var r="",s=null;return{init:function(){s=elBySel(".userProfileUser"),["ban","disableAvatar","disableSignature","enable"].forEach(function(e){var t=elBySel(".userProfileButtonMenu .jsButtonUser"+i.ucfirst(e));t&&(elData(t,"action",e),t.addEventListener(WCF_CLICK_EVENT,this._click.bind(this)))}.bind(this))},_click:function(t){t.preventDefault();var i=elData(t.currentTarget,"action"),n="";switch(i){case"ban":elDataBool(s,"banned")&&(n="unban");break;case"disableAvatar":elDataBool(s,"disable-avatar")&&(n="enableAvatar");break;case"disableSignature":elDataBool(s,"disable-signature")&&(n="enableSignature");break;case"enable":n=elDataBool(s,"is-disabled")?"enable":"disable"}""===n?(r=i,a.open(this)):e.api(this,{actionName:n})},_submit:function(i){i.preventDefault();var n=elById("wcfUiUserEditorExpiresLabel"),a="",o="";elById("wcfUiUserEditorNeverExpires").checked||""===(a=elById("wcfUiUserEditorExpiresDatePicker").value)&&(o=t.get("wcf.global.form.error.empty")),elInnerError(n,o);var s={};s[r+"Expires"]=a,s[r+"Reason"]=elById("wcfUiUserEditorReason").value.trim(),e.api(this,{actionName:r,parameters:s})},_ajaxSuccess:function(e){switch(e.actionName){case"ban":case"unban":elData(s,"banned","ban"===e.actionName),elBySel(".userProfileButtonMenu .jsButtonUserBan").textContent=t.get("wcf.user."+("ban"===e.actionName?"unban":"ban"));var i=elBySel(".contentTitle",s),n=elBySel(".jsUserBanned",i);"ban"===e.actionName?(n=elCreate("span"),n.className="icon icon24 fa-lock jsUserBanned jsTooltip",n.title=e.returnValues,i.appendChild(n)):n&&elRemove(n);break;case"disableAvatar":case"enableAvatar":elData(s,"disable-avatar","disableAvatar"===e.actionName),elBySel(".userProfileButtonMenu .jsButtonUserDisableAvatar").textContent=t.get("wcf.user."+("disableAvatar"===e.actionName?"enable":"disable")+"Avatar");break;case"disableSignature":case"enableSignature":elData(s,"disable-signature","disableSignature"===e.actionName),elBySel(".userProfileButtonMenu .jsButtonUserDisableSignature").textContent=t.get("wcf.user."+("disableSignature"===e.actionName?"enable":"disable")+"Signature");break;case"enable":case"disable":elData(s,"is-disabled","disable"===e.actionName),elBySel(".userProfileButtonMenu .jsButtonUserEnable").textContent=t.get("wcf.acp.user."+("enable"===e.actionName?"disable":"enable"))}"ban"!==e.actionName&&"disableAvatar"!==e.actionName&&"disableSignature"!==e.actionName||a.close(this),o.show()},_ajaxSetup:function(){return{data:{className:"wcf\\data\\user\\UserAction",objectIDs:[elData(s,"object-id")]}}},_dialogSetup:function(){return{id:"wcfUiUserEditor",options:{onSetup:function(e){elById("wcfUiUserEditorNeverExpires").addEventListener("change",function(){window[this.checked?"elHide":"elShow"](elById("wcfUiUserEditorExpiresSettings"))}),elBySel("button.buttonPrimary",e).addEventListener(WCF_CLICK_EVENT,this._submit.bind(this))}.bind(this),onShow:function(e){a.setTitle("wcfUiUserEditor",t.get("wcf.user."+r+".confirmMessage"));var i=elById("wcfUiUserEditorReason").nextElementSibling,n="wcf.user."+r+".reason.description";i.textContent=t.get(n),window[i.textContent===n?"elHide":"elShow"](i),i=elById("wcfUiUserEditorNeverExpires").nextElementSibling,i.textContent=t.get("wcf.user."+r+".neverExpires"),i=elBySel('label[for="wcfUiUserEditorExpires"]',e),i.textContent=t.get("wcf.user."+r+".expires"),i=elById("wcfUiUserEditorExpiresLabel"),i.textContent=t.get("wcf.user."+r+".expires.description")}},source:'<div class="section"><dl><dt><label for="wcfUiUserEditorReason">'+t.get("wcf.global.reason")+'</label></dt><dd><textarea id="wcfUiUserEditorReason" cols="40" rows="3"></textarea><small></small></dd></dl><dl><dt></dt><dd><label><input type="checkbox" id="wcfUiUserEditorNeverExpires" checked> <span></span></label></dd></dl><dl id="wcfUiUserEditorExpiresSettings" style="display: none"><dt><label for="wcfUiUserEditorExpires"></label></dt><dd><input type="date" name="wcfUiUserEditorExpires" id="wcfUiUserEditorExpires" class="medium" min="'+new Date(1e3*TIME_NOW).toISOString()+'" data-ignore-timezone="true"><small id="wcfUiUserEditorExpiresLabel"></small></dd></dl></div><div class="formSubmit"><button class="buttonPrimary">'+t.get("wcf.global.button.submit")+"</button></div>"}}}}),define("WoltLabSuite/Core/Controller/Condition/Page/Dependence",["Dom/ChangeListener","Dom/Traverse","EventHandler","ObjectMap"],function(e,t,i,n){"use strict";var a=elBySelAll('input[name="pageIDs[]"]'),o=[],r=new n,s=new n,l=!1;return{register:function(e,i){if(o.push(e),r.set(e,i),s.set(e,[]),!l){for(var n=0,c=a.length;n<c;n++)a[n].addEventListener("change",this._checkVisibility.bind(this));l=!0}t.parentByTag(e,"FORM").addEventListener("submit",function(){"none"===e.style.getPropertyValue("display")&&e.remove()}),this._checkVisibility()},_checkVisibility:function(){for(var e,t,n,s,l,c=0,d=o.length;c<d;c++){e=o[c],n=r.get(e),s=[];for(var u=0,h=a.length;u<h;u++)t=a[u],t.checked&&s.push(~~t.value);l=s.filter(function(e){return-1===n.indexOf(e)}),!s.length||l.length?this._hideDependentElement(e):this._showDependentElement(e)}i.fire("com.woltlab.wcf.pageConditionDependence","checkVisivility")},_hideDependentElement:function(e){elHide(e);for(var t=s.get(e),i=0,n=t.length;i<n;i++)elHide(t[i]);s.set(e,[])},_showDependentElement:function(e){elShow(e);for(var t=e;(t=t.parentNode)&&t instanceof Element;)"none"===t.style.getPropertyValue("display")&&s.get(e).push(t),elShow(t)}}}),define("WoltLabSuite/Core/Controller/Map/Route/Planner",["Dom/Traverse","Dom/Util","Language","Ui/Dialog","WoltLabSuite/Core/Ajax/Status"],function(e,t,i,n,a){function o(e,t){if(this._button=elById(e),null===this._button)throw new Error("Unknown button with id '"+e+"'");this._button.addEventListener("click",this._openDialog.bind(this)),this._destination=t}return o.prototype={_dialogSetup:function(){return{id:this._button.id+"Dialog",options:{onShow:this._initDialog.bind(this),title:i.get("wcf.map.route.planner")},source:'<div class="googleMapsDirectionsContainer" style="display: none;"><div class="googleMap"></div><div class="googleMapsDirections"></div></div><small class="googleMapsDirectionsGoogleLinkContainer"><a href="'+this._getGoogleMapsLink()+'" class="googleMapsDirectionsGoogleLink" target="_blank" style="display: none;">'+i.get("wcf.map.route.viewOnGoogleMaps")+"</a></small><dl><dt>"+i.get("wcf.map.route.origin")+'</dt><dd><input type="text" name="origin" class="long" autofocus /></dd></dl><dl style="display: none;"><dt>'+i.get("wcf.map.route.travelMode")+'</dt><dd><select name="travelMode"><option value="driving">'+i.get("wcf.map.route.travelMode.driving")+'</option><option value="walking">'+i.get("wcf.map.route.travelMode.walking")+'</option><option value="bicycling">'+i.get("wcf.map.route.travelMode.bicycling")+'</option><option value="transit">'+i.get("wcf.map.route.travelMode.transit")+"</option></select></dd></dl>"}},_calculateRoute:function(e){var t=n.getDialog(this).dialog;e.label&&(this._originInput.value=e.label),void 0===this._map&&(this._map=new google.maps.Map(elByClass("googleMap",t)[0],{disableDoubleClickZoom:WCF.Location.GoogleMaps.Settings.get("disableDoubleClickZoom"),draggable:WCF.Location.GoogleMaps.Settings.get("draggable"),mapTypeId:google.maps.MapTypeId.ROADMAP,scaleControl:WCF.Location.GoogleMaps.Settings.get("scaleControl"),scrollwheel:WCF.Location.GoogleMaps.Settings.get("scrollwheel")}),this._directionsService=new google.maps.DirectionsService,this._directionsRenderer=new google.maps.DirectionsRenderer,this._directionsRenderer.setMap(this._map),this._directionsRenderer.setPanel(elByClass("googleMapsDirections",t)[0]),this._googleLink=elByClass("googleMapsDirectionsGoogleLink",t)[0]);var i={destination:this._destination,origin:e.location,provideRouteAlternatives:!0,travelMode:google.maps.TravelMode[this._travelMode.value.toUpperCase()]};a.show(),this._directionsService.route(i,this._setRoute.bind(this)),elAttr(this._googleLink,"href",this._getGoogleMapsLink(e.location,this._travelMode.value)),
-this._lastOrigin=e.location},_getGoogleMapsLink:function(e,t){if(e){var i="https://www.google.com/maps/dir/?api=1&origin="+e.lat()+","+e.lng()+"&destination="+this._destination.lat()+","+this._destination.lng();return t&&(i+="&travelmode="+t),i}return"https://www.google.com/maps/search/?api=1&query="+this._destination.lat()+","+this._destination.lng()},_initDialog:function(){if(!this._didInitDialog){var e=n.getDialog(this).dialog;this._originInput=elBySel('input[name="origin"]',e),new WCF.Location.GoogleMaps.LocationSearch(this._originInput,this._calculateRoute.bind(this)),this._travelMode=elBySel('select[name="travelMode"]',e),this._travelMode.addEventListener("change",this._updateRoute.bind(this)),this._didInitDialog=!0}},_openDialog:function(){n.open(this)},_setRoute:function(t,n){a.hide(),"OK"===n?(elShow(this._map.getDiv().parentNode),google.maps.event.trigger(this._map,"resize"),this._directionsRenderer.setDirections(t),elShow(e.parentByTag(this._travelMode,"DL")),elShow(this._googleLink),elInnerError(this._originInput,!1)):("OVER_QUERY_LIMIT"!==n&&"REQUEST_DENIED"!==n&&(n="NOT_FOUND"),elInnerError(this._originInput,i.get("wcf.map.route.error."+n.toLowerCase())))},_updateRoute:function(){this._calculateRoute({location:this._lastOrigin})}},o}),define("WoltLabSuite/Core/Controller/User/Notification/Settings",["Dictionary","Language","Dom/Traverse","Ui/SimpleDropdown"],function(e,t,i,n){"use strict";var a=new e,o=null,r=null;return{setup:function(){o=this._click.bind(this),r=this._selectType.bind(this);for(var e,t,i=elBySelAll("#notificationSettings .flexibleButtonGroup"),n=0,a=i.length;n<a;n++)e=i[n],null!==(t=elBySel(".notificationSettingsEmail",e))&&this._initGroup(e,t)},_initGroup:function(e,t){var n=~~elData(e,"object-id");elById("settings_"+n+"_disabled").addEventListener(WCF_CLICK_EVENT,function(){t.classList.remove("active")}),elById("settings_"+n+"_enabled").addEventListener(WCF_CLICK_EVENT,function(){t.classList.add("active")});var r=i.childByTag(t,"INPUT"),s=i.childByTag(t,"A");elData(s,"object-id",n),s.addEventListener(WCF_CLICK_EVENT,o),a.set(n,{button:s,dropdownMenu:null,mailSetting:t,mailValue:r})},_click:function(e){e.preventDefault();var t=e.currentTarget,o=~~elData(t,"object-id"),r=a.get(o);if(null===r.dropdownMenu)r.dropdownMenu=this._createDropdown(o,r.mailValue.value),t.parentNode.classList.add("dropdown"),t.parentNode.appendChild(r.dropdownMenu),n.init(t,!0);else for(var s=i.childrenByTag(r.dropdownMenu,"LI"),l=r.mailValue.value,c=0;c<4;c++)s[c].classList[elData(s[c],"value")===l?"add":"remove"]("active")},_createDropdown:function(e,i){var n=elCreate("ul");n.className="dropdownMenu",elData(n,"object-id",e);for(var a,o,s,l=["instant","daily","divider","none"],c=0;c<4;c++)s=l[c],o=elCreate("li"),"divider"===s?o.className="dropdownDivider":(a=elCreate("a"),a.textContent=t.get("wcf.user.notification.mailNotificationType."+s),o.appendChild(a),elData(o,"value",s),o.addEventListener(WCF_CLICK_EVENT,r),i===s&&(o.className="active")),n.appendChild(o);return n},_selectType:function(e){var i=elData(e.currentTarget,"value"),n=~~elData(e.currentTarget.parentNode,"object-id"),o=a.get(n);o.mailValue.value=i,elBySel("span.title",o.mailSetting).textContent=t.get("wcf.user.notification.mailNotificationType."+i),o.button.classList["none"===i?"remove":"add"]("yellow"),o.button.classList["none"===i?"remove":"add"]("active")}}}),define("WoltLabSuite/Core/Ui/Comment/Response/Add",["Core","Language","Dom/ChangeListener","Dom/Util","Dom/Traverse","Ui/Notification","WoltLabSuite/Core/Ui/Comment/Add"],function(e,t,i,n,a,o,r){"use strict";function s(e,t){this.init(e,t)}return e.inherit(s,r,{init:function(t,i){s._super.prototype.init.call(this,t),this._options=e.extend({callbackInsert:null},i)},getContainer:function(){return this._isBusy?null:this._container},getContent:function(){return window.jQuery(this._textarea).redactor("code.get")},setContent:function(e){window.jQuery(this._textarea).redactor("code.set",e),window.jQuery(this._textarea).redactor("WoltLabCaret.endOfEditor");var t=elBySel(".innerError",this._textarea.parentNode);null!==t&&elRemove(t),this._content.classList.remove("collapsed"),this._focusEditor()},_getParameters:function(){var e=s._super.prototype._getParameters.call(this);return e.data.commentID=~~elData(this._container.closest(".comment"),"object-id"),e},_insertMessage:function(e){var r=a.childByClass(this._container.parentNode,"commentContent"),s=r.nextElementSibling;return null!==s&&s.classList.contains("commentResponseList")||(s=elCreate("ul"),s.className="containerList commentResponseList",elData(s,"responses",0),r.parentNode.insertBefore(s,r.nextSibling)),n.insertHtml(e.returnValues.template,s,"append"),o.show(t.get("wcf.global.success.add")),i.trigger(),window.jQuery(this._textarea).redactor("code.set",""),null!==this._options.callbackInsert&&this._options.callbackInsert(),elData(s,"responses",s.children.length),s.lastElementChild},_ajaxSetup:function(){var e=s._super.prototype._ajaxSetup.call(this);return e.data.actionName="addResponse",e}}),s}),define("WoltLabSuite/Core/Ui/Comment/Response/Edit",["Ajax","Core","Dictionary","Environment","EventHandler","Language","List","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Notification","Ui/ReusableDropdown","WoltLabSuite/Core/Ui/Scroll","WoltLabSuite/Core/Ui/Comment/Edit"],function(e,t,i,n,a,o,r,s,l,c,d,u,h,p){"use strict";function f(e){this.init(e)}return t.inherit(f,p,{init:function(e){this._activeElement=null,this._callbackClick=null,this._container=e,this._editorContainer=null,this._responses=new r,this.rebuild(),s.add("Ui/Comment/Response/Edit_"+c.identify(this._container),this.rebuild.bind(this))},rebuild:function(){elBySelAll(".commentResponse",this._container,function(e){if(!this._responses.has(e)){if(elDataBool(e,"can-edit")){var t=elBySel(".jsCommentResponseEditButton",e);null!==t&&(null===this._callbackClick&&(this._callbackClick=this._click.bind(this)),t.addEventListener(WCF_CLICK_EVENT,this._callbackClick))}this._responses.add(e)}}.bind(this))},_click:function(t){t.preventDefault(),null===this._activeElement?(this._activeElement=t.currentTarget.closest(".commentResponse"),this._prepare(),e.api(this,{actionName:"beginEdit",objectIDs:[this._getObjectId(this._activeElement)]})):d.show("wcf.message.error.editorAlreadyInUse",null,"warning")},_prepare:function(){this._editorContainer=elCreate("div"),this._editorContainer.className="commentEditorContainer",this._editorContainer.innerHTML='<span class="icon icon48 fa-spinner"></span>';var e=elBySel(".commentResponseContent",this._activeElement);e.insertBefore(this._editorContainer,e.firstChild)},_showMessage:function(e){c.setInnerHtml(elBySel(".commentResponseContent .userMessage",this._editorContainer.parentNode),e.returnValues.message),this._restoreMessage(),d.show()},_getEditorId:function(){return"commentResponseEditor"+this._getObjectId(this._activeElement)},_ajaxSetup:function(){return{data:{className:"wcf\\data\\comment\\response\\CommentResponseAction",parameters:{data:{objectTypeID:~~elData(this._container,"object-type-id")}}},silent:!0}}}),f}),define("WoltLabSuite/Core/Ui/Page/Header/Fixed",["Core","EventHandler","Ui/Alignment","Ui/CloseOverlay","Ui/SimpleDropdown","Ui/Screen"],function(e,t,i,n,a,o){"use strict";var r,s,l,c,d,u,h,p=!1;return{init:function(){r=elById("pageHeader"),s=elById("pageHeaderContainer"),this._initSearchBar(),o.on("screen-md-down",{match:function(){p=!0},unmatch:function(){p=!1},setup:function(){p=!0}}),t.add("com.woltlab.wcf.Search","close",this._closeSearchBar.bind(this))},_initSearchBar:function(){c=elById("pageHeaderSearch"),c.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation()}),l=elById("pageHeaderPanel"),d=elById("pageHeaderSearchInput"),u=elById("topMenu"),h=elById("userPanelSearchButton"),h.addEventListener(WCF_CLICK_EVENT,function(e){e.preventDefault(),e.stopPropagation(),r.classList.contains("searchBarOpen")?this._closeSearchBar():this._openSearchBar()}.bind(this)),n.add("WoltLabSuite/Core/Ui/Page/Header/Fixed",function(){r.classList.contains("searchBarForceOpen")||this._closeSearchBar()}.bind(this)),t.add("com.woltlab.wcf.MainMenuMobile","more",function(t){"com.woltlab.wcf.search"===t.identifier&&(t.handler.close(!0),e.triggerEvent(h,WCF_CLICK_EVENT))}.bind(this))},_openSearchBar:function(){window.WCF.Dropdown.Interactive.Handler.closeAll(),r.classList.add("searchBarOpen"),h.parentNode.classList.add("open"),p||i.set(c,u,{horizontal:"right"}),c.style.setProperty("top",l.clientHeight+"px",""),d.focus()},_closeSearchBar:function(){r.classList.remove("searchBarOpen"),h.parentNode.classList.remove("open"),["bottom","left","right","top"].forEach(function(e){c.style.removeProperty(e)}),d.blur();var e=elBySel(".pageHeaderSearchType",c);a.close(e.id)}}}),define("WoltLabSuite/Core/Ui/Page/Search/Input",["Core","WoltLabSuite/Core/Ui/Search/Input"],function(e,t){"use strict";function i(e,t){this.init(e,t)}return e.inherit(i,t,{init:function(t,n){if(n=e.extend({ajax:{className:"wcf\\data\\page\\PageAction"},callbackSuccess:null},n),"function"!=typeof n.callbackSuccess)throw new Error("Expected a valid callback function for 'callbackSuccess'.");i._super.prototype.init.call(this,t,n),this._pageId=0},setPageId:function(e){this._pageId=e},_getParameters:function(e){var t=i._super.prototype._getParameters.call(this,e);return t.objectIDs=[this._pageId],t},_ajaxSuccess:function(e){this._options.callbackSuccess(e)}}),i}),define("WoltLabSuite/Core/Ui/Page/Search/Handler",["Language","StringUtil","Dom/Util","Ui/Dialog","./Input"],function(e,t,i,n,a){"use strict";var o=null,r=null,s=null,l=null,c=null,d=null;return{open:function(t,i,a,r){o=a,n.open(this),n.setTitle(this,i),s.textContent=r?e.get(r):e.get("wcf.page.pageObjectID.search.terms"),this._getSearchInputHandler().setPageId(t)},_buildList:function(i){if(this._resetList(),!Array.isArray(i.returnValues)||0===i.returnValues.length)return void elInnerError(r,e.get("wcf.page.pageObjectID.search.noResults"));for(var n,a,o,s=0,l=i.returnValues.length;s<l;s++)a=i.returnValues[s],n=a.image,/^fa-/.test(n)&&(n='<span class="icon icon48 '+n+' pointer jsTooltip" title="'+e.get("wcf.global.select")+'"></span>'),o=elCreate("li"),elData(o,"object-id",a.objectID),o.innerHTML='<div class="box48">'+n+'<div><div class="containerHeadline"><h3><a href="'+t.escapeHTML(a.link)+'">'+t.escapeHTML(a.title)+"</a></h3>"+(a.description?"<p>"+a.description+"</p>":"")+"</div></div></div>",o.addEventListener(WCF_CLICK_EVENT,this._click.bind(this)),c.appendChild(o);elShow(d)},_resetList:function(){elInnerError(r,!1),c.innerHTML="",elHide(d)},_getSearchInputHandler:function(){if(null===l){var e=this._buildList.bind(this);l=new a(elById("wcfUiPageSearchInput"),{callbackSuccess:e})}return l},_click:function(e){"A"!==e.target.nodeName&&(e.stopPropagation(),o(elData(e.currentTarget,"object-id")),n.close(this))},_dialogSetup:function(){return{id:"wcfUiPageSearchHandler",options:{onShow:function(){null===r&&(r=elById("wcfUiPageSearchInput"),s=r.parentNode.previousSibling.childNodes[0],c=elById("wcfUiPageSearchResultList"),d=elById("wcfUiPageSearchResultListContainer")),r.value="",elHide(d),c.innerHTML="",r.focus()},title:""},source:'<div class="section"><dl><dt><label for="wcfUiPageSearchInput">'+e.get("wcf.page.pageObjectID.search.terms")+'</label></dt><dd><input type="text" id="wcfUiPageSearchInput" class="long"></dd></dl></div><section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList"><header class="sectionHeader"><h2 class="sectionTitle">'+e.get("wcf.page.pageObjectID.search.results")+'</h2></header><ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul></section>'}}}}),define("WoltLabSuite/Core/Ui/User/Activity/Recent",["Ajax","Language","Dom/Util"],function(e,t,i){"use strict";function n(e){this.init(e)}return n.prototype={init:function(e){this._containerId=e;var i=elById(this._containerId);this._list=elBySel(".recentActivityList",i);var n=elCreate("li");n.className="showMore",this._list.childElementCount?(n.innerHTML='<button class="small">'+t.get("wcf.user.recentActivity.more")+"</button>",n.children[0].addEventListener(WCF_CLICK_EVENT,this._showMore.bind(this))):n.innerHTML="<small>"+t.get("wcf.user.recentActivity.noMoreEntries")+"</small>",this._list.appendChild(n),this._showMoreItem=n,elBySelAll(".jsRecentActivitySwitchContext .button",i,function(e){e.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),e.classList.contains("active")||this._switchContext()}.bind(this))}.bind(this))},_showMore:function(t){t.preventDefault(),this._showMoreItem.children[0].disabled=!0,e.api(this,{actionName:"load",parameters:{boxID:~~elData(this._list,"box-id"),filteredByFollowedUsers:elDataBool(this._list,"filtered-by-followed-users"),lastEventId:elData(this._list,"last-event-id"),lastEventTime:elData(this._list,"last-event-time"),userID:~~elData(this._list,"user-id")}})},_switchContext:function(){e.api(this,{actionName:"switchContext"},function(){window.location.hash="#"+this._containerId,window.location.reload()}.bind(this))},_ajaxSuccess:function(e){e.returnValues.template?(i.insertHtml(e.returnValues.template,this._showMoreItem,"before"),elData(this._list,"last-event-time",e.returnValues.lastEventTime),elData(this._list,"last-event-id",e.returnValues.lastEventID),this._showMoreItem.children[0].disabled=!1):this._showMoreItem.innerHTML="<small>"+t.get("wcf.user.recentActivity.noMoreEntries")+"</small>"},_ajaxSetup:function(){return{data:{className:"wcf\\data\\user\\activity\\event\\UserActivityEventAction"}}}},n}),define("WoltLabSuite/Core/Ui/User/CoverPhoto/Delete",["Ajax","EventHandler","Language","Ui/Confirmation","Ui/Notification"],function(e,t,i,n,a){"use strict";var o;return{init:function(){o=elBySel(".jsButtonDeleteCoverPhoto"),o.addEventListener(WCF_CLICK_EVENT,this._click.bind(this)),t.add("com.woltlab.wcf.user","coverPhoto",function(e){"string"==typeof e.url&&e.url.length>0&&elShow(o.parentNode)})},_click:function(){n.show({confirm:e.api.bind(e,this),message:i.get("wcf.user.coverPhoto.delete.confirmMessage")})},_ajaxSuccess:function(e){elBySel(".userProfileCoverPhoto").style.setProperty("background-image","url("+e.returnValues.url+")",""),elHide(o.parentNode),a.show()},_ajaxSetup:function(){return{data:{actionName:"deleteCoverPhoto",className:"wcf\\data\\user\\UserProfileAction"}}}}}),define("WoltLabSuite/Core/Ui/User/CoverPhoto/Upload",["Core","EventHandler","Upload","Ui/Notification","Ui/Dialog"],function(e,t,i,n,a){"use strict";function o(){i.call(this,"coverPhotoUploadButtonContainer","coverPhotoUploadPreview",{action:"uploadCoverPhoto",className:"wcf\\data\\user\\UserProfileAction"})}return e.inherit(o,i,{_success:function(e,i){elInnerError(this._button,i.returnValues.errorMessage),this._target.innerHTML="",i.returnValues.url&&(elBySel(".userProfileCoverPhoto").style.setProperty("background-image","url("+i.returnValues.url+")",""),a.close("userProfileCoverPhotoUpload"),n.show(),t.fire("com.woltlab.wcf.user","coverPhoto",{url:i.returnValues.url}))}}),o}),define("WoltLabSuite/Core/Ui/User/Trophy/List",["Ajax","Core","Dictionary","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ui/Pagination","Dom/ChangeListener","List"],function(e,t,i,n,a,o,r,s){"use strict";function l(){this.init()}return l.prototype={init:function(){this._cache=new i,this._knownElements=new s,this._options={className:"wcf\\data\\user\\trophy\\UserTrophyAction",parameters:{}},this._rebuild(),r.add("WoltLabSuite/Core/Ui/User/Trophy/List",this._rebuild.bind(this))},_rebuild:function(){elBySelAll(".userTrophyOverlayList",void 0,function(e){this._knownElements.has(e)||(e.addEventListener(WCF_CLICK_EVENT,this._open.bind(this,elData(e,"user-id"))),this._knownElements.add(e))}.bind(this))},_open:function(e,t){t.preventDefault(),this._currentPageNo=1,this._currentUser=e,this._showPage()},_showPage:function(t){if(void 0!==t&&(this._currentPageNo=t),this._cache.has(this._currentUser)){if(0!==this._cache.get(this._currentUser).get("pageCount")&&(this._currentPageNo<1||this._currentPageNo>this._cache.get(this._currentUser).get("pageCount")))throw new RangeError("pageNo must be between 1 and "+this._cache.get(this._currentUser).get("pageCount")+" ("+this._currentPageNo+" given).")}else this._cache.set(this._currentUser,new i);if(this._cache.get(this._currentUser).has(this._currentPageNo)){var n=a.open(this,this._cache.get(this._currentUser).get(this._currentPageNo));if(a.setTitle("userTrophyListOverlay",this._cache.get(this._currentUser).get("title")),this._cache.get(this._currentUser).get("pageCount")>1){var r=elBySel(".jsPagination",n.content);null!==r&&new o(r,{activePage:this._currentPageNo,maxPage:this._cache.get(this._currentUser).get("pageCount"),callbackSwitch:this._showPage.bind(this)})}}else this._options.parameters.pageNo=this._currentPageNo,this._options.parameters.userID=this._currentUser,e.api(this,{parameters:this._options.parameters})},_ajaxSuccess:function(e){void 0!==e.returnValues.pageCount&&this._cache.get(this._currentUser).set("pageCount",~~e.returnValues.pageCount),this._cache.get(this._currentUser).set(this._currentPageNo,e.returnValues.template),this._cache.get(this._currentUser).set("title",e.returnValues.title),this._showPage()},_ajaxSetup:function(){return{data:{actionName:"getGroupedUserTrophyList",className:this._options.className}}},_dialogSetup:function(){return{id:"userTrophyListOverlay",options:{title:""},source:null}}},l}),define("WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Abstract",["Ajax","Dom/Util"],function(e,t){"use strict";function i(e,t){}return i.prototype={init:function(e,t){this._userId=e,this._isActive=!1!==t,this._initButton(),this._updateButton()},_initButton:function(){var e=elCreate("a");e.href="#",e.addEventListener(WCF_CLICK_EVENT,this._toggle.bind(this));var i=elCreate("li");i.appendChild(e);var n=elBySel('.userProfileButtonMenu[data-menu="interaction"]');t.prepend(i,n),this._button=e,this._listItem=i},_toggle:function(t){t.preventDefault(),e.api(this,{actionName:this._getAjaxActionName(),parameters:{data:{userID:this._userId}}})},_updateButton:function(){this._button.textContent=this._getLabel(),this._listItem.classList[this._isActive?"add":"remove"]("active")},_getLabel:function(){throw new Error("Implement me!")},_getAjaxActionName:function(){throw new Error("Implement me!")},_ajaxSuccess:function(){throw new Error("Implement me!")},_ajaxSetup:function(){throw new Error("Implement me!")}},i}),define("WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Follow",["Core","Language","Ui/Notification","./Abstract"],function(e,t,i,n){"use strict";function a(e,t){this.init(e,t)}return e.inherit(a,n,{_getLabel:function(){return t.get("wcf.user.button."+(this._isActive?"un":"")+"follow")},_getAjaxActionName:function(){return this._isActive?"unfollow":"follow"},_ajaxSuccess:function(e){this._isActive=!!e.returnValues.following,this._updateButton(),i.show()},_ajaxSetup:function(){return{data:{className:"wcf\\data\\user\\follow\\UserFollowAction"}}}}),a}),define("WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Ignore",["Core","Language","Ui/Notification","./Abstract"],function(e,t,i,n){"use strict";function a(e,t){this.init(e,t)}return e.inherit(a,n,{_getLabel:function(){return t.get("wcf.user.button."+(this._isActive?"un":"")+"ignore")},_getAjaxActionName:function(){return this._isActive?"unignore":"ignore"},_ajaxSuccess:function(e){this._isActive=!!e.returnValues.isIgnoredUser,this._updateButton(),i.show()},_ajaxSetup:function(){return{data:{className:"wcf\\data\\user\\ignore\\UserIgnoreAction"}}}}),a}),function(e){e.matches=e.matches||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector,e.closest=e.closest||function(e){for(var t=this;t&&!t.matches(e);)t=t.parentElement;return t}}(Element.prototype),define("closest",function(){}),function(e){function t(){for(;n.length&&"function"==typeof n[0];)n.shift()()}var i=e.require,n=[],a=0;e.require=function(o,r,s){if(!Array.isArray(o))return i.apply(e,arguments);var l=new Promise(function(e,r){var s=a++;n.push(s),i(o,function(){var i=arguments;n[n.indexOf(s)]=function(){e(i)},t()},function(e){n[n.indexOf(s)]=function(){r(e)},t()})});return r&&l.then(function(t){r.apply(e,t)}),s&&l.catch(s),l},e.require.config=i.config}(window),define("require.linearExecution",function(){});
\ No newline at end of file
+/**
+ * @license alameda 1.2.0 Copyright jQuery Foundation and other contributors.
+ * Released under MIT license, https://github.com/requirejs/alameda/blob/master/LICENSE
+ */
+// Going sloppy because loader plugin execs may depend on non-strict execution.
+/*jslint sloppy: true, nomen: true, regexp: true */
+/*global document, navigator, importScripts, Promise, setTimeout */
+
+var requirejs, require, define;
+(function (global, Promise, undef) {
+ if (!Promise) {
+ throw new Error('No Promise implementation available');
+ }
+
+ var topReq, dataMain, src, subPath,
+ bootstrapConfig = requirejs || require,
+ hasOwn = Object.prototype.hasOwnProperty,
+ contexts = {},
+ queue = [],
+ currDirRegExp = /^\.\//,
+ urlRegExp = /^\/|\:|\?|\.js$/,
+ commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg,
+ cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
+ jsSuffixRegExp = /\.js$/,
+ slice = Array.prototype.slice;
+
+ if (typeof requirejs === 'function') {
+ return;
+ }
+
+ var asap = Promise.resolve(undefined);
+
+ // Could match something like ')//comment', do not lose the prefix to comment.
+ function commentReplace(match, singlePrefix) {
+ return singlePrefix || '';
+ }
+
+ function hasProp(obj, prop) {
+ return hasOwn.call(obj, prop);
+ }
+
+ function getOwn(obj, prop) {
+ return obj && hasProp(obj, prop) && obj[prop];
+ }
+
+ function obj() {
+ return Object.create(null);
+ }
+
+ /**
+ * Cycles over properties in an object and calls a function for each
+ * property value. If the function returns a truthy value, then the
+ * iteration is stopped.
+ */
+ function eachProp(obj, func) {
+ var prop;
+ for (prop in obj) {
+ if (hasProp(obj, prop)) {
+ if (func(obj[prop], prop)) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Simple function to mix in properties from source into target,
+ * but only if target does not already have a property of the same name.
+ */
+ function mixin(target, source, force, deepStringMixin) {
+ if (source) {
+ eachProp(source, function (value, prop) {
+ if (force || !hasProp(target, prop)) {
+ if (deepStringMixin && typeof value === 'object' && value &&
+ !Array.isArray(value) && typeof value !== 'function' &&
+ !(value instanceof RegExp)) {
+
+ if (!target[prop]) {
+ target[prop] = {};
+ }
+ mixin(target[prop], value, force, deepStringMixin);
+ } else {
+ target[prop] = value;
+ }
+ }
+ });
+ }
+ return target;
+ }
+
+ // Allow getting a global that expressed in
+ // dot notation, like 'a.b.c'.
+ function getGlobal(value) {
+ if (!value) {
+ return value;
+ }
+ var g = global;
+ value.split('.').forEach(function (part) {
+ g = g[part];
+ });
+ return g;
+ }
+
+ function newContext(contextName) {
+ var req, main, makeMap, callDep, handlers, checkingLater, load, context,
+ defined = obj(),
+ waiting = obj(),
+ config = {
+ // Defaults. Do not set a default for map
+ // config to speed up normalize(), which
+ // will run faster if there is no default.
+ waitSeconds: 7,
+ baseUrl: './',
+ paths: {},
+ bundles: {},
+ pkgs: {},
+ shim: {},
+ config: {}
+ },
+ mapCache = obj(),
+ requireDeferreds = [],
+ deferreds = obj(),
+ calledDefine = obj(),
+ calledPlugin = obj(),
+ loadCount = 0,
+ startTime = (new Date()).getTime(),
+ errCount = 0,
+ trackedErrors = obj(),
+ urlFetched = obj(),
+ bundlesMap = obj(),
+ asyncResolve = Promise.resolve();
+
+ /**
+ * Trims the . and .. from an array of path segments.
+ * It will keep a leading path segment if a .. will become
+ * the first path segment, to help with module name lookups,
+ * which act like paths, but can be remapped. But the end result,
+ * all paths that use this function should look normalized.
+ * NOTE: this method MODIFIES the input array.
+ * @param {Array} ary the array of path segments.
+ */
+ function trimDots(ary) {
+ var i, part, length = ary.length;
+ for (i = 0; i < length; i++) {
+ part = ary[i];
+ if (part === '.') {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === '..') {
+ // If at the start, or previous value is still ..,
+ // keep them so that when converted to a path it may
+ // still work when converted to a path, even though
+ // as an ID it is less than ideal. In larger point
+ // releases, may be better to just kick out an error.
+ if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') {
+ continue;
+ } else if (i > 0) {
+ ary.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Given a relative module name, like ./something, normalize it to
+ * a real name that can be mapped to a path.
+ * @param {String} name the relative name
+ * @param {String} baseName a real name that the name arg is relative
+ * to.
+ * @param {Boolean} applyMap apply the map config to the value. Should
+ * only be done if this normalization is for a dependency ID.
+ * @returns {String} normalized name
+ */
+ function normalize(name, baseName, applyMap) {
+ var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
+ foundMap, foundI, foundStarMap, starI,
+ baseParts = baseName && baseName.split('/'),
+ normalizedBaseParts = baseParts,
+ map = config.map,
+ starMap = map && map['*'];
+
+
+ //Adjust any relative paths.
+ if (name) {
+ name = name.split('/');
+ lastIndex = name.length - 1;
+
+ // If wanting node ID compatibility, strip .js from end
+ // of IDs. Have to do this here, and not in nameToUrl
+ // because node allows either .js or non .js to map
+ // to same file.
+ if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
+ name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
+ }
+
+ // Starts with a '.' so need the baseName
+ if (name[0].charAt(0) === '.' && baseParts) {
+ //Convert baseName to array, and lop off the last part,
+ //so that . matches that 'directory' and not name of the baseName's
+ //module. For instance, baseName of 'one/two/three', maps to
+ //'one/two/three.js', but we want the directory, 'one/two' for
+ //this normalization.
+ normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
+ name = normalizedBaseParts.concat(name);
+ }
+
+ trimDots(name);
+ name = name.join('/');
+ }
+
+ // Apply map config if available.
+ if (applyMap && map && (baseParts || starMap)) {
+ nameParts = name.split('/');
+
+ outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
+ nameSegment = nameParts.slice(0, i).join('/');
+
+ if (baseParts) {
+ // Find the longest baseName segment match in the config.
+ // So, do joins on the biggest to smallest lengths of baseParts.
+ for (j = baseParts.length; j > 0; j -= 1) {
+ mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
+
+ // baseName segment has config, find if it has one for
+ // this name.
+ if (mapValue) {
+ mapValue = getOwn(mapValue, nameSegment);
+ if (mapValue) {
+ // Match, update name to the new value.
+ foundMap = mapValue;
+ foundI = i;
+ break outerLoop;
+ }
+ }
+ }
+ }
+
+ // Check for a star map match, but just hold on to it,
+ // if there is a shorter segment match later in a matching
+ // config, then favor over this star map.
+ if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
+ foundStarMap = getOwn(starMap, nameSegment);
+ starI = i;
+ }
+ }
+
+ if (!foundMap && foundStarMap) {
+ foundMap = foundStarMap;
+ foundI = starI;
+ }
+
+ if (foundMap) {
+ nameParts.splice(0, foundI, foundMap);
+ name = nameParts.join('/');
+ }
+ }
+
+ // If the name points to a package's name, use
+ // the package main instead.
+ pkgMain = getOwn(config.pkgs, name);
+
+ return pkgMain ? pkgMain : name;
+ }
+
+ function makeShimExports(value) {
+ function fn() {
+ var ret;
+ if (value.init) {
+ ret = value.init.apply(global, arguments);
+ }
+ return ret || (value.exports && getGlobal(value.exports));
+ }
+ return fn;
+ }
+
+ function takeQueue(anonId) {
+ var i, id, args, shim;
+ for (i = 0; i < queue.length; i += 1) {
+ // Peek to see if anon
+ if (typeof queue[i][0] !== 'string') {
+ if (anonId) {
+ queue[i].unshift(anonId);
+ anonId = undef;
+ } else {
+ // Not our anon module, stop.
+ break;
+ }
+ }
+ args = queue.shift();
+ id = args[0];
+ i -= 1;
+
+ if (!(id in defined) && !(id in waiting)) {
+ if (id in deferreds) {
+ main.apply(undef, args);
+ } else {
+ waiting[id] = args;
+ }
+ }
+ }
+
+ // if get to the end and still have anonId, then could be
+ // a shimmed dependency.
+ if (anonId) {
+ shim = getOwn(config.shim, anonId) || {};
+ main(anonId, shim.deps || [], shim.exportsFn);
+ }
+ }
+
+ function makeRequire(relName, topLevel) {
+ var req = function (deps, callback, errback, alt) {
+ var name, cfg;
+
+ if (topLevel) {
+ takeQueue();
+ }
+
+ if (typeof deps === "string") {
+ if (handlers[deps]) {
+ return handlers[deps](relName);
+ }
+ // Just return the module wanted. In this scenario, the
+ // deps arg is the module name, and second arg (if passed)
+ // is just the relName.
+ // Normalize module name, if it contains . or ..
+ name = makeMap(deps, relName, true).id;
+ if (!(name in defined)) {
+ throw new Error('Not loaded: ' + name);
+ }
+ return defined[name];
+ } else if (deps && !Array.isArray(deps)) {
+ // deps is a config object, not an array.
+ cfg = deps;
+ deps = undef;
+
+ if (Array.isArray(callback)) {
+ // callback is an array, which means it is a dependency list.
+ // Adjust args if there are dependencies
+ deps = callback;
+ callback = errback;
+ errback = alt;
+ }
+
+ if (topLevel) {
+ // Could be a new context, so call returned require
+ return req.config(cfg)(deps, callback, errback);
+ }
+ }
+
+ // Support require(['a'])
+ callback = callback || function () {
+ // In case used later as a promise then value, return the
+ // arguments as an array.
+ return slice.call(arguments, 0);
+ };
+
+ // Complete async to maintain expected execution semantics.
+ return asyncResolve.then(function () {
+ // Grab any modules that were defined after a require call.
+ takeQueue();
+
+ return main(undef, deps || [], callback, errback, relName);
+ });
+ };
+
+ req.isBrowser = typeof document !== 'undefined' &&
+ typeof navigator !== 'undefined';
+
+ req.nameToUrl = function (moduleName, ext, skipExt) {
+ var paths, syms, i, parentModule, url,
+ parentPath, bundleId,
+ pkgMain = getOwn(config.pkgs, moduleName);
+
+ if (pkgMain) {
+ moduleName = pkgMain;
+ }
+
+ bundleId = getOwn(bundlesMap, moduleName);
+
+ if (bundleId) {
+ return req.nameToUrl(bundleId, ext, skipExt);
+ }
+
+ // If a colon is in the URL, it indicates a protocol is used and it is
+ // just an URL to a file, or if it starts with a slash, contains a query
+ // arg (i.e. ?) or ends with .js, then assume the user meant to use an
+ // url and not a module id. The slash is important for protocol-less
+ // URLs as well as full paths.
+ if (urlRegExp.test(moduleName)) {
+ // Just a plain path, not module name lookup, so just return it.
+ // Add extension if it is included. This is a bit wonky, only non-.js
+ // things pass an extension, this method probably needs to be
+ // reworked.
+ url = moduleName + (ext || '');
+ } else {
+ // A module that needs to be converted to a path.
+ paths = config.paths;
+
+ syms = moduleName.split('/');
+ // For each module name segment, see if there is a path
+ // registered for it. Start with most specific name
+ // and work up from it.
+ for (i = syms.length; i > 0; i -= 1) {
+ parentModule = syms.slice(0, i).join('/');
+
+ parentPath = getOwn(paths, parentModule);
+ if (parentPath) {
+ // If an array, it means there are a few choices,
+ // Choose the one that is desired
+ if (Array.isArray(parentPath)) {
+ parentPath = parentPath[0];
+ }
+ syms.splice(0, i, parentPath);
+ break;
+ }
+ }
+
+ // Join the path parts together, then figure out if baseUrl is needed.
+ url = syms.join('/');
+ url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js'));
+ url = (url.charAt(0) === '/' ||
+ url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
+ }
+
+ return config.urlArgs && !/^blob\:/.test(url) ?
+ url + config.urlArgs(moduleName, url) : url;
+ };
+
+ /**
+ * Converts a module name + .extension into an URL path.
+ * *Requires* the use of a module name. It does not support using
+ * plain URLs like nameToUrl.
+ */
+ req.toUrl = function (moduleNamePlusExt) {
+ var ext,
+ index = moduleNamePlusExt.lastIndexOf('.'),
+ segment = moduleNamePlusExt.split('/')[0],
+ isRelative = segment === '.' || segment === '..';
+
+ // Have a file extension alias, and it is not the
+ // dots from a relative path.
+ if (index !== -1 && (!isRelative || index > 1)) {
+ ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
+ moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
+ }
+
+ return req.nameToUrl(normalize(moduleNamePlusExt, relName), ext, true);
+ };
+
+ req.defined = function (id) {
+ return makeMap(id, relName, true).id in defined;
+ };
+
+ req.specified = function (id) {
+ id = makeMap(id, relName, true).id;
+ return id in defined || id in deferreds;
+ };
+
+ return req;
+ }
+
+ function resolve(name, d, value) {
+ if (name) {
+ defined[name] = value;
+ if (requirejs.onResourceLoad) {
+ requirejs.onResourceLoad(context, d.map, d.deps);
+ }
+ }
+ d.finished = true;
+ d.resolve(value);
+ }
+
+ function reject(d, err) {
+ d.finished = true;
+ d.rejected = true;
+ d.reject(err);
+ }
+
+ function makeNormalize(relName) {
+ return function (name) {
+ return normalize(name, relName, true);
+ };
+ }
+
+ function defineModule(d) {
+ d.factoryCalled = true;
+
+ var ret,
+ name = d.map.id;
+
+ try {
+ ret = context.execCb(name, d.factory, d.values, defined[name]);
+ } catch(err) {
+ return reject(d, err);
+ }
+
+ if (name) {
+ // Favor return value over exports. If node/cjs in play,
+ // then will not have a return value anyway. Favor
+ // module.exports assignment over exports object.
+ if (ret === undef) {
+ if (d.cjsModule) {
+ ret = d.cjsModule.exports;
+ } else if (d.usingExports) {
+ ret = defined[name];
+ }
+ }
+ } else {
+ // Remove the require deferred from the list to
+ // make cycle searching faster. Do not need to track
+ // it anymore either.
+ requireDeferreds.splice(requireDeferreds.indexOf(d), 1);
+ }
+ resolve(name, d, ret);
+ }
+
+ // This method is attached to every module deferred,
+ // so the "this" in here is the module deferred object.
+ function depFinished(val, i) {
+ if (!this.rejected && !this.depDefined[i]) {
+ this.depDefined[i] = true;
+ this.depCount += 1;
+ this.values[i] = val;
+ if (!this.depending && this.depCount === this.depMax) {
+ defineModule(this);
+ }
+ }
+ }
+
+ function makeDefer(name, calculatedMap) {
+ var d = {};
+ d.promise = new Promise(function (resolve, reject) {
+ d.resolve = resolve;
+ d.reject = function(err) {
+ if (!name) {
+ requireDeferreds.splice(requireDeferreds.indexOf(d), 1);
+ }
+ reject(err);
+ };
+ });
+ d.map = name ? (calculatedMap || makeMap(name)) : {};
+ d.depCount = 0;
+ d.depMax = 0;
+ d.values = [];
+ d.depDefined = [];
+ d.depFinished = depFinished;
+ if (d.map.pr) {
+ // Plugin resource ID, implicitly
+ // depends on plugin. Track it in deps
+ // so cycle breaking can work
+ d.deps = [makeMap(d.map.pr)];
+ }
+ return d;
+ }
+
+ function getDefer(name, calculatedMap) {
+ var d;
+ if (name) {
+ d = (name in deferreds) && deferreds[name];
+ if (!d) {
+ d = deferreds[name] = makeDefer(name, calculatedMap);
+ }
+ } else {
+ d = makeDefer();
+ requireDeferreds.push(d);
+ }
+ return d;
+ }
+
+ function makeErrback(d, name) {
+ return function (err) {
+ if (!d.rejected) {
+ if (!err.dynaId) {
+ err.dynaId = 'id' + (errCount += 1);
+ err.requireModules = [name];
+ }
+ reject(d, err);
+ }
+ };
+ }
+
+ function waitForDep(depMap, relName, d, i) {
+ d.depMax += 1;
+
+ // Do the fail at the end to catch errors
+ // in the then callback execution.
+ callDep(depMap, relName).then(function (val) {
+ d.depFinished(val, i);
+ }, makeErrback(d, depMap.id)).catch(makeErrback(d, d.map.id));
+ }
+
+ function makeLoad(id) {
+ var fromTextCalled;
+ function load(value) {
+ // Protect against older plugins that call load after
+ // calling load.fromText
+ if (!fromTextCalled) {
+ resolve(id, getDefer(id), value);
+ }
+ }
+
+ load.error = function (err) {
+ getDefer(id).reject(err);
+ };
+
+ load.fromText = function (text, textAlt) {
+ /*jslint evil: true */
+ var d = getDefer(id),
+ map = makeMap(makeMap(id).n),
+ plainId = map.id;
+
+ fromTextCalled = true;
+
+ // Set up the factory just to be a return of the value from
+ // plainId.
+ d.factory = function (p, val) {
+ return val;
+ };
+
+ // As of requirejs 2.1.0, support just passing the text, to reinforce
+ // fromText only being called once per resource. Still
+ // support old style of passing moduleName but discard
+ // that moduleName in favor of the internal ref.
+ if (textAlt) {
+ text = textAlt;
+ }
+
+ // Transfer any config to this other module.
+ if (hasProp(config.config, id)) {
+ config.config[plainId] = config.config[id];
+ }
+
+ try {
+ req.exec(text);
+ } catch (e) {
+ reject(d, new Error('fromText eval for ' + plainId +
+ ' failed: ' + e));
+ }
+
+ // Execute any waiting define created by the plainId
+ takeQueue(plainId);
+
+ // Mark this as a dependency for the plugin
+ // resource
+ d.deps = [map];
+ waitForDep(map, null, d, d.deps.length);
+ };
+
+ return load;
+ }
+
+ load = typeof importScripts === 'function' ?
+ function (map) {
+ var url = map.url;
+ if (urlFetched[url]) {
+ return;
+ }
+ urlFetched[url] = true;
+
+ // Ask for the deferred so loading is triggered.
+ // Do this before loading, since loading is sync.
+ getDefer(map.id);
+ importScripts(url);
+ takeQueue(map.id);
+ } :
+ function (map) {
+ var script,
+ id = map.id,
+ url = map.url;
+
+ if (urlFetched[url]) {
+ return;
+ }
+ urlFetched[url] = true;
+
+ script = document.createElement('script');
+ script.setAttribute('data-requiremodule', id);
+ script.type = config.scriptType || 'text/javascript';
+ script.charset = 'utf-8';
+ script.async = true;
+
+ loadCount += 1;
+
+ script.addEventListener('load', function () {
+ loadCount -= 1;
+ takeQueue(id);
+ }, false);
+ script.addEventListener('error', function () {
+ loadCount -= 1;
+ var err,
+ pathConfig = getOwn(config.paths, id);
+ if (pathConfig && Array.isArray(pathConfig) &&
+ pathConfig.length > 1) {
+ script.parentNode.removeChild(script);
+ // Pop off the first array value, since it failed, and
+ // retry
+ pathConfig.shift();
+ var d = getDefer(id);
+ d.map = makeMap(id);
+ // mapCache will have returned previous map value, update the
+ // url, which will also update mapCache value.
+ d.map.url = req.nameToUrl(id);
+ load(d.map);
+ } else {
+ err = new Error('Load failed: ' + id + ': ' + script.src);
+ err.requireModules = [id];
+ getDefer(id).reject(err);
+ }
+ }, false);
+
+ script.src = url;
+
+ // If the script is cached, IE10 executes the script body and the
+ // onload handler synchronously here. That's a spec violation,
+ // so be sure to do this asynchronously.
+ if (document.documentMode === 10) {
+ asap.then(function() {
+ document.head.appendChild(script);
+ });
+ } else {
+ document.head.appendChild(script);
+ }
+ };
+
+ function callPlugin(plugin, map, relName) {
+ plugin.load(map.n, makeRequire(relName), makeLoad(map.id), config);
+ }
+
+ callDep = function (map, relName) {
+ var args, bundleId,
+ name = map.id,
+ shim = config.shim[name];
+
+ if (name in waiting) {
+ args = waiting[name];
+ delete waiting[name];
+ main.apply(undef, args);
+ } else if (!(name in deferreds)) {
+ if (map.pr) {
+ // If a bundles config, then just load that file instead to
+ // resolve the plugin, as it is built into that bundle.
+ if ((bundleId = getOwn(bundlesMap, name))) {
+ map.url = req.nameToUrl(bundleId);
+ load(map);
+ } else {
+ return callDep(makeMap(map.pr)).then(function (plugin) {
+ // Redo map now that plugin is known to be loaded
+ var newMap = map.prn ? map : makeMap(name, relName, true),
+ newId = newMap.id,
+ shim = getOwn(config.shim, newId);
+
+ // Make sure to only call load once per resource. Many
+ // calls could have been queued waiting for plugin to load.
+ if (!(newId in calledPlugin)) {
+ calledPlugin[newId] = true;
+ if (shim && shim.deps) {
+ req(shim.deps, function () {
+ callPlugin(plugin, newMap, relName);
+ });
+ } else {
+ callPlugin(plugin, newMap, relName);
+ }
+ }
+ return getDefer(newId).promise;
+ });
+ }
+ } else if (shim && shim.deps) {
+ req(shim.deps, function () {
+ load(map);
+ });
+ } else {
+ load(map);
+ }
+ }
+
+ return getDefer(name).promise;
+ };
+
+ // Turns a plugin!resource to [plugin, resource]
+ // with the plugin being undefined if the name
+ // did not have a plugin prefix.
+ function splitPrefix(name) {
+ var prefix,
+ index = name ? name.indexOf('!') : -1;
+ if (index > -1) {
+ prefix = name.substring(0, index);
+ name = name.substring(index + 1, name.length);
+ }
+ return [prefix, name];
+ }
+
+ /**
+ * Makes a name map, normalizing the name, and using a plugin
+ * for normalization if necessary. Grabs a ref to plugin
+ * too, as an optimization.
+ */
+ makeMap = function (name, relName, applyMap) {
+ if (typeof name !== 'string') {
+ return name;
+ }
+
+ var plugin, url, parts, prefix, result, prefixNormalized,
+ cacheKey = name + ' & ' + (relName || '') + ' & ' + !!applyMap;
+
+ parts = splitPrefix(name);
+ prefix = parts[0];
+ name = parts[1];
+
+ if (!prefix && (cacheKey in mapCache)) {
+ return mapCache[cacheKey];
+ }
+
+ if (prefix) {
+ prefix = normalize(prefix, relName, applyMap);
+ plugin = (prefix in defined) && defined[prefix];
+ }
+
+ // Normalize according
+ if (prefix) {
+ if (plugin && plugin.normalize) {
+ name = plugin.normalize(name, makeNormalize(relName));
+ prefixNormalized = true;
+ } else {
+ // If nested plugin references, then do not try to
+ // normalize, as it will not normalize correctly. This
+ // places a restriction on resourceIds, and the longer
+ // term solution is not to normalize until plugins are
+ // loaded and all normalizations to allow for async
+ // loading of a loader plugin. But for now, fixes the
+ // common uses. Details in requirejs#1131
+ name = name.indexOf('!') === -1 ?
+ normalize(name, relName, applyMap) :
+ name;
+ }
+ } else {
+ name = normalize(name, relName, applyMap);
+ parts = splitPrefix(name);
+ prefix = parts[0];
+ name = parts[1];
+
+ url = req.nameToUrl(name);
+ }
+
+ // Using ridiculous property names for space reasons
+ result = {
+ id: prefix ? prefix + '!' + name : name, // fullName
+ n: name,
+ pr: prefix,
+ url: url,
+ prn: prefix && prefixNormalized
+ };
+
+ if (!prefix) {
+ mapCache[cacheKey] = result;
+ }
+
+ return result;
+ };
+
+ handlers = {
+ require: function (name) {
+ return makeRequire(name);
+ },
+ exports: function (name) {
+ var e = defined[name];
+ if (typeof e !== 'undefined') {
+ return e;
+ } else {
+ return (defined[name] = {});
+ }
+ },
+ module: function (name) {
+ return {
+ id: name,
+ uri: '',
+ exports: handlers.exports(name),
+ config: function () {
+ return getOwn(config.config, name) || {};
+ }
+ };
+ }
+ };
+
+ function breakCycle(d, traced, processed) {
+ var id = d.map.id;
+
+ traced[id] = true;
+ if (!d.finished && d.deps) {
+ d.deps.forEach(function (depMap) {
+ var depId = depMap.id,
+ dep = !hasProp(handlers, depId) && getDefer(depId, depMap);
+
+ // Only force things that have not completed
+ // being defined, so still in the registry,
+ // and only if it has not been matched up
+ // in the module already.
+ if (dep && !dep.finished && !processed[depId]) {
+ if (hasProp(traced, depId)) {
+ d.deps.forEach(function (depMap, i) {
+ if (depMap.id === depId) {
+ d.depFinished(defined[depId], i);
+ }
+ });
+ } else {
+ breakCycle(dep, traced, processed);
+ }
+ }
+ });
+ }
+ processed[id] = true;
+ }
+
+ function check(d) {
+ var err, mid, dfd,
+ notFinished = [],
+ waitInterval = config.waitSeconds * 1000,
+ // It is possible to disable the wait interval by using waitSeconds 0.
+ expired = waitInterval &&
+ (startTime + waitInterval) < (new Date()).getTime();
+
+ if (loadCount === 0) {
+ // If passed in a deferred, it is for a specific require call.
+ // Could be a sync case that needs resolution right away.
+ // Otherwise, if no deferred, means it was the last ditch
+ // timeout-based check, so check all waiting require deferreds.
+ if (d) {
+ if (!d.finished) {
+ breakCycle(d, {}, {});
+ }
+ } else if (requireDeferreds.length) {
+ requireDeferreds.forEach(function (d) {
+ breakCycle(d, {}, {});
+ });
+ }
+ }
+
+ // If still waiting on loads, and the waiting load is something
+ // other than a plugin resource, or there are still outstanding
+ // scripts, then just try back later.
+ if (expired) {
+ // If wait time expired, throw error of unloaded modules.
+ for (mid in deferreds) {
+ dfd = deferreds[mid];
+ if (!dfd.finished) {
+ notFinished.push(dfd.map.id);
+ }
+ }
+ err = new Error('Timeout for modules: ' + notFinished);
+ err.requireModules = notFinished;
+ req.onError(err);
+ } else if (loadCount || requireDeferreds.length) {
+ // Something is still waiting to load. Wait for it, but only
+ // if a later check is not already scheduled. Using setTimeout
+ // because want other things in the event loop to happen,
+ // to help in dependency resolution, and this is really a
+ // last ditch check, mostly for detecting timeouts (cycles
+ // should come through the main() use of check()), so it can
+ // wait a bit before doing the final check.
+ if (!checkingLater) {
+ checkingLater = true;
+ setTimeout(function () {
+ checkingLater = false;
+ check();
+ }, 70);
+ }
+ }
+ }
+
+ // Used to break out of the promise try/catch chains.
+ function delayedError(e) {
+ setTimeout(function () {
+ if (!e.dynaId || !trackedErrors[e.dynaId]) {
+ trackedErrors[e.dynaId] = true;
+ req.onError(e);
+ }
+ });
+ return e;
+ }
+
+ main = function (name, deps, factory, errback, relName) {
+ if (name) {
+ // Only allow main calling once per module.
+ if (name in calledDefine) {
+ return;
+ }
+ calledDefine[name] = true;
+ }
+
+ var d = getDefer(name);
+
+ // This module may not have dependencies
+ if (deps && !Array.isArray(deps)) {
+ // deps is not an array, so probably means
+ // an object literal or factory function for
+ // the value. Adjust args.
+ factory = deps;
+ deps = [];
+ }
+
+ // Create fresh array instead of modifying passed in value.
+ deps = deps ? slice.call(deps, 0) : null;
+
+ if (!errback) {
+ if (hasProp(config, 'defaultErrback')) {
+ if (config.defaultErrback) {
+ errback = config.defaultErrback;
+ }
+ } else {
+ errback = delayedError;
+ }
+ }
+
+ if (errback) {
+ d.promise.catch(errback);
+ }
+
+ // Use name if no relName
+ relName = relName || name;
+
+ // Call the factory to define the module, if necessary.
+ if (typeof factory === 'function') {
+
+ if (!deps.length && factory.length) {
+ // Remove comments from the callback string,
+ // look for require calls, and pull them into the dependencies,
+ // but only if there are function args.
+ factory
+ .toString()
+ .replace(commentRegExp, commentReplace)
+ .replace(cjsRequireRegExp, function (match, dep) {
+ deps.push(dep);
+ });
+
+ // May be a CommonJS thing even without require calls, but still
+ // could use exports, and module. Avoid doing exports and module
+ // work though if it just needs require.
+ // REQUIRES the function to expect the CommonJS variables in the
+ // order listed below.
+ deps = (factory.length === 1 ?
+ ['require'] :
+ ['require', 'exports', 'module']).concat(deps);
+ }
+
+ // Save info for use later.
+ d.factory = factory;
+ d.deps = deps;
+
+ d.depending = true;
+ deps.forEach(function (depName, i) {
+ var depMap;
+ deps[i] = depMap = makeMap(depName, relName, true);
+ depName = depMap.id;
+
+ // Fast path CommonJS standard dependencies.
+ if (depName === "require") {
+ d.values[i] = handlers.require(name);
+ } else if (depName === "exports") {
+ // CommonJS module spec 1.1
+ d.values[i] = handlers.exports(name);
+ d.usingExports = true;
+ } else if (depName === "module") {
+ // CommonJS module spec 1.1
+ d.values[i] = d.cjsModule = handlers.module(name);
+ } else if (depName === undefined) {
+ d.values[i] = undefined;
+ } else {
+ waitForDep(depMap, relName, d, i);
+ }
+ });
+ d.depending = false;
+
+ // Some modules just depend on the require, exports, modules, so
+ // trigger their definition here if so.
+ if (d.depCount === d.depMax) {
+ defineModule(d);
+ }
+ } else if (name) {
+ // May just be an object definition for the module. Only
+ // worry about defining if have a module name.
+ resolve(name, d, factory);
+ }
+
+ startTime = (new Date()).getTime();
+
+ if (!name) {
+ check(d);
+ }
+
+ return d.promise;
+ };
+
+ req = makeRequire(null, true);
+
+ /*
+ * Just drops the config on the floor, but returns req in case
+ * the config return value is used.
+ */
+ req.config = function (cfg) {
+ if (cfg.context && cfg.context !== contextName) {
+ var existingContext = getOwn(contexts, cfg.context);
+ if (existingContext) {
+ return existingContext.req.config(cfg);
+ } else {
+ return newContext(cfg.context).config(cfg);
+ }
+ }
+
+ // Since config changed, mapCache may not be valid any more.
+ mapCache = obj();
+
+ // Make sure the baseUrl ends in a slash.
+ if (cfg.baseUrl) {
+ if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
+ cfg.baseUrl += '/';
+ }
+ }
+
+ // Convert old style urlArgs string to a function.
+ if (typeof cfg.urlArgs === 'string') {
+ var urlArgs = cfg.urlArgs;
+ cfg.urlArgs = function(id, url) {
+ return (url.indexOf('?') === -1 ? '?' : '&') + urlArgs;
+ };
+ }
+
+ // Save off the paths and packages since they require special processing,
+ // they are additive.
+ var shim = config.shim,
+ objs = {
+ paths: true,
+ bundles: true,
+ config: true,
+ map: true
+ };
+
+ eachProp(cfg, function (value, prop) {
+ if (objs[prop]) {
+ if (!config[prop]) {
+ config[prop] = {};
+ }
+ mixin(config[prop], value, true, true);
+ } else {
+ config[prop] = value;
+ }
+ });
+
+ // Reverse map the bundles
+ if (cfg.bundles) {
+ eachProp(cfg.bundles, function (value, prop) {
+ value.forEach(function (v) {
+ if (v !== prop) {
+ bundlesMap[v] = prop;
+ }
+ });
+ });
+ }
+
+ // Merge shim
+ if (cfg.shim) {
+ eachProp(cfg.shim, function (value, id) {
+ // Normalize the structure
+ if (Array.isArray(value)) {
+ value = {
+ deps: value
+ };
+ }
+ if ((value.exports || value.init) && !value.exportsFn) {
+ value.exportsFn = makeShimExports(value);
+ }
+ shim[id] = value;
+ });
+ config.shim = shim;
+ }
+
+ // Adjust packages if necessary.
+ if (cfg.packages) {
+ cfg.packages.forEach(function (pkgObj) {
+ var location, name;
+
+ pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
+
+ name = pkgObj.name;
+ location = pkgObj.location;
+ if (location) {
+ config.paths[name] = pkgObj.location;
+ }
+
+ // Save pointer to main module ID for pkg name.
+ // Remove leading dot in main, so main paths are normalized,
+ // and remove any trailing .js, since different package
+ // envs have different conventions: some use a module name,
+ // some use a file name.
+ config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
+ .replace(currDirRegExp, '')
+ .replace(jsSuffixRegExp, '');
+ });
+ }
+
+ // If a deps array or a config callback is specified, then call
+ // require with those args. This is useful when require is defined as a
+ // config object before require.js is loaded.
+ if (cfg.deps || cfg.callback) {
+ req(cfg.deps, cfg.callback);
+ }
+
+ return req;
+ };
+
+ req.onError = function (err) {
+ throw err;
+ };
+
+ context = {
+ id: contextName,
+ defined: defined,
+ waiting: waiting,
+ config: config,
+ deferreds: deferreds,
+ req: req,
+ execCb: function execCb(name, callback, args, exports) {
+ return callback.apply(exports, args);
+ }
+ };
+
+ contexts[contextName] = context;
+
+ return req;
+ }
+
+ requirejs = topReq = newContext('_');
+
+ if (typeof require !== 'function') {
+ require = topReq;
+ }
+
+ /**
+ * Executes the text. Normally just uses eval, but can be modified
+ * to use a better, environment-specific call. Only used for transpiling
+ * loader plugins, not for plain JS modules.
+ * @param {String} text the text to execute/evaluate.
+ */
+ topReq.exec = function (text) {
+ /*jslint evil: true */
+ return eval(text);
+ };
+
+ topReq.contexts = contexts;
+
+ define = function () {
+ queue.push(slice.call(arguments, 0));
+ };
+
+ define.amd = {
+ jQuery: true
+ };
+
+ if (bootstrapConfig) {
+ topReq.config(bootstrapConfig);
+ }
+
+ // data-main support.
+ if (topReq.isBrowser && !contexts._.config.skipDataMain) {
+ dataMain = document.querySelectorAll('script[data-main]')[0];
+ dataMain = dataMain && dataMain.getAttribute('data-main');
+ if (dataMain) {
+ // Strip off any trailing .js since dataMain is now
+ // like a module name.
+ dataMain = dataMain.replace(jsSuffixRegExp, '');
+
+ // Set final baseUrl if there is not already an explicit one,
+ // but only do so if the data-main value is not a loader plugin
+ // module ID.
+ if ((!bootstrapConfig || !bootstrapConfig.baseUrl) &&
+ dataMain.indexOf('!') === -1) {
+ // Pull off the directory of data-main for use as the
+ // baseUrl.
+ src = dataMain.split('/');
+ dataMain = src.pop();
+ subPath = src.length ? src.join('/') + '/' : './';
+
+ topReq.config({baseUrl: subPath});
+ }
+
+ topReq([dataMain]);
+ }
+ }
+}(this, (typeof Promise !== 'undefined' ? Promise : undefined)));
+
+define("requireLib", function(){});
+
+//noinspection JSUnresolvedVariable
+requirejs.config({
+ paths: {
+ enquire: '3rdParty/enquire',
+ favico: '3rdParty/favico',
+ 'perfect-scrollbar': '3rdParty/perfect-scrollbar',
+ 'Pica': '3rdParty/pica',
+ prism: '3rdParty/prism',
+ },
+ shim: {
+ enquire: { exports: 'enquire' },
+ favico: { exports: 'Favico' },
+ 'perfect-scrollbar': { exports: 'PerfectScrollbar' }
+ },
+ map: {
+ '*': {
+ 'Ajax': 'WoltLabSuite/Core/Ajax',
+ 'AjaxJsonp': 'WoltLabSuite/Core/Ajax/Jsonp',
+ 'AjaxRequest': 'WoltLabSuite/Core/Ajax/Request',
+ 'CallbackList': 'WoltLabSuite/Core/CallbackList',
+ 'ColorUtil': 'WoltLabSuite/Core/ColorUtil',
+ 'Core': 'WoltLabSuite/Core/Core',
+ 'DateUtil': 'WoltLabSuite/Core/Date/Util',
+ 'Devtools': 'WoltLabSuite/Core/Devtools',
+ 'Dictionary': 'WoltLabSuite/Core/Dictionary',
+ 'Dom/ChangeListener': 'WoltLabSuite/Core/Dom/Change/Listener',
+ 'Dom/Traverse': 'WoltLabSuite/Core/Dom/Traverse',
+ 'Dom/Util': 'WoltLabSuite/Core/Dom/Util',
+ 'Environment': 'WoltLabSuite/Core/Environment',
+ 'EventHandler': 'WoltLabSuite/Core/Event/Handler',
+ 'EventKey': 'WoltLabSuite/Core/Event/Key',
+ 'Language': 'WoltLabSuite/Core/Language',
+ 'List': 'WoltLabSuite/Core/List',
+ 'ObjectMap': 'WoltLabSuite/Core/ObjectMap',
+ 'Permission': 'WoltLabSuite/Core/Permission',
+ 'StringUtil': 'WoltLabSuite/Core/StringUtil',
+ 'Ui/Alignment': 'WoltLabSuite/Core/Ui/Alignment',
+ 'Ui/CloseOverlay': 'WoltLabSuite/Core/Ui/CloseOverlay',
+ 'Ui/Confirmation': 'WoltLabSuite/Core/Ui/Confirmation',
+ 'Ui/Dialog': 'WoltLabSuite/Core/Ui/Dialog',
+ 'Ui/Notification': 'WoltLabSuite/Core/Ui/Notification',
+ 'Ui/ReusableDropdown': 'WoltLabSuite/Core/Ui/Dropdown/Reusable',
+ 'Ui/Screen': 'WoltLabSuite/Core/Ui/Screen',
+ 'Ui/Scroll': 'WoltLabSuite/Core/Ui/Scroll',
+ 'Ui/SimpleDropdown': 'WoltLabSuite/Core/Ui/Dropdown/Simple',
+ 'Ui/TabMenu': 'WoltLabSuite/Core/Ui/TabMenu',
+ 'Upload': 'WoltLabSuite/Core/Upload',
+ 'User': 'WoltLabSuite/Core/User'
+ }
+ },
+ waitSeconds: 0
+});
+
+/* Define jQuery shim. We cannot use the shim object in the configuration above,
+ because it tries to load the file, even if the exported global already exists.
+ This shim is needed for jQuery plugins supporting an AMD loaded jQuery, because
+ we break the AMD support of jQuery for BC reasons.
+*/
+define('jquery', [],function() {
+ return window.jQuery;
+});
+
+
+define("require.config", function(){});
+
+/**
+ * Collection of global short hand functions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+(function(window, document) {
+ /**
+ * Shorthand function to retrieve or set an attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @param {?=} value attribute value, omit if attribute should be read
+ * @return {(string|undefined)} attribute value, empty string if attribute is not set or undefined if `value` was omitted
+ */
+ window.elAttr = function(element, attribute, value) {
+ if (value === undefined) {
+ return element.getAttribute(attribute) || '';
+ }
+
+ element.setAttribute(attribute, value);
+ };
+
+ /**
+ * Shorthand function to retrieve a boolean attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @return {boolean} true if value is either `1` or `true`
+ */
+ window.elAttrBool = function(element, attribute) {
+ var value = elAttr(element, attribute);
+
+ return (value === "1" || value === "true");
+ };
+
+ /**
+ * Shorthand function to find elements by class name.
+ *
+ * @param {string} className CSS class name
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @return {NodeList} matching elements
+ */
+ window.elByClass = function(className, context) {
+ return (context || document).getElementsByClassName(className);
+ };
+
+ /**
+ * Shorthand function to retrieve an element by id.
+ *
+ * @param {string} id element id
+ * @return {(Element|null)} matching element or null if not found
+ */
+ window.elById = function(id) {
+ return document.getElementById(id);
+ };
+
+ /**
+ * Shorthand function to find an element by CSS selector.
+ *
+ * @param {string} selector CSS selector
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @return {(Element|null)} matching element or null if no match
+ */
+ window.elBySel = function(selector, context) {
+ return (context || document).querySelector(selector);
+ };
+
+ /**
+ * Shorthand function to find elements by CSS selector.
+ *
+ * @param {string} selector CSS selector
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @param {function=} callback callback function passed to forEach()
+ * @return {NodeList} matching elements
+ */
+ window.elBySelAll = function(selector, context, callback) {
+ var nodeList = (context || document).querySelectorAll(selector);
+ if (typeof callback === 'function') {
+ Array.prototype.forEach.call(nodeList, callback);
+ }
+
+ return nodeList;
+ };
+
+ /**
+ * Shorthand function to find elements by tag name.
+ *
+ * @param {string} tagName element tag name
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @return {NodeList} matching elements
+ */
+ window.elByTag = function(tagName, context) {
+ return (context || document).getElementsByTagName(tagName);
+ };
+
+ /**
+ * Shorthand function to create a DOM element.
+ *
+ * @param {string} tagName element tag name
+ * @return {Element} new DOM element
+ */
+ window.elCreate = function(tagName) {
+ return document.createElement(tagName);
+ };
+
+ /**
+ * Returns the closest element (parent for text nodes), optionally matching
+ * the provided selector.
+ *
+ * @param {Node} node start node
+ * @param {string=} selector optional CSS selector
+ * @return {Element} closest matching element
+ */
+ window.elClosest = function (node, selector) {
+ if (!(node instanceof Node)) {
+ throw new TypeError('Provided element is not a Node.');
+ }
+
+ // retrieve the parent element for text nodes
+ if (node.nodeType === Node.TEXT_NODE) {
+ node = node.parentNode;
+
+ // text node had no parent
+ if (node === null) return null;
+ }
+
+ if (typeof selector !== 'string') selector = '';
+
+ if (selector.length === 0) return node;
+
+ return node.closest(selector);
+ };
+
+ /**
+ * Shorthand function to retrieve or set a 'data-' attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @param {?=} value attribute value, omit if attribute should be read
+ * @return {(string|undefined)} attribute value, empty string if attribute is not set or undefined if `value` was omitted
+ */
+ window.elData = function(element, attribute, value) {
+ attribute = 'data-' + attribute;
+
+ if (value === undefined) {
+ return element.getAttribute(attribute) || '';
+ }
+
+ element.setAttribute(attribute, value);
+ };
+
+ /**
+ * Shorthand function to retrieve a boolean 'data-' attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @return {boolean} true if value is either `1` or `true`
+ */
+ window.elDataBool = function(element, attribute) {
+ var value = elData(element, attribute);
+
+ return (value === "1" || value === "true");
+ };
+
+ /**
+ * Shorthand function to hide an element by setting its 'display' value to 'none'.
+ *
+ * @param {Element} element DOM element
+ */
+ window.elHide = function(element) {
+ element.style.setProperty('display', 'none', '');
+ };
+
+ /**
+ * Shorthand function to check if given element is hidden by setting its 'display'
+ * value to 'none'.
+ *
+ * @param {Element} element DOM element
+ * @return {boolean}
+ */
+ window.elIsHidden = function(element) {
+ return element.style.getPropertyValue('display') === 'none';
+ }
+
+ /**
+ * Displays or removes an error message below the provided element.
+ *
+ * @param {Element} element DOM element
+ * @param {string?} errorMessage error message; `false`, `null` and `undefined` are treated as an empty string
+ * @param {boolean?} isHtml defaults to false, causes `errorMessage` to be treated as text only
+ * @return {?Element} the inner error element or null if it was removed
+ */
+ window.elInnerError = function (element, errorMessage, isHtml) {
+ var parent = element.parentNode;
+ if (parent === null) {
+ throw new Error('Only elements that have a parent element or document are valid.');
+ }
+
+ if (typeof errorMessage !== 'string') {
+ if (errorMessage === undefined || errorMessage === null || errorMessage === false) {
+ errorMessage = '';
+ }
+ else {
+ throw new TypeError('The error message must be a string; `false`, `null` or `undefined` can be used as a substitute for an empty string.');
+ }
+ }
+
+ var innerError = element.nextElementSibling;
+ if (innerError === null || innerError.nodeName !== 'SMALL' || !innerError.classList.contains('innerError')) {
+ if (errorMessage === '') {
+ innerError = null;
+ }
+ else {
+ innerError = elCreate('small');
+ innerError.className = 'innerError';
+ parent.insertBefore(innerError, element.nextSibling);
+ }
+ }
+
+ if (errorMessage === '') {
+ if (innerError !== null) {
+ parent.removeChild(innerError);
+ innerError = null;
+ }
+ }
+ else {
+ innerError[(isHtml ? 'innerHTML' : 'textContent')] = errorMessage;
+ }
+
+ return innerError;
+ };
+
+ /**
+ * Shorthand function to remove an element.
+ *
+ * @param {Node} element DOM node
+ */
+ window.elRemove = function(element) {
+ element.parentNode.removeChild(element);
+ };
+
+ /**
+ * Shorthand function to show an element previously hidden by using `elHide()`.
+ *
+ * @param {Element} element DOM element
+ */
+ window.elShow = function(element) {
+ element.style.removeProperty('display');
+ };
+
+ /**
+ * Toggles visibility of an element using the display style.
+ *
+ * @param {Element} element DOM element
+ */
+ window.elToggle = function (element) {
+ if (element.style.getPropertyValue('display') === 'none') {
+ elShow(element);
+ }
+ else {
+ elHide(element);
+ }
+ };
+
+ /**
+ * Shorthand function to iterative over an array-like object, arguments passed are the value and the index second.
+ *
+ * Do not use this function if a simple `for()` is enough or `list` is a plain object.
+ *
+ * @param {object} list array-like object
+ * @param {function} callback callback function
+ */
+ window.forEach = function(list, callback) {
+ for (var i = 0, length = list.length; i < length; i++) {
+ callback(list[i], i);
+ }
+ };
+
+ /**
+ * Shorthand function to check if an object has a property while ignoring the chain.
+ *
+ * @param {object} obj target object
+ * @param {string} property property name
+ * @return {boolean} false if property does not exist or belongs to the chain
+ */
+ window.objOwns = function(obj, property) {
+ return obj.hasOwnProperty(property);
+ };
+
+ /* assigns a global constant defining the proper 'click' event depending on the browser,
+ enforcing 'touchstart' on mobile devices for a better UX. We're using defineProperty()
+ here because at the time of writing Safari does not support 'const'. Thanks Safari.
+ */
+ var clickEvent = ('touchstart' in document.documentElement || 'ontouchstart' in window || navigator.MaxTouchPoints > 0 || navigator.msMaxTouchPoints > 0) ? 'touchstart' : 'click';
+ Object.defineProperty(window, 'WCF_CLICK_EVENT', {
+ value: 'click' //clickEvent
+ });
+
+ /* Overwrites any history states after 'initial' with 'skip' on initial page load.
+ This is done, as the necessary DOM of other history states may not exist any more.
+ On forward navigation these 'skip' states are automatically skipped, otherwise the
+ user might have to press the forward button several times.
+ Note: A 'skip' state cannot be hit in the 'popstate' event when navigation backwards,
+ because the history already is left of all the 'skip' states for the current page.
+ Note 2: Setting the URL component of `history.replaceState()` to an empty string will
+ cause the Internet Explorer to discard the path and query string from the
+ address bar.
+ */
+ (function() {
+ var stateDepth = 0;
+ function check() {
+ if (window.history.state && window.history.state.name && window.history.state.name !== 'initial') {
+ window.history.replaceState({
+ name: 'skip',
+ depth: ++stateDepth
+ }, '');
+ window.history.back();
+
+ // window.history does not update in this iteration of the event loop
+ setTimeout(check, 1);
+ }
+ else {
+ window.history.replaceState({name: 'initial'}, '');
+ }
+ }
+ check();
+
+ window.addEventListener('popstate', function(event) {
+ if (event.state && event.state.name && event.state.name === 'skip') {
+ window.history.go(event.state.depth);
+ }
+ });
+ })();
+
+ /**
+ * Provides a hashCode() method for strings, similar to Java's String.hashCode().
+ *
+ * @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
+ */
+ window.String.prototype.hashCode = function() {
+ var $char;
+ var $hash = 0;
+
+ if (this.length) {
+ for (var $i = 0, $length = this.length; $i < $length; $i++) {
+ $char = this.charCodeAt($i);
+ $hash = (($hash << 5) - $hash) + $char;
+ $hash = $hash & $hash; // convert to 32bit integer
+ }
+ }
+
+ return $hash;
+ };
+})(window, document);
+
+define("wcf.globalHelper", function(){});
+
+/**
+ * Provides the basic core functionality.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Core
+ */
+define('WoltLabSuite/Core/Core',[], function() {
+ "use strict";
+
+ var _clone = function(variable) {
+ if (typeof variable === 'object' && (Array.isArray(variable) || Core.isPlainObject(variable))) {
+ return _cloneObject(variable);
+ }
+
+ return variable;
+ };
+
+ var _cloneObject = function(obj) {
+ if (!obj) {
+ return null;
+ }
+
+ if (Array.isArray(obj)) {
+ return obj.slice();
+ }
+
+ var newObj = {};
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') {
+ newObj[key] = _clone(obj[key]);
+ }
+ }
+
+ return newObj;
+ };
+
+ //noinspection JSUnresolvedVariable
+ var _prefix = 'wsc' + window.WCF_PATH.hashCode() + '-';
+
+ /**
+ * @exports WoltLabSuite/Core/Core
+ */
+ var Core = {
+ /**
+ * Deep clones an object.
+ *
+ * @param {object} obj source object
+ * @return {object} cloned object
+ */
+ clone: function(obj) {
+ return _clone(obj);
+ },
+
+ /**
+ * Converts WCF 2.0-style URLs into the default URL layout.
+ *
+ * @param string url target url
+ * @return rewritten url
+ */
+ convertLegacyUrl: function(url) {
+ return url.replace(/^index\.php\/(.*?)\/\?/, function(match, controller) {
+ var parts = controller.split(/([A-Z][a-z0-9]+)/);
+ controller = '';
+ for (var i = 0, length = parts.length; i < length; i++) {
+ var part = parts[i].trim();
+ if (part.length) {
+ if (controller.length) controller += '-';
+ controller += part.toLowerCase();
+ }
+ }
+
+ return 'index.php?' + controller + '/&';
+ });
+ },
+
+ /**
+ * Merges objects with the first argument.
+ *
+ * @param {object} out destination object
+ * @param {...object} arguments variable number of objects to be merged into the destination object
+ * @return {object} destination object with all provided objects merged into
+ */
+ extend: function(out) {
+ out = out || {};
+ var newObj = this.clone(out);
+
+ for (var i = 1, length = arguments.length; i < length; i++) {
+ var obj = arguments[i];
+
+ if (!obj) continue;
+
+ for (var key in obj) {
+ if (objOwns(obj, key)) {
+ if (!Array.isArray(obj[key]) && typeof obj[key] === 'object') {
+ if (this.isPlainObject(obj[key])) {
+ // object literals have the prototype of Object which in return has no parent prototype
+ newObj[key] = this.extend(out[key], obj[key]);
+ }
+ else {
+ newObj[key] = obj[key];
+ }
+ }
+ else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ }
+
+ return newObj;
+ },
+
+ /**
+ * Inherits the prototype methods from one constructor to another
+ * constructor.
+ *
+ * Usage:
+ *
+ * function MyDerivedClass() {}
+ * Core.inherit(MyDerivedClass, TheAwesomeBaseClass, {
+ * // regular prototype for `MyDerivedClass`
+ *
+ * overwrittenMethodFromBaseClass: function(foo, bar) {
+ * // do stuff
+ *
+ * // invoke parent
+ * MyDerivedClass._super.prototype.overwrittenMethodFromBaseClass.call(this, foo, bar);
+ * }
+ * });
+ *
+ * @see https://github.com/nodejs/node/blob/7d14dd9b5e78faabb95d454a79faa513d0bbc2a5/lib/util.js#L697-L735
+ * @param {function} constructor inheriting constructor function
+ * @param {function} superConstructor inherited constructor function
+ * @param {object=} propertiesObject additional prototype properties
+ */
+ inherit: function(constructor, superConstructor, propertiesObject) {
+ if (constructor === undefined || constructor === null) {
+ throw new TypeError("The constructor must not be undefined or null.");
+ }
+ if (superConstructor === undefined || superConstructor === null) {
+ throw new TypeError("The super constructor must not be undefined or null.");
+ }
+ if (superConstructor.prototype === undefined) {
+ throw new TypeError("The super constructor must have a prototype.");
+ }
+
+ constructor._super = superConstructor;
+ constructor.prototype = Core.extend(Object.create(superConstructor.prototype, {
+ constructor: {
+ configurable: true,
+ enumerable: false,
+ value: constructor,
+ writable: true
+ }
+ }), propertiesObject || {});
+ },
+
+ /**
+ * Returns true if `obj` is an object literal.
+ *
+ * @param {*} obj target object
+ * @returns {boolean} true if target is an object literal
+ */
+ isPlainObject: function(obj) {
+ if (typeof obj !== 'object' || obj === null || obj.nodeType) {
+ return false;
+ }
+
+ return (Object.getPrototypeOf(obj) === Object.prototype);
+ },
+
+ /**
+ * Returns the object's class name.
+ *
+ * @param {object} obj target object
+ * @return {string} object class name
+ */
+ getType: function(obj) {
+ return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1');
+ },
+
+ /**
+ * Returns a RFC4122 version 4 compilant UUID.
+ *
+ * @see http://stackoverflow.com/a/2117523
+ * @return {string}
+ */
+ getUuid: function() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ });
+ },
+
+ /**
+ * Recursively serializes an object into an encoded URI parameter string.
+ *
+ * @param {object} obj target object
+ * @param {string=} prefix parameter prefix
+ * @return {string} encoded parameter string
+ */
+ serialize: function(obj, prefix) {
+ var parameters = [];
+
+ for (var key in obj) {
+ if (objOwns(obj, key)) {
+ var parameterKey = (prefix) ? prefix + '[' + key + ']' : key;
+ var value = obj[key];
+
+ if (typeof value === 'object') {
+ parameters.push(this.serialize(value, parameterKey));
+ }
+ else {
+ parameters.push(encodeURIComponent(parameterKey) + '=' + encodeURIComponent(value));
+ }
+ }
+ }
+
+ return parameters.join('&');
+ },
+
+ /**
+ * Triggers a custom or built-in event.
+ *
+ * @param {Element} element target element
+ * @param {string} eventName event name
+ */
+ triggerEvent: function(element, eventName) {
+ var event;
+
+ try {
+ event = new Event(eventName, {
+ bubbles: true,
+ cancelable: true
+ });
+ }
+ catch (e) {
+ event = document.createEvent('Event');
+ event.initEvent(eventName, true, true);
+ }
+
+ element.dispatchEvent(event);
+ },
+
+ /**
+ * Returns the unique prefix for the localStorage.
+ *
+ * @return {string} prefix for the localStorage
+ */
+ getStoragePrefix: function() {
+ return _prefix;
+ }
+ };
+
+ return Core;
+});
+
+/**
+ * Dictionary implementation relying on an object or if supported on a Map to hold key => value data.
+ *
+ * If you're looking for a dictionary with object keys, please see `WoltLabSuite/Core/ObjectMap`.
+ *
+ * @author Tim Duesterhus, Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dictionary
+ */
+define('WoltLabSuite/Core/Dictionary',['Core'], function(Core) {
+ "use strict";
+
+ var _hasMap = objOwns(window, 'Map') && typeof window.Map === 'function';
+
+ /**
+ * @constructor
+ */
+ function Dictionary() {
+ this._dictionary = (_hasMap) ? new Map() : {};
+ }
+ Dictionary.prototype = {
+ /**
+ * Sets a new key with given value, will overwrite an existing key.
+ *
+ * @param {(number|string)} key key
+ * @param {?} value value
+ */
+ set: function(key, value) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (typeof key !== "string") {
+ throw new TypeError("Only strings can be used as keys, rejected '" + key + "' (" + typeof key + ").");
+ }
+
+ if (_hasMap) this._dictionary.set(key, value);
+ else this._dictionary[key] = value;
+ },
+
+ /**
+ * Removes a key from the dictionary.
+ *
+ * @param {(number|string)} key key
+ */
+ 'delete': function(key) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (_hasMap) this._dictionary['delete'](key);
+ else this._dictionary[key] = undefined;
+ },
+
+ /**
+ * Returns true if dictionary contains a value for given key and is not undefined.
+ *
+ * @param {(number|string)} key key
+ * @return {boolean} true if key exists and value is not undefined
+ */
+ has: function(key) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (_hasMap) return this._dictionary.has(key);
+ else {
+ return (objOwns(this._dictionary, key) && typeof this._dictionary[key] !== "undefined");
+ }
+ },
+
+ /**
+ * Retrieves a value by key, returns undefined if there is no match.
+ *
+ * @param {(number|string)} key key
+ * @return {*}
+ */
+ get: function(key) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (this.has(key)) {
+ if (_hasMap) return this._dictionary.get(key);
+ else return this._dictionary[key];
+ }
+
+ return undefined;
+ },
+
+ /**
+ * Iterates over the dictionary keys and values, callback function should expect the
+ * value as first parameter and the key name second.
+ *
+ * @param {function<*, string>} callback callback for each iteration
+ */
+ forEach: function(callback) {
+ if (typeof callback !== "function") {
+ throw new TypeError("forEach() expects a callback as first parameter.");
+ }
+
+ if (_hasMap) {
+ this._dictionary.forEach(callback);
+ }
+ else {
+ var keys = Object.keys(this._dictionary);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ callback(this._dictionary[keys[i]], keys[i]);
+ }
+ }
+ },
+
+ /**
+ * Merges one or more Dictionary instances into this one.
+ *
+ * @param {...Dictionary} var_args one or more Dictionary instances
+ */
+ merge: function() {
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var dictionary = arguments[i];
+ if (!(dictionary instanceof Dictionary)) {
+ throw new TypeError("Expected an object of type Dictionary, but argument " + i + " is not.");
+ }
+
+ dictionary.forEach((function(value, key) {
+ this.set(key, value);
+ }).bind(this));
+ }
+ },
+
+ /**
+ * Returns the object representation of the dictionary.
+ *
+ * @return {object} dictionary's object representation
+ */
+ toObject: function() {
+ if (!_hasMap) return Core.clone(this._dictionary);
+
+ var object = { };
+ this._dictionary.forEach(function(value, key) {
+ object[key] = value;
+ });
+
+ return object;
+ }
+ };
+
+ /**
+ * Creates a new Dictionary based on the given object.
+ * All properties that are owned by the object will be added
+ * as keys to the resulting Dictionary.
+ *
+ * @param {object} object
+ * @return {Dictionary}
+ */
+ Dictionary.fromObject = function(object) {
+ var result = new Dictionary();
+
+ for (var key in object) {
+ if (objOwns(object, key)) {
+ result.set(key, object[key]);
+ }
+ }
+
+ return result;
+ };
+
+ Object.defineProperty(Dictionary.prototype, 'size', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
+ if (_hasMap) {
+ return this._dictionary.size;
+ }
+ else {
+ return Object.keys(this._dictionary).length;
+ }
+ }
+ });
+
+ return Dictionary;
+});
+
+
+
+define('WoltLabSuite/Core/Template.grammar',['require'],function(require){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[2,37],$V1=[5,9,11,12,13,18,19,21,22,23,25,26,27,28,30,31,32,33,35,37,39],$V2=[1,24],$V3=[1,25],$V4=[1,31],$V5=[1,29],$V6=[1,30],$V7=[1,26],$V8=[1,27],$V9=[1,33],$Va=[11,12,15,40,41,45,47,49,50,52],$Vb=[9,11,12,13,18,19,21,23,26,28,30,31,32,33,35,37],$Vc=[11,12,15,40,41,44,45,46,47,49,50,52],$Vd=[18,35,37],$Ve=[12,15];
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"TEMPLATE":3,"CHUNK_STAR":4,"EOF":5,"CHUNK_STAR_repetition0":6,"CHUNK":7,"PLAIN_ANY":8,"T_LITERAL":9,"COMMAND":10,"T_ANY":11,"T_WS":12,"{if":13,"COMMAND_PARAMETERS":14,"}":15,"COMMAND_repetition0":16,"COMMAND_option0":17,"{/if}":18,"{include":19,"COMMAND_PARAMETER_LIST":20,"{implode":21,"{/implode}":22,"{foreach":23,"COMMAND_option1":24,"{/foreach}":25,"{lang}":26,"{/lang}":27,"{":28,"VARIABLE":29,"{#":30,"{@":31,"{ldelim}":32,"{rdelim}":33,"ELSE":34,"{else}":35,"ELSE_IF":36,"{elseif":37,"FOREACH_ELSE":38,"{foreachelse}":39,"T_VARIABLE":40,"T_VARIABLE_NAME":41,"VARIABLE_repetition0":42,"VARIABLE_SUFFIX":43,"[":44,"]":45,".":46,"(":47,"VARIABLE_SUFFIX_option0":48,")":49,"=":50,"COMMAND_PARAMETER_VALUE":51,"T_QUOTED_STRING":52,"COMMAND_PARAMETERS_repetition_plus0":53,"COMMAND_PARAMETER":54,"$accept":0,"$end":1},
+terminals_: {2:"error",5:"EOF",9:"T_LITERAL",11:"T_ANY",12:"T_WS",13:"{if",15:"}",18:"{/if}",19:"{include",21:"{implode",22:"{/implode}",23:"{foreach",25:"{/foreach}",26:"{lang}",27:"{/lang}",28:"{",30:"{#",31:"{@",32:"{ldelim}",33:"{rdelim}",35:"{else}",37:"{elseif",39:"{foreachelse}",40:"T_VARIABLE",41:"T_VARIABLE_NAME",44:"[",45:"]",46:".",47:"(",49:")",50:"=",52:"T_QUOTED_STRING"},
+productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[8,1],[8,1],[10,7],[10,3],[10,5],[10,6],[10,3],[10,3],[10,3],[10,3],[10,1],[10,1],[34,2],[36,4],[38,2],[29,3],[43,3],[43,2],[43,3],[20,5],[20,3],[51,1],[51,1],[14,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,3],[6,0],[6,2],[16,0],[16,2],[17,0],[17,1],[24,0],[24,1],[42,0],[42,2],[48,0],[48,1],[53,1],[53,2]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1:
+ return $$[$0-1] + ";";
+break;
+case 2:
+
+ var result = $$[$0].reduce(function (carry, item) {
+ if (item.encode && !carry[1]) carry[0] += " + '" + item.value;
+ else if (item.encode && carry[1]) carry[0] += item.value;
+ else if (!item.encode && carry[1]) carry[0] += "' + " + item.value;
+ else if (!item.encode && !carry[1]) carry[0] += " + " + item.value;
+
+ carry[1] = item.encode;
+ return carry;
+ }, [ "''", false ]);
+ if (result[1]) result[0] += "'";
+
+ this.$ = result[0];
+
+break;
+case 3: case 4:
+this.$ = { encode: true, value: $$[$0].replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/(\r\n|\n|\r)/g, '\\n') };
+break;
+case 5:
+this.$ = { encode: false, value: $$[$0] };
+break;
+case 8:
+
+ this.$ = "(function() { if (" + $$[$0-5] + ") { return " + $$[$0-3] + "; } " + $$[$0-2].join(' ') + " " + ($$[$0-1] || '') + " return ''; })()";
+
+break;
+case 9:
+
+ if (!$$[$0-1]['file']) throw new Error('Missing parameter file');
+
+ this.$ = $$[$0-1]['file'] + ".fetch(v)";
+
+break;
+case 10:
+
+ if (!$$[$0-3]['from']) throw new Error('Missing parameter from');
+ if (!$$[$0-3]['item']) throw new Error('Missing parameter item');
+ if (!$$[$0-3]['glue']) $$[$0-3]['glue'] = "', '";
+
+ this.$ = "(function() { return " + $$[$0-3]['from'] + ".map(function(item) { v[" + $$[$0-3]['item'] + "] = item; return " + $$[$0-1] + "; }).join(" + $$[$0-3]['glue'] + "); })()";
+
+break;
+case 11:
+
+ if (!$$[$0-4]['from']) throw new Error('Missing parameter from');
+ if (!$$[$0-4]['item']) throw new Error('Missing parameter item');
+
+ this.$ = "(function() {"
+ + "var looped = false, result = '';"
+ + "if (" + $$[$0-4]['from'] + " instanceof Array) {"
+ + "for (var i = 0; i < " + $$[$0-4]['from'] + ".length; i++) { looped = true;"
+ + "v[" + $$[$0-4]['key'] + "] = i;"
+ + "v[" + $$[$0-4]['item'] + "] = " + $$[$0-4]['from'] + "[i];"
+ + "result += " + $$[$0-2] + ";"
+ + "}"
+ + "} else {"
+ + "for (var key in " + $$[$0-4]['from'] + ") {"
+ + "if (!" + $$[$0-4]['from'] + ".hasOwnProperty(key)) continue;"
+ + "looped = true;"
+ + "v[" + $$[$0-4]['key'] + "] = key;"
+ + "v[" + $$[$0-4]['item'] + "] = " + $$[$0-4]['from'] + "[key];"
+ + "result += " + $$[$0-2] + ";"
+ + "}"
+ + "}"
+ + "return (looped ? result : " + ($$[$0-1] || "''") + "); })()"
+
+break;
+case 12:
+this.$ = "Language.get(" + $$[$0-1] + ", v)";
+break;
+case 13:
+this.$ = "StringUtil.escapeHTML(" + $$[$0-1] + ")";
+break;
+case 14:
+this.$ = "StringUtil.formatNumeric(" + $$[$0-1] + ")";
+break;
+case 15:
+this.$ = $$[$0-1];
+break;
+case 16:
+this.$ = "'{'";
+break;
+case 17:
+this.$ = "'}'";
+break;
+case 18:
+this.$ = "else { return " + $$[$0] + "; }";
+break;
+case 19:
+this.$ = "else if (" + $$[$0-2] + ") { return " + $$[$0] + "; }";
+break;
+case 20:
+this.$ = $$[$0];
+break;
+case 21:
+this.$ = "v['" + $$[$0-1] + "']" + $$[$0].join('');;
+break;
+case 22:
+this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 23:
+this.$ = "['" + $$[$0] + "']";
+break;
+case 24: case 36:
+this.$ = $$[$0-2] + ($$[$0-1] || '') + $$[$0];
+break;
+case 25:
+ this.$ = $$[$0]; this.$[$$[$0-4]] = $$[$0-2];
+break;
+case 26:
+ this.$ = {}; this.$[$$[$0-2]] = $$[$0];
+break;
+case 29:
+this.$ = $$[$0].join('');
+break;
+case 37: case 39: case 45:
+this.$ = [];
+break;
+case 38: case 40: case 46: case 50:
+$$[$0-1].push($$[$0]);
+break;
+case 49:
+this.$ = [$$[$0]];
+break;
+}
+},
+table: [o([5,9,11,12,13,19,21,23,26,28,30,31,32,33],$V0,{3:1,4:2,6:3}),{1:[3]},{5:[1,4]},o([5,18,22,25,27,35,37,39],[2,2],{7:5,8:6,10:8,9:[1,7],11:[1,9],12:[1,10],13:[1,11],19:[1,12],21:[1,13],23:[1,14],26:[1,15],28:[1,16],30:[1,17],31:[1,18],32:[1,19],33:[1,20]}),{1:[2,1]},o($V1,[2,38]),o($V1,[2,3]),o($V1,[2,4]),o($V1,[2,5]),o($V1,[2,6]),o($V1,[2,7]),{11:$V2,12:$V3,14:21,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},{20:32,41:$V9},{20:34,41:$V9},{20:35,41:$V9},o([9,11,12,13,19,21,23,26,27,28,30,31,32,33],$V0,{6:3,4:36}),{29:37,40:$V4},{29:38,40:$V4},{29:39,40:$V4},o($V1,[2,16]),o($V1,[2,17]),{15:[1,40]},o([15,45,49],[2,29],{29:28,54:41,11:$V2,12:$V3,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8}),o($Va,[2,49]),o($Va,[2,30]),o($Va,[2,31]),o($Va,[2,32]),o($Va,[2,33]),o($Va,[2,34]),o($Va,[2,35]),{11:$V2,12:$V3,14:42,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},{41:[1,43]},{15:[1,44]},{50:[1,45]},{15:[1,46]},{15:[1,47]},{27:[1,48]},{15:[1,49]},{15:[1,50]},{15:[1,51]},o($Vb,$V0,{6:3,4:52}),o($Va,[2,50]),{49:[1,53]},o($Vc,[2,45],{42:54}),o($V1,[2,9]),{29:57,40:$V4,51:55,52:[1,56]},o([9,11,12,13,19,21,22,23,26,28,30,31,32,33],$V0,{6:3,4:58}),o([9,11,12,13,19,21,23,25,26,28,30,31,32,33,39],$V0,{6:3,4:59}),o($V1,[2,12]),o($V1,[2,13]),o($V1,[2,14]),o($V1,[2,15]),o($Vd,[2,39],{16:60}),o($Va,[2,36]),o([11,12,15,40,41,45,49,50,52],[2,21],{43:61,44:[1,62],46:[1,63],47:[1,64]}),{12:[1,65],15:[2,26]},o($Ve,[2,27]),o($Ve,[2,28]),{22:[1,66]},{24:67,25:[2,43],38:68,39:[1,69]},{17:70,18:[2,41],34:72,35:[1,74],36:71,37:[1,73]},o($Vc,[2,46]),{11:$V2,12:$V3,14:75,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},{41:[1,76]},{11:$V2,12:$V3,14:78,29:28,40:$V4,41:$V5,47:$V6,48:77,49:[2,47],50:$V7,52:$V8,53:22,54:23},{20:79,41:$V9},o($V1,[2,10]),{25:[1,80]},{25:[2,44]},o([9,11,12,13,19,21,23,25,26,28,30,31,32,33],$V0,{6:3,4:81}),{18:[1,82]},o($Vd,[2,40]),{18:[2,42]},{11:$V2,12:$V3,14:83,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},o([9,11,12,13,18,19,21,23,26,28,30,31,32,33],$V0,{6:3,4:84}),{45:[1,85]},o($Vc,[2,23]),{49:[1,86]},{49:[2,48]},{15:[2,25]},o($V1,[2,11]),{25:[2,20]},o($V1,[2,8]),{15:[1,87]},{18:[2,18]},o($Vc,[2,22]),o($Vc,[2,24]),o($Vb,$V0,{6:3,4:88}),o($Vd,[2,19])],
+defaultActions: {4:[2,1],68:[2,44],72:[2,42],78:[2,48],79:[2,25],81:[2,20],84:[2,18]},
+parseError: function parseError(str, hash) {
+ if (hash.recoverable) {
+ this.trace(str);
+ } else {
+ function _parseError (msg, hash) {
+ this.message = msg;
+ this.hash = hash;
+ }
+ _parseError.prototype = Error;
+
+ throw new _parseError(str, hash);
+ }
+},
+parse: function parse(input) {
+ var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+ var args = lstack.slice.call(arguments, 1);
+ var lexer = Object.create(this.lexer);
+ var sharedState = { yy: {} };
+ for (var k in this.yy) {
+ if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+ sharedState.yy[k] = this.yy[k];
+ }
+ }
+ lexer.setInput(input, sharedState.yy);
+ sharedState.yy.lexer = lexer;
+ sharedState.yy.parser = this;
+ if (typeof lexer.yylloc == 'undefined') {
+ lexer.yylloc = {};
+ }
+ var yyloc = lexer.yylloc;
+ lstack.push(yyloc);
+ var ranges = lexer.options && lexer.options.ranges;
+ if (typeof sharedState.yy.parseError === 'function') {
+ this.parseError = sharedState.yy.parseError;
+ } else {
+ this.parseError = Object.getPrototypeOf(this).parseError;
+ }
+ function popStack(n) {
+ stack.length = stack.length - 2 * n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+ _token_stack:
+ var lex = function () {
+ var token;
+ token = lexer.lex() || EOF;
+ if (typeof token !== 'number') {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ };
+ var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+ while (true) {
+ state = stack[stack.length - 1];
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol === null || typeof symbol == 'undefined') {
+ symbol = lex();
+ }
+ action = table[state] && table[state][symbol];
+ }
+ if (typeof action === 'undefined' || !action.length || !action[0]) {
+ var errStr = '';
+ expected = [];
+ for (p in table[state]) {
+ if (this.terminals_[p] && p > TERROR) {
+ expected.push('\'' + this.terminals_[p] + '\'');
+ }
+ }
+ if (lexer.showPosition) {
+ errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+ } else {
+ errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+ }
+ this.parseError(errStr, {
+ text: lexer.match,
+ token: this.terminals_[symbol] || symbol,
+ line: lexer.yylineno,
+ loc: yyloc,
+ expected: expected
+ });
+ }
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+ }
+ switch (action[0]) {
+ case 1:
+ stack.push(symbol);
+ vstack.push(lexer.yytext);
+ lstack.push(lexer.yylloc);
+ stack.push(action[1]);
+ symbol = null;
+ if (!preErrorSymbol) {
+ yyleng = lexer.yyleng;
+ yytext = lexer.yytext;
+ yylineno = lexer.yylineno;
+ yyloc = lexer.yylloc;
+ if (recovering > 0) {
+ recovering--;
+ }
+ } else {
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+ case 2:
+ len = this.productions_[action[1]][1];
+ yyval.$ = vstack[vstack.length - len];
+ yyval._$ = {
+ first_line: lstack[lstack.length - (len || 1)].first_line,
+ last_line: lstack[lstack.length - 1].last_line,
+ first_column: lstack[lstack.length - (len || 1)].first_column,
+ last_column: lstack[lstack.length - 1].last_column
+ };
+ if (ranges) {
+ yyval._$.range = [
+ lstack[lstack.length - (len || 1)].range[0],
+ lstack[lstack.length - 1].range[1]
+ ];
+ }
+ r = this.performAction.apply(yyval, [
+ yytext,
+ yyleng,
+ yylineno,
+ sharedState.yy,
+ action[1],
+ vstack,
+ lstack
+ ].concat(args));
+ if (typeof r !== 'undefined') {
+ return r;
+ }
+ if (len) {
+ stack = stack.slice(0, -1 * len * 2);
+ vstack = vstack.slice(0, -1 * len);
+ lstack = lstack.slice(0, -1 * len);
+ }
+ stack.push(this.productions_[action[1]][0]);
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+ stack.push(newState);
+ break;
+ case 3:
+ return true;
+ }
+ }
+ return true;
+}};
+
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+ if (this.yy.parser) {
+ this.yy.parser.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+ this.yy = yy || this.yy || {};
+ this._input = input;
+ this._more = this._backtrack = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = '';
+ this.conditionStack = ['INITIAL'];
+ this.yylloc = {
+ first_line: 1,
+ first_column: 0,
+ last_line: 1,
+ last_column: 0
+ };
+ if (this.options.ranges) {
+ this.yylloc.range = [0,0];
+ }
+ this.offset = 0;
+ return this;
+ },
+
+// consumes and returns one char from the input
+input:function () {
+ var ch = this._input[0];
+ this.yytext += ch;
+ this.yyleng++;
+ this.offset++;
+ this.match += ch;
+ this.matched += ch;
+ var lines = ch.match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno++;
+ this.yylloc.last_line++;
+ } else {
+ this.yylloc.last_column++;
+ }
+ if (this.options.ranges) {
+ this.yylloc.range[1]++;
+ }
+
+ this._input = this._input.slice(1);
+ return ch;
+ },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+ var len = ch.length;
+ var lines = ch.split(/(?:\r\n?|\n)/g);
+
+ this._input = ch + this._input;
+ this.yytext = this.yytext.substr(0, this.yytext.length - len);
+ //this.yyleng -= len;
+ this.offset -= len;
+ var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+ this.match = this.match.substr(0, this.match.length - 1);
+ this.matched = this.matched.substr(0, this.matched.length - 1);
+
+ if (lines.length - 1) {
+ this.yylineno -= lines.length - 1;
+ }
+ var r = this.yylloc.range;
+
+ this.yylloc = {
+ first_line: this.yylloc.first_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.first_column,
+ last_column: lines ?
+ (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+ + oldLines[oldLines.length - lines.length].length - lines[0].length :
+ this.yylloc.first_column - len
+ };
+
+ if (this.options.ranges) {
+ this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+ }
+ this.yyleng = this.yytext.length;
+ return this;
+ },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+ this._more = true;
+ return this;
+ },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+ if (this.options.backtrack_lexer) {
+ this._backtrack = true;
+ } else {
+ return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+ text: "",
+ token: null,
+ line: this.yylineno
+ });
+
+ }
+ return this;
+ },
+
+// retain first n characters of the match
+less:function (n) {
+ this.unput(this.match.slice(n));
+ },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+ },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20-next.length);
+ }
+ return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+ },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c + "^";
+ },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+ var token,
+ lines,
+ backup;
+
+ if (this.options.backtrack_lexer) {
+ // save context
+ backup = {
+ yylineno: this.yylineno,
+ yylloc: {
+ first_line: this.yylloc.first_line,
+ last_line: this.last_line,
+ first_column: this.yylloc.first_column,
+ last_column: this.yylloc.last_column
+ },
+ yytext: this.yytext,
+ match: this.match,
+ matches: this.matches,
+ matched: this.matched,
+ yyleng: this.yyleng,
+ offset: this.offset,
+ _more: this._more,
+ _input: this._input,
+ yy: this.yy,
+ conditionStack: this.conditionStack.slice(0),
+ done: this.done
+ };
+ if (this.options.ranges) {
+ backup.yylloc.range = this.yylloc.range.slice(0);
+ }
+ }
+
+ lines = match[0].match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno += lines.length;
+ }
+ this.yylloc = {
+ first_line: this.yylloc.last_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ?
+ lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+ this.yylloc.last_column + match[0].length
+ };
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ if (this.options.ranges) {
+ this.yylloc.range = [this.offset, this.offset += this.yyleng];
+ }
+ this._more = false;
+ this._backtrack = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+ if (this.done && this._input) {
+ this.done = false;
+ }
+ if (token) {
+ return token;
+ } else if (this._backtrack) {
+ // recover context
+ for (var k in backup) {
+ this[k] = backup[k];
+ }
+ return false; // rule action called reject() implying the next rule should be tested instead.
+ }
+ return false;
+ },
+
+// return next match in input
+next:function () {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) {
+ this.done = true;
+ }
+
+ var token,
+ match,
+ tempMatch,
+ index;
+ if (!this._more) {
+ this.yytext = '';
+ this.match = '';
+ }
+ var rules = this._currentRules();
+ for (var i = 0; i < rules.length; i++) {
+ tempMatch = this._input.match(this.rules[rules[i]]);
+ if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+ match = tempMatch;
+ index = i;
+ if (this.options.backtrack_lexer) {
+ token = this.test_match(tempMatch, rules[i]);
+ if (token !== false) {
+ return token;
+ } else if (this._backtrack) {
+ match = false;
+ continue; // rule action called reject() implying a rule MISmatch.
+ } else {
+ // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+ return false;
+ }
+ } else if (!this.options.flex) {
+ break;
+ }
+ }
+ }
+ if (match) {
+ token = this.test_match(match, rules[index]);
+ if (token !== false) {
+ return token;
+ }
+ // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+ return false;
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+ text: "",
+ token: null,
+ line: this.yylineno
+ });
+ }
+ },
+
+// return next match that has a token
+lex:function lex() {
+ var r = this.next();
+ if (r) {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+ var n = this.conditionStack.length - 1;
+ if (n > 0) {
+ return this.conditionStack.pop();
+ } else {
+ return this.conditionStack[0];
+ }
+ },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+ if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+ return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+ } else {
+ return this.conditions["INITIAL"].rules;
+ }
+ },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+ n = this.conditionStack.length - 1 - Math.abs(n || 0);
+ if (n >= 0) {
+ return this.conditionStack[n];
+ } else {
+ return "INITIAL";
+ }
+ },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+ this.begin(condition);
+ },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+ return this.conditionStack.length;
+ },
+options: {},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:/* comment */
+break;
+case 1: yy_.yytext = yy_.yytext.substring(9, yy_.yytext.length - 10); return 9;
+break;
+case 2:return 52;
+break;
+case 3:return 52;
+break;
+case 4:return 40;
+break;
+case 5: return 41;
+break;
+case 6:return 46;
+break;
+case 7:return 44;
+break;
+case 8:return 45;
+break;
+case 9:return 47;
+break;
+case 10:return 49;
+break;
+case 11:return 50;
+break;
+case 12:return 32;
+break;
+case 13:return 33;
+break;
+case 14: this.begin('command'); return 30;
+break;
+case 15: this.begin('command'); return 31;
+break;
+case 16: this.begin('command'); return 13;
+break;
+case 17: this.begin('command'); return 37;
+break;
+case 18: this.begin('command'); return 37;
+break;
+case 19:return 35;
+break;
+case 20:return 18;
+break;
+case 21:return 26;
+break;
+case 22:return 27;
+break;
+case 23: this.begin('command'); return 19;
+break;
+case 24: this.begin('command'); return 21;
+break;
+case 25:return 22;
+break;
+case 26: this.begin('command'); return 23;
+break;
+case 27:return 39;
+break;
+case 28:return 25;
+break;
+case 29: this.begin('command'); return 28;
+break;
+case 30: this.popState(); return 15;
+break;
+case 31:return 12;
+break;
+case 32:return 5;
+break;
+case 33:return 11;
+break;
+}
+},
+rules: [/^(?:\{\*[\s\S]*?\*\})/,/^(?:\{literal\}[\s\S]*?\{\/literal\})/,/^(?:"([^"]|\\\.)*")/,/^(?:'([^']|\\\.)*')/,/^(?:\$)/,/^(?:[_a-zA-Z][_a-zA-Z0-9]*)/,/^(?:\.)/,/^(?:\[)/,/^(?:\])/,/^(?:\()/,/^(?:\))/,/^(?:=)/,/^(?:\{ldelim\})/,/^(?:\{rdelim\})/,/^(?:\{#)/,/^(?:\{@)/,/^(?:\{if )/,/^(?:\{else if )/,/^(?:\{elseif )/,/^(?:\{else\})/,/^(?:\{\/if\})/,/^(?:\{lang\})/,/^(?:\{\/lang\})/,/^(?:\{include )/,/^(?:\{implode )/,/^(?:\{\/implode\})/,/^(?:\{foreach )/,/^(?:\{foreachelse\})/,/^(?:\{\/foreach\})/,/^(?:\{(?!\s))/,/^(?:\})/,/^(?:\s+)/,/^(?:$)/,/^(?:[^{])/],
+conditions: {"command":{"rules":[0,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],"inclusive":true},"INITIAL":{"rules":[0,1,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+return parser;
+});
+/**
+ * Provides helper functions for Number handling.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/NumberUtil
+ */
+define('WoltLabSuite/Core/NumberUtil',[], function() {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/NumberUtil
+ */
+ var NumberUtil = {
+ /**
+ * Decimal adjustment of a number.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
+ * @param {Number} value The number.
+ * @param {Integer} exp The exponent (the 10 logarithm of the adjustment base).
+ * @returns {Number} The adjusted value.
+ */
+ round: function (value, exp) {
+ // If the exp is undefined or zero...
+ if (typeof exp === 'undefined' || +exp === 0) {
+ return Math.round(value);
+ }
+ value = +value;
+ exp = +exp;
+
+ // If the value is not a number or the exp is not an integer...
+ if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
+ return NaN;
+ }
+
+ // Shift
+ value = value.toString().split('e');
+ value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
+
+ // Shift back
+ value = value.toString().split('e');
+ return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
+ }
+ };
+
+ return NumberUtil;
+});
+
+/**
+ * Provides helper functions for String handling.
+ *
+ * @author Tim Duesterhus, Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/StringUtil
+ */
+define('WoltLabSuite/Core/StringUtil',['Language', './NumberUtil'], function(Language, NumberUtil) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/StringUtil
+ */
+ return {
+ /**
+ * Adds thousands separators to a given number.
+ *
+ * @see http://stackoverflow.com/a/6502556/782822
+ * @param {?} number
+ * @return {String}
+ */
+ addThousandsSeparator: function(number) {
+ // Fetch Language, as it cannot be provided because of a circular dependency
+ if (Language === undefined) Language = require('Language');
+
+ return String(number).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g, '$1' + Language.get('wcf.global.thousandsSeparator'));
+ },
+
+ /**
+ * Escapes special HTML-characters within a string
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ escapeHTML: function (string) {
+ return String(string).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
+ },
+
+ /**
+ * Escapes a String to work with RegExp.
+ *
+ * @see https://github.com/sstephenson/prototype/blob/master/src/prototype/lang/regexp.js#L25
+ * @param {?} string
+ * @return {String}
+ */
+ escapeRegExp: function(string) {
+ return String(string).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ /**
+ * Rounds number to given count of floating point digits, localizes decimal-point and inserts thousands separators.
+ *
+ * @param {?} number
+ * @param {int} decimalPlaces The number of decimal places to leave after rounding.
+ * @return {String}
+ */
+ formatNumeric: function(number, decimalPlaces) {
+ // Fetch Language, as it cannot be provided because of a circular dependency
+ if (Language === undefined) Language = require('Language');
+
+ number = String(NumberUtil.round(number, decimalPlaces || -2));
+ var numberParts = number.split('.');
+
+ number = this.addThousandsSeparator(numberParts[0]);
+ if (numberParts.length > 1) number += Language.get('wcf.global.decimalPoint') + numberParts[1];
+
+ number = number.replace('-', '\u2212');
+
+ return number;
+ },
+
+ /**
+ * Makes a string's first character lowercase.
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ lcfirst: function(string) {
+ return String(string).substring(0, 1).toLowerCase() + string.substring(1);
+ },
+
+ /**
+ * Makes a string's first character uppercase.
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ ucfirst: function(string) {
+ return String(string).substring(0, 1).toUpperCase() + string.substring(1);
+ },
+
+ /**
+ * Unescapes special HTML-characters within a string.
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ unescapeHTML: function(string) {
+ return String(string).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
+ },
+
+ /**
+ * Shortens numbers larger than 1000 by using unit suffixes.
+ *
+ * @param {?} number
+ * @return {String}
+ */
+ shortUnit: function(number) {
+ var unitSuffix = '';
+
+ if (number >= 1000000) {
+ number /= 1000000;
+
+ if (number > 10) {
+ number = Math.floor(number);
+ }
+ else {
+ number = NumberUtil.round(number, -1);
+ }
+
+ unitSuffix = 'M';
+ }
+ else if (number >= 1000) {
+ number /= 1000;
+
+ if (number > 10) {
+ number = Math.floor(number);
+ }
+ else {
+ number = NumberUtil.round(number, -1);
+ }
+
+ unitSuffix = 'k';
+ }
+
+ return this.formatNumeric(number) + unitSuffix;
+ }
+ };
+});
+
+/**
+ * WoltLabSuite/Core/Template provides a template scripting compiler similar
+ * to the PHP one of WoltLab Suite Core. It supports a limited
+ * set of useful commands and compiles templates down to a pure
+ * JavaScript Function.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Template
+ */
+define('WoltLabSuite/Core/Template',['./Template.grammar', './StringUtil', 'Language'], function(parser, StringUtil, Language) {
+ "use strict";
+
+ // work around bug in AMD module generation of Jison
+ function Parser() {
+ this.yy = {};
+ }
+ Parser.prototype = parser;
+ parser.Parser = Parser;
+ parser = new Parser();
+
+ /**
+ * Compiles the given template.
+ *
+ * @param {string} template Template to compile.
+ * @constructor
+ */
+ function Template(template) {
+ // Fetch Language/StringUtil, as it cannot be provided because of a circular dependency
+ if (Language === undefined) Language = require('Language');
+ if (StringUtil === undefined) StringUtil = require('StringUtil');
+
+ try {
+ template = parser.parse(template);
+ template = "var tmp = {};\n"
+ + "for (var key in v) tmp[key] = v[key];\n"
+ + "v = tmp;\n"
+ + "v.__wcf = window.WCF; v.__window = window;\n"
+ + "return " + template;
+
+ this.fetch = new Function("StringUtil", "Language", "v", template).bind(undefined, StringUtil, Language);
+ }
+ catch (e) {
+ console.debug(e.message);
+ throw e;
+ }
+ }
+
+ Object.defineProperty(Template, 'callbacks', {
+ enumerable: false,
+ configurable: false,
+ get: function() {
+ throw new Error('WCF.Template.callbacks is no longer supported');
+ },
+ set: function(value) {
+ throw new Error('WCF.Template.callbacks is no longer supported');
+ }
+ });
+
+ Template.prototype = {
+ /**
+ * Evaluates the Template using the given parameters.
+ *
+ * @param {object} v Parameters to pass to the template.
+ */
+ fetch: function(v) {
+ // this will be replaced in the init function
+ throw new Error('This Template is not initialized.');
+ }
+ };
+
+ return Template;
+});
+
+/**
+ * Manages language items.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language
+ */
+define('WoltLabSuite/Core/Language',['Dictionary', './Template'], function(Dictionary, Template) {
+ "use strict";
+
+ var _languageItems = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Language
+ */
+ var Language = {
+ /**
+ * Adds all the language items in the given object to the store.
+ *
+ * @param {Object.<string, string>} object
+ */
+ addObject: function(object) {
+ _languageItems.merge(Dictionary.fromObject(object));
+ },
+
+ /**
+ * Adds a single language item to the store.
+ *
+ * @param {string} key
+ * @param {string} value
+ */
+ add: function(key, value) {
+ _languageItems.set(key, value);
+ },
+
+ /**
+ * Fetches the language item specified by the given key.
+ * If the language item is a string it will be evaluated as
+ * WoltLabSuite/Core/Template with the given parameters.
+ *
+ * @param {string} key Language item to return.
+ * @param {Object=} parameters Parameters to provide to WoltLabSuite/Core/Template.
+ * @return {string}
+ */
+ get: function(key, parameters) {
+ if (!parameters) parameters = { };
+
+ var value = _languageItems.get(key);
+
+ if (value === undefined) {
+ return key;
+ }
+
+ // fetch Template, as it cannot be provided because of a circular dependency
+ if (Template === undefined) Template = require('WoltLabSuite/Core/Template');
+
+ if (typeof value === 'string') {
+ // lazily convert to WCF.Template
+ try {
+ _languageItems.set(key, new Template(value));
+ }
+ catch (e) {
+ _languageItems.set(key, new Template('{literal}' + value.replace(/\{\/literal\}/g, '{/literal}{ldelim}/literal}{literal}') + '{/literal}'));
+ }
+ value = _languageItems.get(key);
+ }
+
+ if (value instanceof Template) {
+ value = value.fetch(parameters);
+ }
+
+ return value;
+ }
+ };
+
+ return Language;
+});
+
+/**
+ * Simple API to store and invoke multiple callbacks per identifier.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/CallbackList
+ */
+define('WoltLabSuite/Core/CallbackList',['Dictionary'], function(Dictionary) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function CallbackList() {
+ this._dictionary = new Dictionary();
+ }
+ CallbackList.prototype = {
+ /**
+ * Adds a callback for given identifier.
+ *
+ * @param {string} identifier arbitrary string to group and identify callbacks
+ * @param {function} callback callback function
+ */
+ add: function(identifier, callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback as second argument for identifier '" + identifier + "'.");
+ }
+
+ if (!this._dictionary.has(identifier)) {
+ this._dictionary.set(identifier, []);
+ }
+
+ this._dictionary.get(identifier).push(callback);
+ },
+
+ /**
+ * Removes all callbacks registered for given identifier
+ *
+ * @param {string} identifier arbitrary string to group and identify callbacks
+ */
+ remove: function(identifier) {
+ this._dictionary['delete'](identifier);
+ },
+
+ /**
+ * Invokes callback function on each registered callback.
+ *
+ * @param {string|null} identifier arbitrary string to group and identify callbacks.
+ * null is a wildcard to match every identifier
+ * @param {function(function)} callback function called with the individual callback as parameter
+ */
+ forEach: function(identifier, callback) {
+ if (identifier === null) {
+ this._dictionary.forEach(function(callbacks, identifier) {
+ callbacks.forEach(callback);
+ });
+ }
+ else {
+ var callbacks = this._dictionary.get(identifier);
+ if (callbacks !== undefined) {
+ callbacks.forEach(callback);
+ }
+ }
+ }
+ };
+
+ return CallbackList;
+});
+
+/**
+ * Allows to be informed when the DOM may have changed and
+ * new elements that are relevant to you may have been added.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dom/Change/Listener
+ */
+define('WoltLabSuite/Core/Dom/Change/Listener',['CallbackList'], function(CallbackList) {
+ "use strict";
+
+ var _callbackList = new CallbackList();
+ var _hot = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Dom/Change/Listener
+ */
+ return {
+ /**
+ * @see WoltLabSuite/Core/CallbackList#add
+ */
+ add: _callbackList.add.bind(_callbackList),
+
+ /**
+ * @see WoltLabSuite/Core/CallbackList#remove
+ */
+ remove: _callbackList.remove.bind(_callbackList),
+
+ /**
+ * Triggers the execution of all the listeners.
+ * Use this function when you added new elements to the DOM that might
+ * be relevant to others.
+ * While this function is in progress further calls to it will be ignored.
+ */
+ trigger: function() {
+ if (_hot) return;
+
+ try {
+ _hot = true;
+ _callbackList.forEach(null, function(callback) {
+ callback();
+ });
+ }
+ finally {
+ _hot = false;
+ }
+ }
+ };
+});
+
+/**
+ * Provides basic details on the JavaScript environment.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Environment
+ */
+define('WoltLabSuite/Core/Environment',[], function() {
+ "use strict";
+
+ var _browser = 'other';
+ var _editor = 'none';
+ var _platform = 'desktop';
+ var _touch = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Environment
+ */
+ return {
+ /**
+ * Determines environment variables.
+ */
+ setup: function() {
+ if (typeof window.chrome === 'object') {
+ // this detects Opera as well, we could check for window.opr if we need to
+ _browser = 'chrome';
+ }
+ else {
+ var styles = window.getComputedStyle(document.documentElement);
+ for (var i = 0, length = styles.length; i < length; i++) {
+ var property = styles[i];
+
+ if (property.indexOf('-ms-') === 0) {
+ // it is tempting to use 'msie', but it wouldn't really represent 'Edge'
+ _browser = 'microsoft';
+ }
+ else if (property.indexOf('-moz-') === 0) {
+ _browser = 'firefox';
+ }
+ else if (_browser !== 'firefox' && property.indexOf('-webkit-') === 0) {
+ _browser = 'safari';
+ }
+ }
+ }
+
+ var ua = window.navigator.userAgent.toLowerCase();
+ if (ua.indexOf('crios') !== -1) {
+ _browser = 'chrome';
+ _platform = 'ios';
+ }
+ else if (/(?:iphone|ipad|ipod)/.test(ua)) {
+ _browser = 'safari';
+ _platform = 'ios';
+ }
+ else if (ua.indexOf('android') !== -1) {
+ _platform = 'android';
+ }
+ else if (ua.indexOf('iemobile') !== -1) {
+ _browser = 'microsoft';
+ _platform = 'windows';
+ }
+
+ if (_platform === 'desktop' && (ua.indexOf('mobile') !== -1 || ua.indexOf('tablet') !== -1)) {
+ _platform = 'mobile';
+ }
+
+ _editor = 'redactor';
+ _touch = (!!('ontouchstart' in window) || (!!('msMaxTouchPoints' in window.navigator) && window.navigator.msMaxTouchPoints > 0) || window.DocumentTouch && document instanceof DocumentTouch);
+ },
+
+ /**
+ * Returns the lower-case browser identifier.
+ *
+ * Possible values:
+ * - chrome: Chrome and Opera
+ * - firefox
+ * - microsoft: Internet Explorer and Microsoft Edge
+ * - safari
+ *
+ * @return {string} browser identifier
+ */
+ browser: function() {
+ return _browser;
+ },
+
+ /**
+ * Returns the available editor's name or an empty string.
+ *
+ * @return {string} editor name
+ */
+ editor: function() {
+ return _editor;
+ },
+
+ /**
+ * Returns the browser platform.
+ *
+ * Possible values:
+ * - desktop
+ * - android
+ * - ios: iPhone, iPad and iPod
+ * - windows: Windows on phones/tablets
+ *
+ * @return {string} browser platform
+ */
+ platform: function() {
+ return _platform;
+ },
+
+ /**
+ * Returns true if browser is potentially used with a touchscreen.
+ *
+ * Warning: Detecting touch is unreliable and should be avoided at all cost.
+ *
+ * @deprecated 3.0 - exists for backward-compatibility only, will be removed in the future
+ *
+ * @return {boolean} true if a touchscreen is present
+ */
+ touch: function() {
+ return _touch;
+ }
+ };
+});
+
+/**
+ * Provides helper functions to work with DOM nodes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dom/Util
+ */
+define('WoltLabSuite/Core/Dom/Util',['Environment', 'StringUtil'], function(Environment, StringUtil) {
+ "use strict";
+
+ function _isBoundaryNode(element, ancestor, position) {
+ if (!ancestor.contains(element)) {
+ throw new Error("Ancestor element does not contain target element.");
+ }
+
+ var node, whichSibling = position + 'Sibling';
+ while (element !== null && element !== ancestor) {
+ if (element[position + 'ElementSibling'] !== null) {
+ return false;
+ }
+ else if (element[whichSibling]) {
+ node = element[whichSibling];
+ while (node) {
+ if (node.textContent.trim() !== '') {
+ return false;
+ }
+
+ node = node[whichSibling];
+ }
+ }
+
+ element = element.parentNode;
+ }
+
+ return true;
+ }
+
+ var _idCounter = 0;
+
+ /**
+ * @exports WoltLabSuite/Core/Dom/Util
+ */
+ var DomUtil = {
+ /**
+ * Returns a DocumentFragment containing the provided HTML string as DOM nodes.
+ *
+ * @param {string} html HTML string
+ * @return {DocumentFragment} fragment containing DOM nodes
+ */
+ createFragmentFromHtml: function(html) {
+ var tmp = elCreate('div');
+ this.setInnerHtml(tmp, html);
+
+ var fragment = document.createDocumentFragment();
+ while (tmp.childNodes.length) {
+ fragment.appendChild(tmp.childNodes[0]);
+ }
+
+ return fragment;
+ },
+
+ /**
+ * Returns a unique element id.
+ *
+ * @return {string} unique id
+ */
+ getUniqueId: function() {
+ var elementId;
+
+ do {
+ elementId = 'wcf' + _idCounter++;
+ }
+ while (elById(elementId) !== null);
+
+ return elementId;
+ },
+
+ /**
+ * Returns the element's id. If there is no id set, a unique id will be
+ * created and assigned.
+ *
+ * @param {Element} el element
+ * @return {string} element id
+ */
+ identify: function(el) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element as argument.");
+ }
+
+ var id = elAttr(el, 'id');
+ if (!id) {
+ id = this.getUniqueId();
+ elAttr(el, 'id', id);
+ }
+
+ return id;
+ },
+
+ /**
+ * Returns the outer height of an element including margins.
+ *
+ * @param {Element} el element
+ * @param {CSSStyleDeclaration=} styles result of window.getComputedStyle()
+ * @return {int} outer height in px
+ */
+ outerHeight: function(el, styles) {
+ styles = styles || window.getComputedStyle(el);
+
+ var height = el.offsetHeight;
+ height += ~~styles.marginTop + ~~styles.marginBottom;
+
+ return height;
+ },
+
+ /**
+ * Returns the outer width of an element including margins.
+ *
+ * @param {Element} el element
+ * @param {CSSStyleDeclaration=} styles result of window.getComputedStyle()
+ * @return {int} outer width in px
+ */
+ outerWidth: function(el, styles) {
+ styles = styles || window.getComputedStyle(el);
+
+ var width = el.offsetWidth;
+ width += ~~styles.marginLeft + ~~styles.marginRight;
+
+ return width;
+ },
+
+ /**
+ * Returns the outer dimensions of an element including margins.
+ *
+ * @param {Element} el element
+ * @return {{height: int, width: int}} dimensions in px
+ */
+ outerDimensions: function(el) {
+ var styles = window.getComputedStyle(el);
+
+ return {
+ height: this.outerHeight(el, styles),
+ width: this.outerWidth(el, styles)
+ };
+ },
+
+ /**
+ * Returns the element's offset relative to the document's top left corner.
+ *
+ * @param {Element} el element
+ * @return {{left: int, top: int}} offset relative to top left corner
+ */
+ offset: function(el) {
+ var rect = el.getBoundingClientRect();
+
+ return {
+ top: Math.round(rect.top + (window.scrollY || window.pageYOffset)),
+ left: Math.round(rect.left + (window.scrollX || window.pageXOffset))
+ };
+ },
+
+ /**
+ * Prepends an element to a parent element.
+ *
+ * @param {Element} el element to prepend
+ * @param {Element} parentEl future containing element
+ */
+ prepend: function(el, parentEl) {
+ if (parentEl.childNodes.length === 0) {
+ parentEl.appendChild(el);
+ }
+ else {
+ parentEl.insertBefore(el, parentEl.childNodes[0]);
+ }
+ },
+
+ /**
+ * Inserts an element after an existing element.
+ *
+ * @param {Element} newEl element to insert
+ * @param {Element} el reference element
+ */
+ insertAfter: function(newEl, el) {
+ if (el.nextSibling !== null) {
+ el.parentNode.insertBefore(newEl, el.nextSibling);
+ }
+ else {
+ el.parentNode.appendChild(newEl);
+ }
+ },
+
+ /**
+ * Applies a list of CSS properties to an element.
+ *
+ * @param {Element} el element
+ * @param {Object<string, *>} styles list of CSS styles
+ */
+ setStyles: function(el, styles) {
+ var important = false;
+ for (var property in styles) {
+ if (styles.hasOwnProperty(property)) {
+ if (/ !important$/.test(styles[property])) {
+ important = true;
+
+ styles[property] = styles[property].replace(/ !important$/, '');
+ }
+ else {
+ important = false;
+ }
+
+ // for a set style property with priority = important, some browsers are
+ // not able to overwrite it with a property != important; removing the
+ // property first solves this issue
+ if (el.style.getPropertyPriority(property) === 'important' && !important) {
+ el.style.removeProperty(property);
+ }
+
+ el.style.setProperty(property, styles[property], (important ? 'important' : ''));
+ }
+ }
+ },
+
+ /**
+ * Returns a style property value as integer.
+ *
+ * The behavior of this method is undefined for properties that are not considered
+ * to have a "numeric" value, e.g. "background-image".
+ *
+ * @param {CSSStyleDeclaration} styles result of window.getComputedStyle()
+ * @param {string} propertyName property name
+ * @return {int} property value as integer
+ */
+ styleAsInt: function(styles, propertyName) {
+ var value = styles.getPropertyValue(propertyName);
+ if (value === null) {
+ return 0;
+ }
+
+ return parseInt(value);
+ },
+
+ /**
+ * Sets the inner HTML of given element and reinjects <script> elements to be properly executed.
+ *
+ * @see http://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0
+ * @param {Element} element target element
+ * @param {string} innerHtml HTML string
+ */
+ setInnerHtml: function(element, innerHtml) {
+ element.innerHTML = innerHtml;
+
+ var newScript, script, scripts = elBySelAll('script', element);
+ for (var i = 0, length = scripts.length; i < length; i++) {
+ script = scripts[i];
+ newScript = elCreate('script');
+ if (script.src) {
+ newScript.src = script.src;
+ }
+ else {
+ newScript.textContent = script.textContent;
+ }
+
+ element.appendChild(newScript);
+ elRemove(script);
+ }
+ },
+
+ /**
+ *
+ * @param html
+ * @param {Element} referenceElement
+ * @param insertMethod
+ */
+ insertHtml: function(html, referenceElement, insertMethod) {
+ var element = elCreate('div');
+ this.setInnerHtml(element, html);
+
+ if (!element.childNodes.length) {
+ return;
+ }
+
+ var node = element.childNodes[0];
+ switch (insertMethod) {
+ case 'append':
+ referenceElement.appendChild(node);
+ break;
+
+ case 'after':
+ this.insertAfter(node, referenceElement);
+ break;
+
+ case 'prepend':
+ this.prepend(node, referenceElement);
+ break;
+
+ case 'before':
+ referenceElement.parentNode.insertBefore(node, referenceElement);
+ break;
+
+ default:
+ throw new Error("Unknown insert method '" + insertMethod + "'.");
+ break;
+ }
+
+ var tmp;
+ while (element.childNodes.length) {
+ tmp = element.childNodes[0];
+
+ this.insertAfter(tmp, node);
+ node = tmp;
+ }
+ },
+
+ /**
+ * Returns true if `element` contains the `child` element.
+ *
+ * @param {Element} element container element
+ * @param {Element} child child element
+ * @returns {boolean} true if `child` is a (in-)direct child of `element`
+ */
+ contains: function(element, child) {
+ while (child !== null) {
+ child = child.parentNode;
+
+ if (element === child) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Retrieves all data attributes from target element, optionally allowing for
+ * a custom prefix that serves two purposes: First it will restrict the results
+ * for items starting with it and second it will remove that prefix.
+ *
+ * @param {Element} element target element
+ * @param {string=} prefix attribute prefix
+ * @param {boolean=} camelCaseName transform attribute names into camel case using dashes as separators
+ * @param {boolean=} idToUpperCase transform '-id' into 'ID'
+ * @returns {object<string, string>} list of data attributes
+ */
+ getDataAttributes: function(element, prefix, camelCaseName, idToUpperCase) {
+ prefix = prefix || '';
+ if (!/^data-/.test(prefix)) prefix = 'data-' + prefix;
+ camelCaseName = (camelCaseName === true);
+ idToUpperCase = (idToUpperCase === true);
+
+ var attribute, attributes = {}, name, tmp;
+ for (var i = 0, length = element.attributes.length; i < length; i++) {
+ attribute = element.attributes[i];
+
+ if (attribute.name.indexOf(prefix) === 0) {
+ name = attribute.name.replace(new RegExp('^' + prefix), '');
+ if (camelCaseName) {
+ tmp = name.split('-');
+ name = '';
+ for (var j = 0, innerLength = tmp.length; j < innerLength; j++) {
+ if (name.length) {
+ if (idToUpperCase && tmp[j] === 'id') {
+ tmp[j] = 'ID';
+ }
+ else {
+ tmp[j] = StringUtil.ucfirst(tmp[j]);
+ }
+ }
+
+ name += tmp[j];
+ }
+ }
+
+ attributes[name] = attribute.value;
+ }
+ }
+
+ return attributes;
+ },
+
+ /**
+ * Unwraps contained nodes by moving them out of `element` while
+ * preserving their previous order. Target element will be removed
+ * at the end of the operation.
+ *
+ * @param {Element} element target element
+ */
+ unwrapChildNodes: function(element) {
+ var parent = element.parentNode;
+ while (element.childNodes.length) {
+ parent.insertBefore(element.childNodes[0], element);
+ }
+
+ elRemove(element);
+ },
+
+ /**
+ * Replaces an element by moving all child nodes into the new element
+ * while preserving their previous order. The old element will be removed
+ * at the end of the operation.
+ *
+ * @param {Element} oldElement old element
+ * @param {Element} newElement old element
+ */
+ replaceElement: function(oldElement, newElement) {
+ while (oldElement.childNodes.length) {
+ newElement.appendChild(oldElement.childNodes[0]);
+ }
+
+ oldElement.parentNode.insertBefore(newElement, oldElement);
+ elRemove(oldElement);
+ },
+
+ /**
+ * Returns true if given element is the most left node of the ancestor, that is
+ * a node without any content nor elements before it or its parent nodes.
+ *
+ * @param {Element} element target element
+ * @param {Element} ancestor ancestor element, must contain the target element
+ * @returns {boolean} true if target element is the most left node
+ */
+ isAtNodeStart: function(element, ancestor) {
+ return _isBoundaryNode(element, ancestor, 'previous');
+ },
+
+ /**
+ * Returns true if given element is the most right node of the ancestor, that is
+ * a node without any content nor elements after it or its parent nodes.
+ *
+ * @param {Element} element target element
+ * @param {Element} ancestor ancestor element, must contain the target element
+ * @returns {boolean} true if target element is the most right node
+ */
+ isAtNodeEnd: function(element, ancestor) {
+ return _isBoundaryNode(element, ancestor, 'next');
+ },
+
+ /**
+ * Returns the first ancestor element with position fixed or null.
+ *
+ * @param {Element} element target element
+ * @returns {(Element|null)} first ancestor with position fixed or null
+ */
+ getFixedParent: function (element) {
+ while (element && element !== document.body) {
+ if (window.getComputedStyle(element).getPropertyValue('position') === 'fixed') {
+ return element;
+ }
+
+ element = element.offsetParent;
+ }
+
+ return null;
+ }
+ };
+
+ // expose on window object for backward compatibility
+ window.bc_wcfDomUtil = DomUtil;
+
+ return DomUtil;
+});
+
+/**
+ * Simple `object` to `object` map using a native WeakMap on supported browsers, otherwise a set of two arrays.
+ *
+ * If you're looking for a dictionary with string keys, please see `WoltLabSuite/Core/Dictionary`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/ObjectMap
+ */
+define('WoltLabSuite/Core/ObjectMap',[], function() {
+ "use strict";
+
+ var _hasMap = objOwns(window, 'WeakMap') && typeof window.WeakMap === 'function';
+
+ /**
+ * @constructor
+ */
+ function ObjectMap() {
+ this._map = (_hasMap) ? new WeakMap() : { key: [], value: [] };
+ }
+ ObjectMap.prototype = {
+ /**
+ * Sets a new key with given value, will overwrite an existing key.
+ *
+ * @param {object} key key
+ * @param {object} value value
+ */
+ set: function(key, value) {
+ if (typeof key !== 'object' || key === null) {
+ throw new TypeError("Only objects can be used as key");
+ }
+
+ if (typeof value !== 'object' || value === null) {
+ throw new TypeError("Only objects can be used as value");
+ }
+
+ if (_hasMap) {
+ this._map.set(key, value);
+ }
+ else {
+ this._map.key.push(key);
+ this._map.value.push(value);
+ }
+ },
+
+ /**
+ * Removes a key from the map.
+ *
+ * @param {object} key key
+ */
+ 'delete': function(key) {
+ if (_hasMap) {
+ this._map['delete'](key);
+ }
+ else {
+ var index = this._map.key.indexOf(key);
+ this._map.key.splice(index);
+ this._map.value.splice(index);
+ }
+ },
+
+ /**
+ * Returns true if dictionary contains a value for given key.
+ *
+ * @param {object} key key
+ * @return {boolean} true if key exists
+ */
+ has: function(key) {
+ if (_hasMap) {
+ return this._map.has(key);
+ }
+ else {
+ return (this._map.key.indexOf(key) !== -1);
+ }
+ },
+
+ /**
+ * Retrieves a value by key, returns undefined if there is no match.
+ *
+ * @param {object} key key
+ * @return {*}
+ */
+ get: function(key) {
+ if (_hasMap) {
+ return this._map.get(key);
+ }
+ else {
+ var index = this._map.key.indexOf(key);
+ if (index !== -1) {
+ return this._map.value[index];
+ }
+
+ return undefined;
+ }
+ }
+ };
+
+ return ObjectMap;
+});
+
+/**
+ * Provides helper functions to traverse the DOM.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dom/Traverse
+ */
+define('WoltLabSuite/Core/Dom/Traverse',[], function() {
+ "use strict";
+
+ /** @const */ var NONE = 0;
+ /** @const */ var SELECTOR = 1;
+ /** @const */ var CLASS_NAME = 2;
+ /** @const */ var TAG_NAME = 3;
+
+ var _probe = [
+ function(el, none) { return true; },
+ function(el, selector) { return el.matches(selector); },
+ function(el, className) { return el.classList.contains(className); },
+ function(el, tagName) { return el.nodeName === tagName; }
+ ];
+
+ var _children = function(el, type, value) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid element as first argument.");
+ }
+
+ var children = [];
+
+ for (var i = 0; i < el.childElementCount; i++) {
+ if (_probe[type](el.children[i], value)) {
+ children.push(el.children[i]);
+ }
+ }
+
+ return children;
+ };
+
+ var _parent = function(el, type, value, untilElement) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid element as first argument.");
+ }
+
+ el = el.parentNode;
+
+ while (el instanceof Element) {
+ if (el === untilElement) {
+ return null;
+ }
+
+ if (_probe[type](el, value)) {
+ return el;
+ }
+
+ el = el.parentNode;
+ }
+
+ return null;
+ };
+
+ var _sibling = function(el, siblingType, type, value) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid element as first argument.");
+ }
+
+ if (el instanceof Element) {
+ if (el[siblingType] !== null && _probe[type](el[siblingType], value)) {
+ return el[siblingType];
+ }
+ }
+
+ return null;
+ };
+
+ /**
+ * @exports WoltLabSuite/Core/Dom/Traverse
+ */
+ return {
+ /**
+ * Examines child elements and returns the first child matching the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match child elements against
+ * @return {(Element|null)} null if there is no child node matching the selector
+ */
+ childBySel: function(el, selector) {
+ return _children(el, SELECTOR, selector)[0] || null;
+ },
+
+ /**
+ * Examines child elements and returns the first child that has the given CSS class set.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {(Element|null)} null if there is no child node with given CSS class
+ */
+ childByClass: function(el, className) {
+ return _children(el, CLASS_NAME, className)[0] || null;
+ },
+
+ /**
+ * Examines child elements and returns the first child which equals the given tag.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {(Element|null)} null if there is no child node which equals given tag
+ */
+ childByTag: function(el, tagName) {
+ return _children(el, TAG_NAME, tagName)[0] || null;
+ },
+
+ /**
+ * Examines child elements and returns all children matching the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match child elements against
+ * @return {array<Element>} list of children matching the selector
+ */
+ childrenBySel: function(el, selector) {
+ return _children(el, SELECTOR, selector);
+ },
+
+ /**
+ * Examines child elements and returns all children that have the given CSS class set.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {array<Element>} list of children with the given class
+ */
+ childrenByClass: function(el, className) {
+ return _children(el, CLASS_NAME, className);
+ },
+
+ /**
+ * Examines child elements and returns all children which equal the given tag.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {array<Element>} list of children equaling the tag name
+ */
+ childrenByTag: function(el, tagName) {
+ return _children(el, TAG_NAME, tagName);
+ },
+
+ /**
+ * Examines parent nodes and returns the first parent that matches the given selector.
+ *
+ * @param {Element} el child element
+ * @param {string} selector CSS selector to match parent nodes against
+ * @param {Element=} untilElement stop when reaching this element
+ * @return {(Element|null)} null if no parent node matched the selector
+ */
+ parentBySel: function(el, selector, untilElement) {
+ return _parent(el, SELECTOR, selector, untilElement);
+ },
+
+ /**
+ * Examines parent nodes and returns the first parent that has the given CSS class set.
+ *
+ * @param {Element} el child element
+ * @param {string} className CSS class name
+ * @param {Element=} untilElement stop when reaching this element
+ * @return {(Element|null)} null if there is no parent node with given class
+ */
+ parentByClass: function(el, className, untilElement) {
+ return _parent(el, CLASS_NAME, className, untilElement);
+ },
+
+ /**
+ * Examines parent nodes and returns the first parent which equals the given tag.
+ *
+ * @param {Element} el child element
+ * @param {string} tagName element tag name
+ * @param {Element=} untilElement stop when reaching this element
+ * @return {(Element|null)} null if there is no parent node of given tag type
+ */
+ parentByTag: function(el, tagName, untilElement) {
+ return _parent(el, TAG_NAME, tagName, untilElement);
+ },
+
+ /**
+ * Returns the next element sibling.
+ *
+ * @param {Element} el element
+ * @return {(Element|null)} null if there is no next sibling element
+ */
+ next: function(el) {
+ return _sibling(el, 'nextElementSibling', NONE, null);
+ },
+
+ /**
+ * Returns the next element sibling that matches the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match parent nodes against
+ * @return {(Element|null)} null if there is no next sibling element or it does not match the selector
+ */
+ nextBySel: function(el, selector) {
+ return _sibling(el, 'nextElementSibling', SELECTOR, selector);
+ },
+
+ /**
+ * Returns the next element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {(Element|null)} null if there is no next sibling element or it does not have the class set
+ */
+ nextByClass: function(el, className) {
+ return _sibling(el, 'nextElementSibling', CLASS_NAME, className);
+ },
+
+ /**
+ * Returns the next element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {(Element|null)} null if there is no next sibling element or it does not have the class set
+ */
+ nextByTag: function(el, tagName) {
+ return _sibling(el, 'nextElementSibling', TAG_NAME, tagName);
+ },
+
+ /**
+ * Returns the previous element sibling.
+ *
+ * @param {Element} el element
+ * @return {(Element|null)} null if there is no previous sibling element
+ */
+ prev: function(el) {
+ return _sibling(el, 'previousElementSibling', NONE, null);
+ },
+
+ /**
+ * Returns the previous element sibling that matches the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match parent nodes against
+ * @return {(Element|null)} null if there is no previous sibling element or it does not match the selector
+ */
+ prevBySel: function(el, selector) {
+ return _sibling(el, 'previousElementSibling', SELECTOR, selector);
+ },
+
+ /**
+ * Returns the previous element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {(Element|null)} null if there is no previous sibling element or it does not have the class set
+ */
+ prevByClass: function(el, className) {
+ return _sibling(el, 'previousElementSibling', CLASS_NAME, className);
+ },
+
+ /**
+ * Returns the previous element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {(Element|null)} null if there is no previous sibling element or it does not have the class set
+ */
+ prevByTag: function(el, tagName) {
+ return _sibling(el, 'previousElementSibling', TAG_NAME, tagName);
+ }
+ };
+});
+
+/**
+ * Provides the confirmation dialog overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Confirmation
+ */
+define('WoltLabSuite/Core/Ui/Confirmation',['Core', 'Language', 'Ui/Dialog'], function(Core, Language, UiDialog) {
+ "use strict";
+
+ var _active = false;
+ var _confirmButton = null;
+ var _content = null;
+ var _options = {};
+ var _text = null;
+
+ /**
+ * Confirmation dialog overlay.
+ *
+ * @exports WoltLabSuite/Core/Ui/Confirmation
+ */
+ return {
+ /**
+ * Shows the confirmation dialog.
+ *
+ * Possible options:
+ * - cancel: callback if user cancels the dialog
+ * - confirm: callback if user confirm the dialog
+ * - legacyCallback: WCF 2.0/2.1 compatible callback with string parameter
+ * - message: displayed confirmation message
+ * - parameters: list of parameters passed to the callback on confirm
+ * - template: optional HTML string to be inserted below the `message`
+ *
+ * @param {object<string, *>} options confirmation options
+ */
+ show: function(options) {
+ if (UiDialog === undefined) UiDialog = require('Ui/Dialog');
+
+ if (_active) {
+ return;
+ }
+
+ _options = Core.extend({
+ cancel: null,
+ confirm: null,
+ legacyCallback: null,
+ message: '',
+ messageIsHtml: false,
+ parameters: {},
+ template: ''
+ }, options);
+
+ _options.message = (typeof _options.message === 'string') ? _options.message.trim() : '';
+ if (!_options.message.length) {
+ throw new Error("Expected a non-empty string for option 'message'.");
+ }
+
+ if (typeof _options.confirm !== 'function' && typeof _options.legacyCallback !== 'function') {
+ throw new TypeError("Expected a valid callback for option 'confirm'.");
+ }
+
+ if (_content === null) {
+ this._createDialog();
+ }
+
+ _content.innerHTML = (typeof _options.template === 'string') ? _options.template.trim() : '';
+ if (_options.messageIsHtml) _text.innerHTML = _options.message;
+ else _text.textContent = _options.message;
+
+ _active = true;
+
+ UiDialog.open(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfSystemConfirmation',
+ options: {
+ onClose: this._onClose.bind(this),
+ onShow: this._onShow.bind(this),
+ title: Language.get('wcf.global.confirmation.title')
+ }
+ };
+ },
+
+ /**
+ * Returns content container element.
+ *
+ * @return {Element} content container element
+ */
+ getContentElement: function() {
+ return _content;
+ },
+
+ /**
+ * Creates the dialog DOM elements.
+ */
+ _createDialog: function() {
+ var dialog = elCreate('div');
+ elAttr(dialog, 'id', 'wcfSystemConfirmation');
+ dialog.classList.add('systemConfirmation');
+
+ _text = elCreate('p');
+ dialog.appendChild(_text);
+
+ _content = elCreate('div');
+ elAttr(_content, 'id', 'wcfSystemConfirmationContent');
+ dialog.appendChild(_content);
+
+ var formSubmit = elCreate('div');
+ formSubmit.classList.add('formSubmit');
+ dialog.appendChild(formSubmit);
+
+ _confirmButton = elCreate('button');
+ _confirmButton.classList.add('buttonPrimary');
+ _confirmButton.textContent = Language.get('wcf.global.confirmation.confirm');
+ _confirmButton.addEventListener(WCF_CLICK_EVENT, this._confirm.bind(this));
+ formSubmit.appendChild(_confirmButton);
+
+ var cancelButton = elCreate('button');
+ cancelButton.textContent = Language.get('wcf.global.confirmation.cancel');
+ cancelButton.addEventListener(WCF_CLICK_EVENT, function() { UiDialog.close('wcfSystemConfirmation'); });
+ formSubmit.appendChild(cancelButton);
+
+ document.body.appendChild(dialog);
+ },
+
+ /**
+ * Invoked if the user confirms the dialog.
+ */
+ _confirm: function() {
+ if (typeof _options.legacyCallback === 'function') {
+ _options.legacyCallback('confirm', _options.parameters, _content);
+ }
+ else {
+ _options.confirm(_options.parameters, _content);
+ }
+
+ _active = false;
+ UiDialog.close('wcfSystemConfirmation');
+ },
+
+ /**
+ * Invoked on dialog close or if user cancels the dialog.
+ */
+ _onClose: function() {
+ if (_active) {
+ _confirmButton.blur();
+ _active = false;
+
+ if (typeof _options.legacyCallback === 'function') {
+ _options.legacyCallback('cancel', _options.parameters, _content);
+ }
+ else if (typeof _options.cancel === 'function') {
+ _options.cancel(_options.parameters);
+ }
+ }
+ },
+
+ /**
+ * Sets the focus on the confirm button on dialog open for proper keyboard support.
+ */
+ _onShow: function() {
+ _confirmButton.blur();
+ _confirmButton.focus();
+ }
+ };
+});
+
+/**
+ * Provides consistent support for media queries and body scrolling.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Screen
+ */
+define('WoltLabSuite/Core/Ui/Screen',['Core', 'Dictionary', 'Environment'], function(Core, Dictionary, Environment) {
+ "use strict";
+
+ var _dialogContainer = null;
+ var _mql = new Dictionary();
+ var _scrollDisableCounter = 0;
+ var _scrollOffsetFrom = null;
+ var _scrollTop = 0;
+ var _pageOverlayCounter = 0;
+
+ var _mqMap = Dictionary.fromObject({
+ 'screen-xs': '(max-width: 544px)', /* smartphone */
+ 'screen-sm': '(min-width: 545px) and (max-width: 768px)', /* tablet (portrait) */
+ 'screen-sm-down': '(max-width: 768px)', /* smartphone + tablet (portrait) */
+ 'screen-sm-up': '(min-width: 545px)', /* tablet (portrait) + tablet (landscape) + desktop */
+ 'screen-sm-md': '(min-width: 545px) and (max-width: 1024px)', /* tablet (portrait) + tablet (landscape) */
+ 'screen-md': '(min-width: 769px) and (max-width: 1024px)', /* tablet (landscape) */
+ 'screen-md-down': '(max-width: 1024px)', /* smartphone + tablet (portrait) + tablet (landscape) */
+ 'screen-md-up': '(min-width: 769px)', /* tablet (landscape) + desktop */
+ 'screen-lg': '(min-width: 1025px)' /* desktop */
+ });
+
+ // Microsoft Edge rewrites the media queries to whatever it
+ // pleases, causing the input and output query to mismatch
+ var _mqMapEdge = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Screen
+ */
+ return {
+ /**
+ * Registers event listeners for media query match/unmatch.
+ *
+ * The `callbacks` object may contain the following keys:
+ * - `match`, triggered when media query matches
+ * - `unmatch`, triggered when media query no longer matches
+ * - `setup`, invoked when media query first matches
+ *
+ * Returns a UUID that is used to internal identify the callbacks, can be used
+ * to remove binding by calling the `remove` method.
+ *
+ * @param {string} query media query
+ * @param {object} callbacks callback functions
+ * @return {string} UUID for listener removal
+ */
+ on: function(query, callbacks) {
+ var uuid = Core.getUuid(), queryObject = this._getQueryObject(query);
+
+ if (typeof callbacks.match === 'function') {
+ queryObject.callbacksMatch.set(uuid, callbacks.match);
+ }
+
+ if (typeof callbacks.unmatch === 'function') {
+ queryObject.callbacksUnmatch.set(uuid, callbacks.unmatch);
+ }
+
+ if (typeof callbacks.setup === 'function') {
+ if (queryObject.mql.matches) {
+ callbacks.setup();
+ }
+ else {
+ queryObject.callbacksSetup.set(uuid, callbacks.setup);
+ }
+ }
+
+ return uuid;
+ },
+
+ /**
+ * Removes all listeners identified by their common UUID.
+ *
+ * @param {string} query must match the `query` argument used when calling `on()`
+ * @param {string} uuid UUID received when calling `on()`
+ */
+ remove: function(query, uuid) {
+ var queryObject = this._getQueryObject(query);
+
+ queryObject.callbacksMatch.delete(uuid);
+ queryObject.callbacksUnmatch.delete(uuid);
+ queryObject.callbacksSetup.delete(uuid);
+ },
+
+ /**
+ * Returns a boolean value if a media query expression currently matches.
+ *
+ * @param {string} query CSS media query
+ * @returns {boolean} true if query matches
+ */
+ is: function(query) {
+ return this._getQueryObject(query).mql.matches;
+ },
+
+ /**
+ * Disables scrolling of body element.
+ */
+ scrollDisable: function() {
+ if (_scrollDisableCounter === 0) {
+ _scrollTop = document.body.scrollTop;
+ _scrollOffsetFrom = 'body';
+ if (!_scrollTop) {
+ _scrollTop = document.documentElement.scrollTop;
+ _scrollOffsetFrom = 'documentElement';
+ }
+
+ var pageContainer = elById('pageContainer');
+
+ // setting translateY causes Mobile Safari to snap
+ if (Environment.platform() === 'ios') {
+ pageContainer.style.setProperty('position', 'relative', '');
+ pageContainer.style.setProperty('top', '-' + _scrollTop + 'px', '');
+ }
+ else {
+ pageContainer.style.setProperty('margin-top', '-' + _scrollTop + 'px', '');
+ }
+
+ document.documentElement.classList.add('disableScrolling');
+ }
+
+ _scrollDisableCounter++;
+ },
+
+ /**
+ * Re-enables scrolling of body element.
+ */
+ scrollEnable: function() {
+ if (_scrollDisableCounter) {
+ _scrollDisableCounter--;
+
+ if (_scrollDisableCounter === 0) {
+ document.documentElement.classList.remove('disableScrolling');
+
+ var pageContainer = elById('pageContainer');
+ if (Environment.platform() === 'ios') {
+ pageContainer.style.removeProperty('position');
+ pageContainer.style.removeProperty('top');
+ }
+ else {
+ pageContainer.style.removeProperty('margin-top');
+ }
+
+ if (_scrollTop) {
+ document[_scrollOffsetFrom].scrollTop = ~~_scrollTop;
+ }
+ }
+ }
+ },
+
+ /**
+ * Indicates that at least one page overlay is currently open.
+ */
+ pageOverlayOpen: function() {
+ if (_pageOverlayCounter === 0) {
+ document.documentElement.classList.add('pageOverlayActive');
+ }
+
+ _pageOverlayCounter++;
+ },
+
+ /**
+ * Marks one page overlay as closed.
+ */
+ pageOverlayClose: function() {
+ if (_pageOverlayCounter) {
+ _pageOverlayCounter--;
+
+ if (_pageOverlayCounter === 0) {
+ document.documentElement.classList.remove('pageOverlayActive');
+ }
+ }
+ },
+
+ /**
+ * Returns true if at least one page overlay is currently open.
+ *
+ * @returns {boolean}
+ */
+ pageOverlayIsActive: function() {
+ return _pageOverlayCounter > 0;
+ },
+
+ /**
+ * Sets the dialog container element. This method is used to
+ * circumvent a possible circular dependency, due to `Ui/Dialog`
+ * requiring the `Ui/Screen` module itself.
+ *
+ * @param {Element} container dialog container element
+ */
+ setDialogContainer: function (container) {
+ _dialogContainer = container;
+ },
+
+ /**
+ *
+ * @param {string} query CSS media query
+ * @return {Object} object containing callbacks and MediaQueryList
+ * @protected
+ */
+ _getQueryObject: function(query) {
+ if (typeof query !== 'string' || query.trim() === '') {
+ throw new TypeError("Expected a non-empty string for parameter 'query'.");
+ }
+
+ // Microsoft Edge rewrites the media queries to whatever it
+ // pleases, causing the input and output query to mismatch
+ if (_mqMapEdge.has(query)) query = _mqMapEdge.get(query);
+
+ if (_mqMap.has(query)) query = _mqMap.get(query);
+
+ var queryObject = _mql.get(query);
+ if (!queryObject) {
+ queryObject = {
+ callbacksMatch: new Dictionary(),
+ callbacksUnmatch: new Dictionary(),
+ callbacksSetup: new Dictionary(),
+ mql: window.matchMedia(query)
+ };
+ queryObject.mql.addListener(this._mqlChange.bind(this));
+
+ _mql.set(query, queryObject);
+
+ if (query !== queryObject.mql.media) {
+ _mqMapEdge.set(queryObject.mql.media, query);
+ }
+ }
+
+ return queryObject;
+ },
+
+ /**
+ * Triggered whenever a registered media query now matches or no longer matches.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _mqlChange: function(event) {
+ var queryObject = this._getQueryObject(event.media);
+ if (event.matches) {
+ if (queryObject.callbacksSetup.size) {
+ queryObject.callbacksSetup.forEach(function(callback) {
+ callback();
+ });
+
+ // discard all setup callbacks after execution
+ queryObject.callbacksSetup = new Dictionary();
+ }
+ else {
+ queryObject.callbacksMatch.forEach(function (callback) {
+ callback();
+ });
+ }
+ }
+ else {
+ queryObject.callbacksUnmatch.forEach(function(callback) {
+ callback();
+ });
+ }
+ }
+ };
+});
+
+/**
+ * Provides reliable checks for common key presses, uses `Event.key` on supported browsers
+ * or the deprecated `Event.which`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Event/Key
+ */
+define('WoltLabSuite/Core/Event/Key',[], function() {
+ "use strict";
+
+ function _isKey(event, key, which) {
+ if (!(event instanceof Event)) {
+ throw new TypeError("Expected a valid event when testing for key '" + key + "'.");
+ }
+
+ return event.key === key || event.which === which;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Event/Key
+ */
+ return {
+ /**
+ * Returns true if the pressed key equals 'ArrowDown'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowDown: function(event) {
+ return _isKey(event, 'ArrowDown', 40);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'ArrowLeft'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowLeft: function(event) {
+ return _isKey(event, 'ArrowLeft', 37);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'ArrowRight'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowRight: function(event) {
+ return _isKey(event, 'ArrowRight', 39);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'ArrowUp'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowUp: function(event) {
+ return _isKey(event, 'ArrowUp', 38);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Comma'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Comma: function(event) {
+ return _isKey(event, ',', 44);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'End'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ End: function(event) {
+ return _isKey(event, 'End', 35);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Enter'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Enter: function(event) {
+ return _isKey(event, 'Enter', 13);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Escape'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Escape: function(event) {
+ return _isKey(event, 'Escape', 27);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Home'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Home: function(event) {
+ return _isKey(event, 'Home', 36);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Space'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Space: function(event) {
+ return _isKey(event, 'Space', 32);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Tab'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Tab: function(event) {
+ return _isKey(event, 'Tab', 9);
+ }
+ };
+});
+
+/**
+ * Utility class to align elements relatively to another.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Alignment
+ */
+define('WoltLabSuite/Core/Ui/Alignment',['Core', 'Language', 'Dom/Traverse', 'Dom/Util'], function(Core, Language, DomTraverse, DomUtil) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Alignment
+ */
+ return {
+ /**
+ * Sets the alignment for target element relatively to the reference element.
+ *
+ * @param {Element} el target element
+ * @param {Element} ref reference element
+ * @param {Object<string, *>} options list of options to alter the behavior
+ */
+ set: function(el, ref, options) {
+ options = Core.extend({
+ // offset to reference element
+ verticalOffset: 0,
+
+ // align the pointer element, expects .elementPointer as a direct child of given element
+ pointer: false,
+
+ // offset from/left side, ignored for center alignment
+ pointerOffset: 4,
+
+ // use static pointer positions, expects two items: class to move it to the bottom and the second to move it to the right
+ pointerClassNames: [],
+
+ // alternate element used to calculate dimensions
+ refDimensionsElement: null,
+
+ // preferred alignment, possible values: left/right/center and top/bottom
+ horizontal: 'left',
+ vertical: 'bottom',
+
+ // allow flipping over axis, possible values: both, horizontal, vertical and none
+ allowFlip: 'both'
+ }, options);
+
+ if (!Array.isArray(options.pointerClassNames) || options.pointerClassNames.length !== (options.pointer ? 1 : 2)) options.pointerClassNames = [];
+ if (['left', 'right', 'center'].indexOf(options.horizontal) === -1) options.horizontal = 'left';
+ if (options.vertical !== 'bottom') options.vertical = 'top';
+ if (['both', 'horizontal', 'vertical', 'none'].indexOf(options.allowFlip) === -1) options.allowFlip = 'both';
+
+ // place element in the upper left corner to prevent calculation issues due to possible scrollbars
+ DomUtil.setStyles(el, {
+ bottom: 'auto !important',
+ left: '0 !important',
+ right: 'auto !important',
+ top: '0 !important',
+ visibility: 'hidden !important'
+ });
+
+ var elDimensions = DomUtil.outerDimensions(el);
+ var refDimensions = DomUtil.outerDimensions((options.refDimensionsElement instanceof Element ? options.refDimensionsElement : ref));
+ var refOffsets = DomUtil.offset(ref);
+ var windowHeight = window.innerHeight;
+ var windowWidth = document.body.clientWidth;
+
+ var horizontal = { result: null };
+ var alignCenter = false;
+ if (options.horizontal === 'center') {
+ alignCenter = true;
+ horizontal = this._tryAlignmentHorizontal(options.horizontal, elDimensions, refDimensions, refOffsets, windowWidth);
+
+ if (!horizontal.result) {
+ if (options.allowFlip === 'both' || options.allowFlip === 'horizontal') {
+ options.horizontal = 'left';
+ }
+ else {
+ horizontal.result = true;
+ }
+ }
+ }
+
+ // in rtl languages we simply swap the value for 'horizontal'
+ if (Language.get('wcf.global.pageDirection') === 'rtl') {
+ options.horizontal = (options.horizontal === 'left') ? 'right' : 'left';
+ }
+
+ if (!horizontal.result) {
+ var horizontalCenter = horizontal;
+ horizontal = this._tryAlignmentHorizontal(options.horizontal, elDimensions, refDimensions, refOffsets, windowWidth);
+ if (!horizontal.result && (options.allowFlip === 'both' || options.allowFlip === 'horizontal')) {
+ var horizontalFlipped = this._tryAlignmentHorizontal((options.horizontal === 'left' ? 'right' : 'left'), elDimensions, refDimensions, refOffsets, windowWidth);
+ // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
+ if (horizontalFlipped.result) {
+ horizontal = horizontalFlipped;
+ }
+ else if (alignCenter) {
+ horizontal = horizontalCenter;
+ }
+ }
+ }
+
+ var left = horizontal.left;
+ var right = horizontal.right;
+
+ var vertical = this._tryAlignmentVertical(options.vertical, elDimensions, refDimensions, refOffsets, windowHeight, options.verticalOffset);
+ if (!vertical.result && (options.allowFlip === 'both' || options.allowFlip === 'vertical')) {
+ var verticalFlipped = this._tryAlignmentVertical((options.vertical === 'top' ? 'bottom' : 'top'), elDimensions, refDimensions, refOffsets, windowHeight, options.verticalOffset);
+ // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
+ if (verticalFlipped.result) {
+ vertical = verticalFlipped;
+ }
+ }
+
+ var bottom = vertical.bottom;
+ var top = vertical.top;
+
+ // set pointer position
+ if (options.pointer) {
+ var pointer = DomTraverse.childrenByClass(el, 'elementPointer');
+ pointer = pointer[0] || null;
+ if (pointer === null) {
+ throw new Error("Expected the .elementPointer element to be a direct children.");
+ }
+
+ if (horizontal.align === 'center') {
+ pointer.classList.add('center');
+
+ pointer.classList.remove('left');
+ pointer.classList.remove('right');
+ }
+ else {
+ pointer.classList.add(horizontal.align);
+
+ pointer.classList.remove('center');
+ pointer.classList.remove(horizontal.align === 'left' ? 'right' : 'left');
+ }
+
+ if (vertical.align === 'top') {
+ pointer.classList.add('flipVertical');
+ }
+ else {
+ pointer.classList.remove('flipVertical');
+ }
+ }
+ else if (options.pointerClassNames.length === 2) {
+ var pointerBottom = 0;
+ var pointerRight = 1;
+
+ el.classList[(top === 'auto' ? 'add' : 'remove')](options.pointerClassNames[pointerBottom]);
+ el.classList[(left === 'auto' ? 'add' : 'remove')](options.pointerClassNames[pointerRight]);
+ }
+
+ if (bottom !== 'auto') bottom = Math.round(bottom) + 'px';
+ if (left !== 'auto') left = Math.ceil(left) + 'px';
+ if (right !== 'auto') right = Math.floor(right) + 'px';
+ if (top !== 'auto') top = Math.round(top) + 'px';
+
+ DomUtil.setStyles(el, {
+ bottom: bottom,
+ left: left,
+ right: right,
+ top: top
+ });
+
+ elShow(el);
+ el.style.removeProperty('visibility');
+ },
+
+ /**
+ * Calculates left/right position and verifies if the element would be still within the page's boundaries.
+ *
+ * @param {string} align align to this side of the reference element
+ * @param {Object<string, int>} elDimensions element dimensions
+ * @param {Object<string, int>} refDimensions reference element dimensions
+ * @param {Object<string, int>} refOffsets position of reference element relative to the document
+ * @param {int} windowWidth window width
+ * @returns {Object<string, *>} calculation results
+ */
+ _tryAlignmentHorizontal: function(align, elDimensions, refDimensions, refOffsets, windowWidth) {
+ var left = 'auto';
+ var right = 'auto';
+ var result = true;
+
+ if (align === 'left') {
+ left = refOffsets.left;
+ if (left + elDimensions.width > windowWidth) {
+ result = false;
+ }
+ }
+ else if (align === 'right') {
+ if (refOffsets.left + refDimensions.width < elDimensions.width) {
+ result = false;
+ }
+ else {
+ right = windowWidth - (refOffsets.left + refDimensions.width);
+ if (right < 0) {
+ result = false;
+ }
+ }
+ }
+ else {
+ left = refOffsets.left + (refDimensions.width / 2) - (elDimensions.width / 2);
+ left = ~~left;
+
+ if (left < 0 || left + elDimensions.width > windowWidth) {
+ result = false;
+ }
+ }
+
+ return {
+ align: align,
+ left: left,
+ right: right,
+ result: result
+ };
+ },
+
+ /**
+ * Calculates top/bottom position and verifies if the element would be still within the page's boundaries.
+ *
+ * @param {string} align align to this side of the reference element
+ * @param {Object<string, int>} elDimensions element dimensions
+ * @param {Object<string, int>} refDimensions reference element dimensions
+ * @param {Object<string, int>} refOffsets position of reference element relative to the document
+ * @param {int} windowHeight window height
+ * @param {int} verticalOffset desired gap between element and reference element
+ * @returns {object<string, *>} calculation results
+ */
+ _tryAlignmentVertical: function(align, elDimensions, refDimensions, refOffsets, windowHeight, verticalOffset) {
+ var bottom = 'auto';
+ var top = 'auto';
+ var result = true;
+
+ if (align === 'top') {
+ var bodyHeight = document.body.clientHeight;
+ bottom = (bodyHeight - refOffsets.top) + verticalOffset;
+ if (bodyHeight - (bottom + elDimensions.height) < (window.scrollY || window.pageYOffset)) {
+ result = false;
+ }
+ }
+ else {
+ top = refOffsets.top + refDimensions.height + verticalOffset;
+ if (top + elDimensions.height - (window.scrollY || window.pageYOffset) > windowHeight) {
+ result = false;
+ }
+ }
+
+ return {
+ align: align,
+ bottom: bottom,
+ top: top,
+ result: result
+ };
+ }
+ };
+});
+
+/**
+ * Allows to be informed when a click event bubbled up to the document's body.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/CloseOverlay
+ */
+define('WoltLabSuite/Core/Ui/CloseOverlay',['CallbackList'], function(CallbackList) {
+ "use strict";
+
+ var _callbackList = new CallbackList();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/CloseOverlay
+ */
+ var UiCloseOverlay = {
+ /**
+ * Sets up global event listener for bubbled clicks events.
+ */
+ setup: function() {
+ document.body.addEventListener(WCF_CLICK_EVENT, this.execute.bind(this));
+ },
+
+ /**
+ * @see WoltLabSuite/Core/CallbackList#add
+ */
+ add: _callbackList.add.bind(_callbackList),
+
+ /**
+ * @see WoltLabSuite/Core/CallbackList#remove
+ */
+ remove: _callbackList.remove.bind(_callbackList),
+
+ /**
+ * Invokes all registered callbacks.
+ */
+ execute: function() {
+ _callbackList.forEach(null, function(callback) {
+ callback();
+ });
+ }
+ };
+
+ UiCloseOverlay.setup();
+
+ return UiCloseOverlay;
+});
+
+/**
+ * Simple dropdown implementation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dropdown/Simple
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Dropdown/Simple',[ 'CallbackList', 'Core', 'Dictionary', 'EventKey', 'Ui/Alignment', 'Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'Ui/CloseOverlay'],
+ function(CallbackList, Core, Dictionary, EventKey, UiAlignment, DomChangeListener, DomTraverse, DomUtil, UiCloseOverlay)
+{
+ "use strict";
+
+ var _availableDropdowns = null;
+ var _callbacks = new CallbackList();
+ var _didInit = false;
+ var _dropdowns = new Dictionary();
+ var _menus = new Dictionary();
+ var _menuContainer = null;
+ var _callbackDropdownMenuKeyDown = null;
+ var _activeTargetId = '';
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dropdown/Simple
+ */
+ return {
+ /**
+ * Performs initial setup such as setting up dropdowns and binding listeners.
+ */
+ setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _menuContainer = elCreate('div');
+ _menuContainer.className = 'dropdownMenuContainer';
+ document.body.appendChild(_menuContainer);
+
+ _availableDropdowns = elByClass('dropdownToggle');
+
+ this.initAll();
+
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.closeAll.bind(this));
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.initAll.bind(this));
+
+ document.addEventListener('scroll', this._onScroll.bind(this));
+
+ // expose on window object for backward compatibility
+ window.bc_wcfSimpleDropdown = this;
+
+ _callbackDropdownMenuKeyDown = this._dropdownMenuKeyDown.bind(this);
+ },
+
+ /**
+ * Loops through all possible dropdowns and registers new ones.
+ */
+ initAll: function() {
+ for (var i = 0, length = _availableDropdowns.length; i < length; i++) {
+ this.init(_availableDropdowns[i], false);
+ }
+ },
+
+ /**
+ * Initializes a dropdown.
+ *
+ * @param {Element} button
+ * @param {boolean|Event} isLazyInitialization
+ */
+ init: function(button, isLazyInitialization) {
+ this.setup();
+
+ elAttr(button, 'role', 'button');
+ elAttr(button, 'tabindex', '0');
+ elAttr(button, 'aria-haspopup', true);
+ elAttr(button, 'aria-expanded', false);
+
+ if (button.classList.contains('jsDropdownEnabled') || elData(button, 'target')) {
+ return false;
+ }
+
+ var dropdown = DomTraverse.parentByClass(button, 'dropdown');
+ if (dropdown === null) {
+ throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a parent with .dropdown.");
+ }
+
+ var menu = DomTraverse.nextByClass(button, 'dropdownMenu');
+ if (menu === null) {
+ throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a menu as next sibling.");
+ }
+
+ // move menu into global container
+ _menuContainer.appendChild(menu);
+
+ var containerId = DomUtil.identify(dropdown);
+ if (!_dropdowns.has(containerId)) {
+ button.classList.add('jsDropdownEnabled');
+ button.addEventListener(WCF_CLICK_EVENT, this._toggle.bind(this));
+ button.addEventListener('keydown', this._handleKeyDown.bind(this));
+
+ _dropdowns.set(containerId, dropdown);
+ _menus.set(containerId, menu);
+
+ if (!containerId.match(/^wcf\d+$/)) {
+ elData(menu, 'source', containerId);
+ }
+
+ // prevent page scrolling
+ if (menu.childElementCount && menu.children[0].classList.contains('scrollableDropdownMenu')) {
+ menu = menu.children[0];
+ elData(menu, 'scroll-to-active', true);
+
+ var menuHeight = null, menuRealHeight = null;
+ menu.addEventListener('wheel', function (event) {
+ if (menuHeight === null) menuHeight = menu.clientHeight;
+ if (menuRealHeight === null) menuRealHeight = menu.scrollHeight;
+
+ // negative value: scrolling up
+ if (event.deltaY < 0 && menu.scrollTop === 0) {
+ event.preventDefault();
+ }
+ else if (event.deltaY > 0 && (menu.scrollTop + menuHeight === menuRealHeight)) {
+ event.preventDefault();
+ }
+ }, { passive: false });
+ }
+ }
+
+ elData(button, 'target', containerId);
+
+ if (isLazyInitialization) {
+ setTimeout(function() {
+ elData(button, 'dropdown-lazy-init', (isLazyInitialization instanceof MouseEvent));
+
+ Core.triggerEvent(button, WCF_CLICK_EVENT);
+
+ setTimeout(function() {
+ button.removeAttribute('data-dropdown-lazy-init');
+ }, 10);
+ }, 10);
+ }
+ },
+
+ /**
+ * Initializes a remote-controlled dropdown.
+ *
+ * @param {Element} dropdown dropdown wrapper element
+ * @param {Element} menu menu list element
+ */
+ initFragment: function(dropdown, menu) {
+ this.setup();
+
+ var containerId = DomUtil.identify(dropdown);
+ if (_dropdowns.has(containerId)) {
+ return;
+ }
+
+ _dropdowns.set(containerId, dropdown);
+ _menuContainer.appendChild(menu);
+
+ _menus.set(containerId, menu);
+ },
+
+ /**
+ * Registers a callback for open/close events.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {function(string, string)} callback
+ */
+ registerCallback: function(containerId, callback) {
+ _callbacks.add(containerId, callback);
+ },
+
+ /**
+ * Returns the requested dropdown wrapper element.
+ *
+ * @return {Element} dropdown wrapper element
+ */
+ getDropdown: function(containerId) {
+ return _dropdowns.get(containerId);
+ },
+
+ /**
+ * Returns the requested dropdown menu list element.
+ *
+ * @return {Element} menu list element
+ */
+ getDropdownMenu: function(containerId) {
+ return _menus.get(containerId);
+ },
+
+ /**
+ * Toggles the requested dropdown between opened and closed.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {Element=} referenceElement alternative reference element, used for reusable dropdown menus
+ * @param {boolean=} disableAutoFocus
+ */
+ toggleDropdown: function(containerId, referenceElement, disableAutoFocus) {
+ this._toggle(null, containerId, referenceElement, disableAutoFocus);
+ },
+
+ /**
+ * Calculates and sets the alignment of given dropdown.
+ *
+ * @param {Element} dropdown dropdown wrapper element
+ * @param {Element} dropdownMenu menu list element
+ * @param {Element=} alternateElement alternative reference element for alignment
+ */
+ setAlignment: function(dropdown, dropdownMenu, alternateElement) {
+ // check if button belongs to an i18n textarea
+ var button = elBySel('.dropdownToggle', dropdown), refDimensionsElement;
+ if (button !== null && button.parentNode.classList.contains('inputAddonTextarea')) {
+ refDimensionsElement = button;
+ }
+
+ UiAlignment.set(dropdownMenu, alternateElement || dropdown, {
+ pointerClassNames: ['dropdownArrowBottom', 'dropdownArrowRight'],
+ refDimensionsElement: refDimensionsElement || null,
+
+ // alignment
+ horizontal: (elData(dropdownMenu, 'dropdown-alignment-horizontal') === 'right') ? 'right' : 'left',
+ vertical: (elData(dropdownMenu, 'dropdown-alignment-vertical') === 'top') ? 'top' : 'bottom',
+
+ allowFlip: elData(dropdownMenu, 'dropdown-allow-flip') || 'both'
+ });
+ },
+
+ /**
+ * Calculates and sets the alignment of the dropdown identified by given id.
+ *
+ * @param {string} containerId dropdown wrapper id
+ */
+ setAlignmentById: function(containerId) {
+ var dropdown = _dropdowns.get(containerId);
+ if (dropdown === undefined) {
+ throw new Error("Unknown dropdown identifier '" + containerId + "'.");
+ }
+
+ var menu = _menus.get(containerId);
+
+ this.setAlignment(dropdown, menu);
+ },
+
+ /**
+ * Returns true if target dropdown exists and is open.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @return {boolean} true if dropdown exists and is open
+ */
+ isOpen: function(containerId) {
+ var menu = _menus.get(containerId);
+ return (menu !== undefined && menu.classList.contains('dropdownOpen'));
+ },
+
+ /**
+ * Opens the dropdown unless it is already open.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {boolean=} disableAutoFocus
+ */
+ open: function(containerId, disableAutoFocus) {
+ var menu = _menus.get(containerId);
+ if (menu !== undefined && !menu.classList.contains('dropdownOpen')) {
+ this.toggleDropdown(containerId, undefined, disableAutoFocus);
+ }
+ },
+
+ /**
+ * Closes the dropdown identified by given id without notifying callbacks.
+ *
+ * @param {string} containerId dropdown wrapper id
+ */
+ close: function(containerId) {
+ var dropdown = _dropdowns.get(containerId);
+ if (dropdown !== undefined) {
+ dropdown.classList.remove('dropdownOpen');
+ _menus.get(containerId).classList.remove('dropdownOpen');
+ }
+ },
+
+ /**
+ * Closes all dropdowns.
+ */
+ closeAll: function() {
+ _dropdowns.forEach((function(dropdown, containerId) {
+ if (dropdown.classList.contains('dropdownOpen')) {
+ dropdown.classList.remove('dropdownOpen');
+ _menus.get(containerId).classList.remove('dropdownOpen');
+
+ this._notifyCallbacks(containerId, 'close');
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Destroys a dropdown identified by given id.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @return {boolean} false for unknown dropdowns
+ */
+ destroy: function(containerId) {
+ if (!_dropdowns.has(containerId)) {
+ return false;
+ }
+
+ try {
+ this.close(containerId);
+
+ elRemove(_menus.get(containerId));
+ }
+ catch (e) {
+ // the elements might not exist anymore thus ignore all errors while cleaning up
+ }
+
+ _menus.delete(containerId);
+ _dropdowns.delete(containerId);
+
+ return true;
+ },
+
+ /**
+ * Handles dropdown positions in overlays when scrolling in the overlay.
+ *
+ * @param {Event} event event object
+ */
+ _onDialogScroll: function(event) {
+ var dialogContent = event.currentTarget;
+ //noinspection JSCheckFunctionSignatures
+ var dropdowns = elBySelAll('.dropdown.dropdownOpen', dialogContent);
+
+ for (var i = 0, length = dropdowns.length; i < length; i++) {
+ var dropdown = dropdowns[i];
+ var containerId = DomUtil.identify(dropdown);
+ var offset = DomUtil.offset(dropdown);
+ var dialogOffset = DomUtil.offset(dialogContent);
+
+ // check if dropdown toggle is still (partially) visible
+ if (offset.top + dropdown.clientHeight <= dialogOffset.top) {
+ // top check
+ this.toggleDropdown(containerId);
+ }
+ else if (offset.top >= dialogOffset.top + dialogContent.offsetHeight) {
+ // bottom check
+ this.toggleDropdown(containerId);
+ }
+ else if (offset.left <= dialogOffset.left) {
+ // left check
+ this.toggleDropdown(containerId);
+ }
+ else if (offset.left >= dialogOffset.left + dialogContent.offsetWidth) {
+ // right check
+ this.toggleDropdown(containerId);
+ }
+ else {
+ this.setAlignment(_dropdowns.get(containerId), _menus.get(containerId));
+ }
+ }
+ },
+
+ /**
+ * Recalculates dropdown positions on page scroll.
+ */
+ _onScroll: function() {
+ _dropdowns.forEach((function(dropdown, containerId) {
+ if (dropdown.classList.contains('dropdownOpen')) {
+ if (elDataBool(dropdown, 'is-overlay-dropdown-button')) {
+ this.setAlignment(dropdown, _menus.get(containerId));
+ }
+ else {
+ var menu = _menus.get(dropdown.id);
+ if (!elDataBool(menu, 'dropdown-ignore-page-scroll')) {
+ this.close(containerId);
+ }
+ }
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Notifies callbacks on status change.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {string} action can be either 'open' or 'close'
+ */
+ _notifyCallbacks: function(containerId, action) {
+ _callbacks.forEach(containerId, function(callback) {
+ callback(containerId, action);
+ });
+ },
+
+ /**
+ * Toggles the dropdown's state between open and close.
+ *
+ * @param {?Event} event event object, should be 'null' if targetId is given
+ * @param {string?} targetId dropdown wrapper id
+ * @param {Element=} alternateElement alternative reference element for alignment
+ * @param {boolean=} disableAutoFocus
+ * @return {boolean} 'false' if event is not null
+ */
+ _toggle: function(event, targetId, alternateElement, disableAutoFocus) {
+ if (event !== null) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ //noinspection JSCheckFunctionSignatures
+ targetId = elData(event.currentTarget, 'target');
+
+ if (disableAutoFocus === undefined && event instanceof MouseEvent) {
+ disableAutoFocus = true;
+ }
+ }
+
+ var dropdown = _dropdowns.get(targetId), preventToggle = false;
+ if (dropdown !== undefined) {
+ var button;
+
+ // check if the dropdown is still the same, as some components (e.g. page actions)
+ // re-create the parent of a button
+ if (event) {
+ button = event.currentTarget, parent = button.parentNode;
+ if (parent !== dropdown) {
+ parent.classList.add('dropdown');
+ parent.id = dropdown.id;
+
+ // remove dropdown class and id from old parent
+ dropdown.classList.remove('dropdown');
+ dropdown.id = '';
+
+ dropdown = parent;
+ _dropdowns.set(targetId, parent);
+ }
+ }
+
+ if (disableAutoFocus === undefined) {
+ button = dropdown.closest('.dropdownToggle');
+ if (!button) {
+ button = elBySel('.dropdownToggle', dropdown);
+
+ if (!button && dropdown.id) {
+ button = elBySel('[data-target="' + dropdown.id + '"]');
+ }
+ }
+
+ if (button && elDataBool(button, 'dropdown-lazy-init')) {
+ disableAutoFocus = true;
+ }
+ }
+
+ // Repeated clicks on the dropdown button will not cause it to close, the only way
+ // to close it is by clicking somewhere else in the document or on another dropdown
+ // toggle. This is used with the search bar to prevent the dropdown from closing by
+ // setting the caret position in the search input field.
+ if (elDataBool(dropdown, 'dropdown-prevent-toggle') && dropdown.classList.contains('dropdownOpen')) {
+ preventToggle = true;
+ }
+
+ // check if 'isOverlayDropdownButton' is set which indicates that the dropdown toggle is within an overlay
+ if (elData(dropdown, 'is-overlay-dropdown-button') === '') {
+ var dialogContent = DomTraverse.parentByClass(dropdown, 'dialogContent');
+ elData(dropdown, 'is-overlay-dropdown-button', (dialogContent !== null));
+
+ if (dialogContent !== null) {
+ dialogContent.addEventListener('scroll', this._onDialogScroll.bind(this));
+ }
+ }
+ }
+
+ // close all dropdowns
+ _activeTargetId = '';
+ _dropdowns.forEach((function(dropdown, containerId) {
+ var menu = _menus.get(containerId);
+
+ if (dropdown.classList.contains('dropdownOpen')) {
+ if (preventToggle === false) {
+ dropdown.classList.remove('dropdownOpen');
+ menu.classList.remove('dropdownOpen');
+
+ var button = elBySel('.dropdownToggle', dropdown);
+ if (button) elAttr(button, 'aria-expanded', false);
+
+ this._notifyCallbacks(containerId, 'close');
+ }
+ else {
+ _activeTargetId = targetId;
+ }
+ }
+ else if (containerId === targetId && menu.childElementCount > 0) {
+ _activeTargetId = targetId;
+ dropdown.classList.add('dropdownOpen');
+ menu.classList.add('dropdownOpen');
+
+ var button = elBySel('.dropdownToggle', dropdown);
+ if (button) elAttr(button, 'aria-expanded', true);
+
+ if (menu.childElementCount && elDataBool(menu.children[0], 'scroll-to-active')) {
+ var list = menu.children[0];
+ list.removeAttribute('data-scroll-to-active');
+
+ var active = null;
+ for (var i = 0, length = list.childElementCount; i < length; i++) {
+ if (list.children[i].classList.contains('active')) {
+ active = list.children[i];
+ break;
+ }
+ }
+
+ if (active) {
+ list.scrollTop = Math.max((active.offsetTop + active.clientHeight) - menu.clientHeight, 0);
+ }
+ }
+
+ var itemList = elBySel('.scrollableDropdownMenu', menu);
+ if (itemList !== null) {
+ itemList.classList[(itemList.scrollHeight > itemList.clientHeight ? 'add' : 'remove')]('forceScrollbar');
+ }
+
+ this._notifyCallbacks(containerId, 'open');
+
+ var firstListItem = null;
+ if (!disableAutoFocus) {
+ elAttr(menu, 'role', 'menu');
+ elAttr(menu, 'tabindex', -1);
+ menu.removeEventListener('keydown', _callbackDropdownMenuKeyDown);
+ menu.addEventListener('keydown', _callbackDropdownMenuKeyDown);
+ elBySelAll('li', menu, function (listItem) {
+ if (!listItem.clientHeight) return;
+ if (firstListItem === null) firstListItem = listItem;
+ else if (listItem.classList.contains('active')) firstListItem = listItem;
+
+ elAttr(listItem, 'role', 'menuitem');
+ elAttr(listItem, 'tabindex', -1);
+ });
+ }
+
+ this.setAlignment(dropdown, menu, alternateElement);
+
+ if (firstListItem !== null) {
+ firstListItem.focus();
+ }
+ }
+ }).bind(this));
+
+ //noinspection JSDeprecatedSymbols
+ window.WCF.Dropdown.Interactive.Handler.closeAll();
+
+ return (event === null);
+ },
+
+ _handleKeyDown: function(event) {
+ if (EventKey.Enter(event) || EventKey.Space(event)) {
+ event.preventDefault();
+ this._toggle(event);
+ }
+ },
+
+ _dropdownMenuKeyDown: function(event) {
+ var button, dropdown;
+
+ var activeItem = document.activeElement;
+ if (activeItem.nodeName !== 'LI') {
+ return;
+ }
+
+ if (EventKey.ArrowDown(event) || EventKey.ArrowUp(event) || EventKey.End(event) || EventKey.Home(event)) {
+ event.preventDefault();
+
+ var listItems = Array.prototype.slice.call(elBySelAll('li', activeItem.closest('.dropdownMenu')));
+ if (EventKey.ArrowUp(event) || EventKey.End(event)) {
+ listItems.reverse();
+ }
+ var newActiveItem = null;
+ var isValidItem = function(listItem) {
+ return !listItem.classList.contains('dropdownDivider') && listItem.clientHeight > 0;
+ };
+
+ var activeIndex = listItems.indexOf(activeItem);
+ if (EventKey.End(event) || EventKey.Home(event)) {
+ activeIndex = -1;
+ }
+
+ for (var i = activeIndex + 1; i < listItems.length; i++) {
+ if (isValidItem(listItems[i])) {
+ newActiveItem = listItems[i];
+ break;
+ }
+ }
+
+ if (newActiveItem === null) {
+ for (i = 0; i < listItems.length; i++) {
+ if (isValidItem(listItems[i])) {
+ newActiveItem = listItems[i];
+ break;
+ }
+ }
+ }
+
+ newActiveItem.focus();
+ }
+ else if (EventKey.Enter(event) || EventKey.Space(event)) {
+ event.preventDefault();
+
+ var target = activeItem;
+ if (target.childElementCount === 1 && (target.children[0].nodeName === 'SPAN' || target.children[0].nodeName === 'A')) {
+ target = target.children[0];
+ }
+
+ dropdown = _dropdowns.get(_activeTargetId);
+ button = elBySel('.dropdownToggle', dropdown);
+ require(['Core'], function(Core) {
+ var mouseEvent = elData(dropdown, 'a11y-mouse-event') || 'click';
+ Core.triggerEvent(target, mouseEvent);
+
+ if (button) button.focus();
+ });
+ }
+ else if (EventKey.Escape(event) || EventKey.Tab(event)) {
+ event.preventDefault();
+
+ dropdown = _dropdowns.get(_activeTargetId);
+ button = elBySel('.dropdownToggle', dropdown);
+ // Remote controlled drop-down menus may not have a dedicated toggle button, instead the
+ // `dropdown` element itself is the button.
+ if (button === null && !dropdown.classList.contains('dropdown')) {
+ button = dropdown;
+ }
+
+ this._toggle(null, _activeTargetId);
+ if (button) button.focus();
+ }
+ }
+ };
+});
+
+/**
+ * Developer tools for WoltLab Suite.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Devtools
+ */
+define('WoltLabSuite/Core/Devtools',[], function() {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ return {
+ help: function () {},
+ toggleEditorAutosave: function () {},
+ toggleEventLogging: function () {},
+ _internal_: {
+ enable: function () {},
+ editorAutosave: function () {},
+ eventLog: function() {}
+ }
+ };
+ }
+
+ var _settings = {
+ editorAutosave: true,
+ eventLogging: false
+ };
+
+ var _updateConfig = function () {
+ if (window.sessionStorage) {
+ window.sessionStorage.setItem("__wsc_devtools_config", JSON.stringify(_settings));
+ }
+ };
+
+ var Devtools = {
+ /**
+ * Prints the list of available commands.
+ */
+ help: function () {
+ window.console.log("");
+ window.console.log("%cAvailable commands:", "text-decoration: underline");
+
+ var cmds = [];
+ for (var cmd in Devtools) {
+ if (cmd !== '_internal_' && Devtools.hasOwnProperty(cmd)) {
+ cmds.push(cmd);
+ }
+ }
+ cmds.sort().forEach(function(cmd) {
+ window.console.log("\tDevtools." + cmd + "()");
+ });
+
+ window.console.log("");
+ },
+
+ /**
+ * Disables/re-enables the editor autosave feature.
+ *
+ * @param {boolean} forceDisable
+ */
+ toggleEditorAutosave: function(forceDisable) {
+ _settings.editorAutosave = (forceDisable === true) ? false : !_settings.editorAutosave;
+ _updateConfig();
+
+ window.console.log("%c\tEditor autosave " + (_settings.editorAutosave ? "enabled" : "disabled"), "font-style: italic");
+ },
+
+ /**
+ * Enables/disables logging for fired event listener events.
+ *
+ * @param {boolean} forceEnable
+ */
+ toggleEventLogging: function(forceEnable) {
+ _settings.eventLogging = (forceEnable === true) ? true : !_settings.eventLogging;
+ _updateConfig();
+
+ window.console.log("%c\tEvent logging " + (_settings.eventLogging ? "enabled" : "disabled"), "font-style: italic");
+ },
+
+ /**
+ * Internal methods not meant to be called directly.
+ */
+ _internal_: {
+ enable: function () {
+ window.Devtools = Devtools;
+
+ window.console.log("%cDevtools for WoltLab Suite loaded", "font-weight: bold");
+
+ if (window.sessionStorage) {
+ var settings = window.sessionStorage.getItem("__wsc_devtools_config");
+ try {
+ if (settings !== null) {
+ _settings = JSON.parse(settings);
+ }
+ }
+ catch (e) {}
+
+ if (!_settings.editorAutosave) Devtools.toggleEditorAutosave(true);
+ if (_settings.eventLogging) Devtools.toggleEventLogging(true);
+ }
+
+ window.console.log("Settings are saved per browser session, enter `Devtools.help()` to learn more.");
+ window.console.log("");
+ },
+
+ editorAutosave: function () {
+ return _settings.editorAutosave;
+ },
+
+ eventLog: function(identifier, action) {
+ if (_settings.eventLogging) {
+ window.console.log("[Devtools.EventLogging] Firing event: " + action + " @ " + identifier);
+ }
+ }
+ }
+ };
+
+ return Devtools;
+});
+
+/**
+ * Versatile event system similar to the WCF-PHP counter part.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Event/Handler
+ */
+define('WoltLabSuite/Core/Event/Handler',['Core', 'Devtools', 'Dictionary'], function(Core, Devtools, Dictionary) {
+ "use strict";
+
+ var _listeners = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Event/Handler
+ */
+ return {
+ /**
+ * Adds an event listener.
+ *
+ * @param {string} identifier event identifier
+ * @param {string} action action name
+ * @param {function(object)} callback callback function
+ * @return {string} uuid required for listener removal
+ */
+ add: function(identifier, action, callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("[WoltLabSuite/Core/Event/Handler] Expected a valid callback for '" + action + "@" + identifier + "'.");
+ }
+
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ actions = new Dictionary();
+ _listeners.set(identifier, actions);
+ }
+
+ var callbacks = actions.get(action);
+ if (callbacks === undefined) {
+ callbacks = new Dictionary();
+ actions.set(action, callbacks);
+ }
+
+ var uuid = Core.getUuid();
+ callbacks.set(uuid, callback);
+
+ return uuid;
+ },
+
+ /**
+ * Fires an event and notifies all listeners.
+ *
+ * @param {string} identifier event identifier
+ * @param {string} action action name
+ * @param {object=} data event data
+ */
+ fire: function(identifier, action, data) {
+ Devtools._internal_.eventLog(identifier, action);
+
+ data = data || {};
+
+ var actions = _listeners.get(identifier);
+ if (actions !== undefined) {
+ var callbacks = actions.get(action);
+ if (callbacks !== undefined) {
+ callbacks.forEach(function(callback) {
+ callback(data);
+ });
+ }
+ }
+ },
+
+ /**
+ * Removes an event listener, requires the uuid returned by add().
+ *
+ * @param {string} identifier event identifier
+ * @param {string} action action name
+ * @param {string} uuid listener uuid
+ */
+ remove: function(identifier, action, uuid) {
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ return;
+ }
+
+ var callbacks = actions.get(action);
+ if (callbacks === undefined) {
+ return;
+ }
+
+ callbacks['delete'](uuid);
+ },
+
+ /**
+ * Removes all event listeners for given action. Omitting the second parameter will
+ * remove all listeners for this identifier.
+ *
+ * @param {string} identifier event identifier
+ * @param {string=} action action name
+ */
+ removeAll: function(identifier, action) {
+ if (typeof action !== 'string') action = undefined;
+
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ return;
+ }
+
+ if (typeof action === 'undefined') {
+ _listeners['delete'](identifier);
+ }
+ else {
+ actions['delete'](action);
+ }
+ },
+
+ /**
+ * Removes all listeners registered for an identifier and ending with a special suffix.
+ * This is commonly used to unbound event handlers for the editor.
+ *
+ * @param {string} identifier event identifier
+ * @param {string} suffix action suffix
+ */
+ removeAllBySuffix: function (identifier, suffix) {
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ return;
+ }
+
+ suffix = '_' + suffix;
+ var length = suffix.length * -1;
+ actions.forEach((function (callbacks, action) {
+ //noinspection JSUnresolvedFunction
+ if (action.substr(length) === suffix) {
+ this.removeAll(identifier, action);
+ }
+ }).bind(this));
+ }
+ };
+});
+
+/**
+ * List implementation relying on an array or if supported on a Set to hold values.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/List
+ */
+define('WoltLabSuite/Core/List',[], function() {
+ "use strict";
+
+ var _hasSet = objOwns(window, 'Set') && typeof window.Set === 'function';
+
+ /**
+ * @constructor
+ */
+ function List() {
+ this._set = (_hasSet) ? new Set() : [];
+ }
+ List.prototype = {
+ /**
+ * Appends an element to the list, silently rejects adding an already existing value.
+ *
+ * @param {?} value unique element
+ */
+ add: function(value) {
+ if (_hasSet) {
+ this._set.add(value);
+ }
+ else if (!this.has(value)) {
+ this._set.push(value);
+ }
+ },
+
+ /**
+ * Removes all elements from the list.
+ */
+ clear: function() {
+ if (_hasSet) {
+ this._set.clear();
+ }
+ else {
+ this._set = [];
+ }
+ },
+
+ /**
+ * Removes an element from the list, returns true if the element was in the list.
+ *
+ * @param {?} value element
+ * @return {boolean} true if element was in the list
+ */
+ 'delete': function(value) {
+ if (_hasSet) {
+ return this._set['delete'](value);
+ }
+ else {
+ var index = this._set.indexOf(value);
+ if (index === -1) {
+ return false;
+ }
+
+ this._set.splice(index, 1);
+ return true;
+ }
+ },
+
+ /**
+ * Calls `callback` for each element in the list.
+ */
+ forEach: function(callback) {
+ if (_hasSet) {
+ this._set.forEach(callback);
+ }
+ else {
+ for (var i = 0, length = this._set.length; i < length; i++) {
+ callback(this._set[i]);
+ }
+ }
+ },
+
+ /**
+ * Returns true if the list contains the element.
+ *
+ * @param {?} value element
+ * @return {boolean} true if element is in the list
+ */
+ has: function(value) {
+ if (_hasSet) {
+ return this._set.has(value);
+ }
+ else {
+ return (this._set.indexOf(value) !== -1);
+ }
+ }
+ };
+
+ Object.defineProperty(List.prototype, 'size', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
+ if (_hasSet) {
+ return this._set.size;
+ }
+ else {
+ return this._set.length;
+ }
+ }
+ });
+
+ return List;
+});
+
+/**
+ * Modal dialog handler.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dialog
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Dialog',[
+ 'Ajax', 'Core', 'Dictionary',
+ 'Environment', 'Language', 'ObjectMap', 'Dom/ChangeListener',
+ 'Dom/Traverse', 'Dom/Util', 'Ui/Confirmation', 'Ui/Screen', 'Ui/SimpleDropdown',
+ 'EventHandler', 'List', 'EventKey'
+ ],
+ function(
+ Ajax, Core, Dictionary,
+ Environment, Language, ObjectMap, DomChangeListener,
+ DomTraverse, DomUtil, UiConfirmation, UiScreen, UiSimpleDropdown,
+ EventHandler, List, EventKey
+ )
+{
+ "use strict";
+
+ var _activeDialog = null;
+ var _callbackFocus = null;
+ var _container = null;
+ var _dialogs = new Dictionary();
+ var _dialogFullHeight = false;
+ var _dialogObjects = new ObjectMap();
+ var _dialogToObject = new Dictionary();
+ var _focusedBeforeDialog = null;
+ var _keyupListener = null;
+ var _staticDialogs = elByClass('jsStaticDialog');
+ var _validCallbacks = ['onBeforeClose', 'onClose', 'onShow'];
+
+ // list of supported `input[type]` values for dialog submit
+ var _validInputTypes = ['number', 'password', 'search', 'tel', 'text', 'url'];
+
+ var _focusableElements = [
+ 'a[href]:not([tabindex^="-"]):not([inert])',
+ 'area[href]:not([tabindex^="-"]):not([inert])',
+ 'input:not([disabled]):not([inert])',
+ 'select:not([disabled]):not([inert])',
+ 'textarea:not([disabled]):not([inert])',
+ 'button:not([disabled]):not([inert])',
+ 'iframe:not([tabindex^="-"]):not([inert])',
+ 'audio:not([tabindex^="-"]):not([inert])',
+ 'video:not([tabindex^="-"]):not([inert])',
+ '[contenteditable]:not([tabindex^="-"]):not([inert])',
+ '[tabindex]:not([tabindex^="-"]):not([inert])'
+ ];
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dialog
+ */
+ return {
+ /**
+ * Sets up global container and internal variables.
+ */
+ setup: function() {
+ // Fetch Ajax, as it cannot be provided because of a circular dependency
+ if (Ajax === undefined) Ajax = require('Ajax');
+
+ _container = elCreate('div');
+ _container.classList.add('dialogOverlay');
+ elAttr(_container, 'aria-hidden', 'true');
+ _container.addEventListener('mousedown', this._closeOnBackdrop.bind(this));
+ _container.addEventListener('wheel', function (event) {
+ if (event.target === _container) {
+ event.preventDefault();
+ }
+ }, { passive: false });
+
+ elById('content').appendChild(_container);
+
+ _keyupListener = (function(event) {
+ if (event.keyCode === 27) {
+ if (event.target.nodeName !== 'INPUT' && event.target.nodeName !== 'TEXTAREA') {
+ this.close(_activeDialog);
+
+ return false;
+ }
+ }
+
+ return true;
+ }).bind(this);
+
+ UiScreen.on('screen-xs', {
+ match: function() { _dialogFullHeight = true; },
+ unmatch: function() { _dialogFullHeight = false; },
+ setup: function() { _dialogFullHeight = true; }
+ });
+
+ this._initStaticDialogs();
+ DomChangeListener.add('Ui/Dialog', this._initStaticDialogs.bind(this));
+
+ UiScreen.setDialogContainer(_container);
+
+ // mobile safari dynamically shows/hides the bottom browser bar
+ // causing the window height to differ significantly
+ if (Environment.platform() === 'ios') {
+ window.addEventListener('resize', (function () {
+ _dialogs.forEach((function (dialog) {
+ if (!elAttrBool(dialog.dialog, 'aria-hidden')) {
+ this.rebuild(elData(dialog.dialog, 'id'));
+ }
+ }).bind(this));
+ }).bind(this));
+ }
+ },
+
+ _initStaticDialogs: function() {
+ var button, container, id;
+ while (_staticDialogs.length) {
+ button = _staticDialogs[0];
+ button.classList.remove('jsStaticDialog');
+
+ id = elData(button, 'dialog-id');
+ if (id && (container = elById(id))) {
+ ((function(button, container) {
+ container.classList.remove('jsStaticDialogContent');
+ elData(container, 'is-static-dialog', true);
+ elHide(container);
+ button.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+
+ this.openStatic(container.id, null, { title: elData(container, 'title') });
+ }).bind(this));
+ }).bind(this))(button, container);
+ }
+ }
+ },
+
+ /**
+ * Opens the dialog and implicitly creates it on first usage.
+ *
+ * @param {object} callbackObject used to invoke `_dialogSetup()` on first call
+ * @param {(string|DocumentFragment=} html html content or document fragment to use for dialog content
+ * @returns {object<string, *>} dialog data
+ */
+ open: function(callbackObject, html) {
+ var dialogData = _dialogObjects.get(callbackObject);
+ if (Core.isPlainObject(dialogData)) {
+ // dialog already exists
+ return this.openStatic(dialogData.id, html);
+ }
+
+ // initialize a new dialog
+ if (typeof callbackObject._dialogSetup !== 'function') {
+ throw new Error("Callback object does not implement the method '_dialogSetup()'.");
+ }
+
+ var setupData = callbackObject._dialogSetup();
+ if (!Core.isPlainObject(setupData)) {
+ throw new Error("Expected an object literal as return value of '_dialogSetup()'.");
+ }
+
+ dialogData = { id: setupData.id };
+
+ var createOnly = true;
+ if (setupData.source === undefined) {
+ var dialogElement = elById(setupData.id);
+ if (dialogElement === null) {
+ throw new Error("Element id '" + setupData.id + "' is invalid and no source attribute was given. If you want to use the `html` argument instead, please add `source: null` to your dialog configuration.");
+ }
+
+ setupData.source = document.createDocumentFragment();
+ setupData.source.appendChild(dialogElement);
+
+ // remove id and `display: none` from dialog element
+ dialogElement.removeAttribute('id');
+ elShow(dialogElement);
+ }
+ else if (setupData.source === null) {
+ // `null` means there is no static markup and `html` should be used instead
+ setupData.source = html;
+ }
+
+ else if (typeof setupData.source === 'function') {
+ setupData.source();
+ }
+ else if (Core.isPlainObject(setupData.source)) {
+ if (typeof html === 'string' && html.trim() !== '') {
+ setupData.source = html;
+ }
+ else {
+ Ajax.api(this, setupData.source.data, (function (data) {
+ if (data.returnValues && typeof data.returnValues.template === 'string') {
+ this.open(callbackObject, data.returnValues.template);
+
+ if (typeof setupData.source.after === 'function') {
+ setupData.source.after(_dialogs.get(setupData.id).content, data);
+ }
+ }
+ }).bind(this));
+
+ // deferred initialization
+ return {};
+ }
+ }
+ else {
+ if (typeof setupData.source === 'string') {
+ var dialogElement = elCreate('div');
+ elAttr(dialogElement, 'id', setupData.id);
+ DomUtil.setInnerHtml(dialogElement, setupData.source);
+
+ setupData.source = document.createDocumentFragment();
+ setupData.source.appendChild(dialogElement);
+ }
+
+ if (!setupData.source.nodeType || setupData.source.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
+ throw new Error("Expected at least a document fragment as 'source' attribute.");
+ }
+
+ createOnly = false;
+ }
+
+ _dialogObjects.set(callbackObject, dialogData);
+ _dialogToObject.set(setupData.id, callbackObject);
+
+ return this.openStatic(setupData.id, setupData.source, setupData.options, createOnly);
+ },
+
+ /**
+ * Opens an dialog, if the dialog is already open the content container
+ * will be replaced by the HTML string contained in the parameter html.
+ *
+ * If id is an existing element id, html will be ignored and the referenced
+ * element will be appended to the content element instead.
+ *
+ * @param {string} id element id, if exists the html parameter is ignored in favor of the existing element
+ * @param {?(string|DocumentFragment)} html content html
+ * @param {object<string, *>} options list of options, is completely ignored if the dialog already exists
+ * @param {boolean=} createOnly create the dialog but do not open it
+ * @return {object<string, *>} dialog data
+ */
+ openStatic: function(id, html, options, createOnly) {
+ UiScreen.pageOverlayOpen();
+
+ if (Environment.platform() !== 'desktop') {
+ if (!this.isOpen(id)) {
+ UiScreen.scrollDisable();
+ }
+ }
+
+ if (_dialogs.has(id)) {
+ this._updateDialog(id, html);
+ }
+ else {
+ options = Core.extend({
+ backdropCloseOnClick: true,
+ closable: true,
+ closeButtonLabel: Language.get('wcf.global.button.close'),
+ closeConfirmMessage: '',
+ disableContentPadding: false,
+ title: '',
+
+ // callbacks
+ onBeforeClose: null,
+ onClose: null,
+ onShow: null
+ }, options);
+
+ if (!options.closable) options.backdropCloseOnClick = false;
+ if (options.closeConfirmMessage) {
+ options.onBeforeClose = (function(id) {
+ UiConfirmation.show({
+ confirm: this.close.bind(this, id),
+ message: options.closeConfirmMessage
+ });
+ }).bind(this);
+ }
+
+ this._createDialog(id, html, options);
+ }
+
+ var data = _dialogs.get(id);
+
+ // iOS breaks `position: fixed` when input elements or `contenteditable`
+ // are focused, this will freeze the screen and force Safari to scroll
+ // to the input field
+ if (Environment.platform() === 'ios') {
+ window.setTimeout((function () {
+ var input = elBySel('input, textarea', data.content);
+ if (input !== null) {
+ input.focus();
+ }
+ }).bind(this), 200);
+ }
+
+ return data;
+ },
+
+ /**
+ * Sets the dialog title.
+ *
+ * @param {(string|object)} id element id
+ * @param {string} title dialog title
+ */
+ setTitle: function(id, title) {
+ id = this._getDialogId(id);
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ var dialogTitle = elByClass('dialogTitle', data.dialog);
+ if (dialogTitle.length) {
+ dialogTitle[0].textContent = title;
+ }
+ },
+
+ /**
+ * Sets a callback function on runtime.
+ *
+ * @param {(string|object)} id element id
+ * @param {string} key callback identifier
+ * @param {?function} value callback function or `null`
+ */
+ setCallback: function(id, key, value) {
+ if (typeof id === 'object') {
+ var dialogData = _dialogObjects.get(id);
+ if (dialogData !== undefined) {
+ id = dialogData.id;
+ }
+ }
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ if (_validCallbacks.indexOf(key) === -1) {
+ throw new Error("Invalid callback identifier, '" + key + "' is not recognized.");
+ }
+
+ if (typeof value !== 'function' && value !== null) {
+ throw new Error("Only functions or the 'null' value are acceptable callback values ('" + typeof value+ "' given).");
+ }
+
+ data[key] = value;
+ },
+
+ /**
+ * Creates the DOM for a new dialog and opens it.
+ *
+ * @param {string} id element id, if exists the html parameter is ignored in favor of the existing element
+ * @param {?(string|DocumentFragment)} html content html
+ * @param {object<string, *>} options list of options
+ * @param {boolean=} createOnly create the dialog but do not open it
+ */
+ _createDialog: function(id, html, options, createOnly) {
+ var element = null;
+ if (html === null) {
+ element = elById(id);
+ if (element === null) {
+ throw new Error("Expected either a HTML string or an existing element id.");
+ }
+ }
+
+ var dialog = elCreate('div');
+ dialog.classList.add('dialogContainer');
+ elAttr(dialog, 'aria-hidden', 'true');
+ elAttr(dialog, 'role', 'dialog');
+ elData(dialog, 'id', id);
+
+ var header = elCreate('header');
+ dialog.appendChild(header);
+
+ var titleId = DomUtil.getUniqueId();
+ elAttr(dialog, 'aria-labelledby', titleId);
+
+ var title = elCreate('span');
+ title.classList.add('dialogTitle');
+ title.textContent = options.title;
+ elAttr(title, 'id', titleId);
+ header.appendChild(title);
+
+ if (options.closable) {
+ var closeButton = elCreate('a');
+ closeButton.className = 'dialogCloseButton jsTooltip';
+ elAttr(closeButton, 'role', 'button');
+ elAttr(closeButton, 'tabindex', '0');
+ elAttr(closeButton, 'title', options.closeButtonLabel);
+ elAttr(closeButton, 'aria-label', options.closeButtonLabel);
+ closeButton.addEventListener(WCF_CLICK_EVENT, this._close.bind(this));
+ header.appendChild(closeButton);
+
+ var span = elCreate('span');
+ span.className = 'icon icon24 fa-times';
+ closeButton.appendChild(span);
+ }
+
+ var contentContainer = elCreate('div');
+ contentContainer.classList.add('dialogContent');
+ if (options.disableContentPadding) contentContainer.classList.add('dialogContentNoPadding');
+ dialog.appendChild(contentContainer);
+
+ contentContainer.addEventListener('wheel', function (event) {
+ var allowScroll = false;
+ var element = event.target, clientHeight, scrollHeight, scrollTop;
+ while (true) {
+ clientHeight = element.clientHeight;
+ scrollHeight = element.scrollHeight;
+
+ if (clientHeight < scrollHeight) {
+ scrollTop = element.scrollTop;
+
+ // negative value: scrolling up
+ if (event.deltaY < 0 && scrollTop > 0) {
+ allowScroll = true;
+ break;
+ }
+ else if (event.deltaY > 0 && (scrollTop + clientHeight < scrollHeight)) {
+ allowScroll = true;
+ break;
+ }
+ }
+
+ if (!element || element === contentContainer) {
+ break;
+ }
+
+ element = element.parentNode;
+ }
+
+ if (allowScroll === false) {
+ event.preventDefault();
+ }
+ }, { passive: false });
+
+ var content;
+ if (element === null) {
+ if (typeof html === 'string') {
+ content = elCreate('div');
+ content.id = id;
+ DomUtil.setInnerHtml(content, html);
+ }
+ else if (html instanceof DocumentFragment) {
+ var children = [], node;
+ for (var i = 0, length = html.childNodes.length; i < length; i++) {
+ node = html.childNodes[i];
+
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ children.push(node);
+ }
+ }
+
+ if (children[0].nodeName !== 'DIV' || children.length > 1) {
+ content = elCreate('div');
+ content.id = id;
+ content.appendChild(html);
+ }
+ else {
+ content = children[0];
+ }
+ }
+ else {
+ throw new TypeError("'html' must either be a string or a DocumentFragment");
+ }
+ }
+ else {
+ content = element;
+ }
+
+ contentContainer.appendChild(content);
+
+ if (content.style.getPropertyValue('display') === 'none') {
+ elShow(content);
+ }
+
+ _dialogs.set(id, {
+ backdropCloseOnClick: options.backdropCloseOnClick,
+ closable: options.closable,
+ content: content,
+ dialog: dialog,
+ header: header,
+ onBeforeClose: options.onBeforeClose,
+ onClose: options.onClose,
+ onShow: options.onShow,
+
+ submitButton: null,
+ inputFields: new List()
+ });
+
+ DomUtil.prepend(dialog, _container);
+
+ if (typeof options.onSetup === 'function') {
+ options.onSetup(content);
+ }
+
+ if (createOnly !== true) {
+ this._updateDialog(id, null);
+ }
+ },
+
+ /**
+ * Updates the dialog's content element.
+ *
+ * @param {string} id element id
+ * @param {?string} html content html, prevent changes by passing null
+ */
+ _updateDialog: function(id, html) {
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ if (typeof html === 'string') {
+ DomUtil.setInnerHtml(data.content, html);
+ }
+
+ if (elAttr(data.dialog, 'aria-hidden') === 'true') {
+ if (_callbackFocus === null) {
+ _callbackFocus = this._maintainFocus.bind(this);
+ document.body.addEventListener('focus', _callbackFocus, { capture: true });
+ }
+
+ if (data.closable && elAttr(_container, 'aria-hidden') === 'true') {
+ window.addEventListener('keyup', _keyupListener);
+ }
+
+ elAttr(data.dialog, 'aria-hidden', 'false');
+ elAttr(_container, 'aria-hidden', 'false');
+ elData(_container, 'close-on-click', (data.backdropCloseOnClick ? 'true' : 'false'));
+ _activeDialog = id;
+
+ // Keep a reference to the currently focused element to be able to restore it later.
+ _focusedBeforeDialog = document.activeElement;
+
+ // Set the focus to the first focusable child of the dialog element.
+ var closeButton = elBySel('.dialogCloseButton', data.header);
+ if (closeButton) elAttr(closeButton, 'inert', true);
+ this._setFocusToFirstItem(data.dialog);
+ if (closeButton) closeButton.removeAttribute('inert');
+
+ if (typeof data.onShow === 'function') {
+ data.onShow(data.content);
+ }
+
+ if (elDataBool(data.content, 'is-static-dialog')) {
+ EventHandler.fire('com.woltlab.wcf.dialog', 'openStatic', {
+ content: data.content,
+ id: id
+ });
+ }
+
+ // close existing dropdowns
+ UiSimpleDropdown.closeAll();
+ window.WCF.Dropdown.Interactive.Handler.closeAll();
+ }
+
+ this.rebuild(id);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * @param {Event} event
+ */
+ _maintainFocus: function(event) {
+ if (_activeDialog) {
+ var data = _dialogs.get(_activeDialog);
+ if (!data.dialog.contains(event.target) && !event.target.closest('.dropdownMenuContainer') && !event.target.closest('.datePicker')) {
+ this._setFocusToFirstItem(data.dialog, true);
+ }
+ }
+ },
+
+ /**
+ * @param {Element} dialog
+ * @param {boolean} maintain
+ */
+ _setFocusToFirstItem: function(dialog, maintain) {
+ var focusElement = this._getFirstFocusableChild(dialog);
+ if (focusElement !== null) {
+ if (maintain) {
+ if (focusElement.id === 'username' || focusElement.name === 'username') {
+ if (Environment.browser() === 'safari' && Environment.platform() === 'ios') {
+ // iOS Safari's username/password autofill breaks if the input field is focused
+ focusElement = null;
+ }
+ }
+ }
+
+ if (focusElement) focusElement.focus();
+ }
+ },
+
+ /**
+ * @param {Element} node
+ * @returns {?Element}
+ */
+ _getFirstFocusableChild: function(node) {
+ var nodeList = elBySelAll(_focusableElements.join(','), node);
+ for (var i = 0, length = nodeList.length; i < length; i++) {
+ if (nodeList[i].offsetWidth && nodeList[i].offsetHeight && nodeList[i].getClientRects().length) {
+ return nodeList[i];
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Rebuilds dialog identified by given id.
+ *
+ * @param {string} id element id
+ */
+ rebuild: function(id) {
+ id = this._getDialogId(id);
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ // ignore non-active dialogs
+ if (elAttr(data.dialog, 'aria-hidden') === 'true') {
+ return;
+ }
+
+ var contentContainer = data.content.parentNode;
+
+ var formSubmit = elBySel('.formSubmit', data.content);
+ var unavailableHeight = 0;
+ if (formSubmit !== null) {
+ contentContainer.classList.add('dialogForm');
+ formSubmit.classList.add('dialogFormSubmit');
+
+ unavailableHeight += DomUtil.outerHeight(formSubmit);
+
+ // Calculated height can be a fractional value and depending on the
+ // browser the results can vary. By subtracting a single pixel we're
+ // working around fractional values, without visually changing anything.
+ unavailableHeight -= 1;
+
+ contentContainer.style.setProperty('margin-bottom', unavailableHeight + 'px', '');
+ }
+ else {
+ contentContainer.classList.remove('dialogForm');
+ contentContainer.style.removeProperty('margin-bottom');
+ }
+
+ unavailableHeight += DomUtil.outerHeight(data.header);
+
+ var maximumHeight = (window.innerHeight * (_dialogFullHeight ? 1 : 0.8)) - unavailableHeight;
+ contentContainer.style.setProperty('max-height', ~~maximumHeight + 'px', '');
+
+ // Chrome and Safari use heavy anti-aliasing when the dialog's width
+ // cannot be evenly divided, causing the whole text to become blurry
+ if (Environment.browser() === 'chrome' || Environment.browser() === 'safari') {
+ // `clientWidth` will report an integer value that isn't rounded properly (e.g. 0.59 -> 0)
+ var floatWidth = parseFloat(window.getComputedStyle(data.content).width);
+ var needsFix = (Math.round(floatWidth) % 2) !== 0;
+
+ data.content.parentNode.classList[(needsFix ? 'add' : 'remove')]('jsWebKitFractionalPixel');
+ }
+
+ var callbackObject = _dialogToObject.get(id);
+ //noinspection JSUnresolvedVariable
+ if (callbackObject !== undefined && typeof callbackObject._dialogSubmit === 'function') {
+ var inputFields = elBySelAll('input[data-dialog-submit-on-enter="true"]', data.content);
+
+ var submitButton = elBySel('.formSubmit > input[type="submit"], .formSubmit > button[data-type="submit"]', data.content);
+ if (submitButton === null) {
+ // check if there is at least one input field with submit handling,
+ // otherwise we'll assume the dialog has not been populated yet
+ if (inputFields.length === 0) {
+ console.warn("Broken dialog, expected a submit button.", data.content);
+ }
+
+ return;
+ }
+
+ if (data.submitButton !== submitButton) {
+ data.submitButton = submitButton;
+
+ submitButton.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ this._submit(id);
+ }).bind(this));
+
+ // bind input fields
+ var inputField, _callbackKeydown = null;
+ for (var i = 0, length = inputFields.length; i < length; i++) {
+ inputField = inputFields[i];
+
+ if (data.inputFields.has(inputField)) continue;
+
+ if (_validInputTypes.indexOf(inputField.type) === -1) {
+ console.warn("Unsupported input type.", inputField);
+ continue;
+ }
+
+ data.inputFields.add(inputField);
+
+ if (_callbackKeydown === null) {
+ _callbackKeydown = (function (event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ this._submit(id);
+ }
+ }).bind(this);
+ }
+ inputField.addEventListener('keydown', _callbackKeydown);
+ }
+ }
+ }
+ },
+
+ /**
+ * Submits the dialog.
+ *
+ * @param {string} id dialog id
+ * @protected
+ */
+ _submit: function (id) {
+ var data = _dialogs.get(id);
+
+ var isValid = true;
+ data.inputFields.forEach(function (inputField) {
+ if (inputField.required) {
+ if (inputField.value.trim() === '') {
+ elInnerError(inputField, Language.get('wcf.global.form.error.empty'));
+
+ isValid = false;
+ }
+ else {
+ elInnerError(inputField, false);
+ }
+ }
+ });
+
+ if (isValid) {
+ //noinspection JSUnresolvedFunction
+ _dialogToObject.get(id)._dialogSubmit();
+ }
+ },
+
+ /**
+ * Handles clicks on the close button or the backdrop if enabled.
+ *
+ * @param {object} event click event
+ * @return {boolean} false if the event should be cancelled
+ */
+ _close: function(event) {
+ event.preventDefault();
+
+ var data = _dialogs.get(_activeDialog);
+ if (typeof data.onBeforeClose === 'function') {
+ data.onBeforeClose(_activeDialog);
+
+ return false;
+ }
+
+ this.close(_activeDialog);
+ },
+
+ /**
+ * Closes the current active dialog by clicks on the backdrop.
+ *
+ * @param {object} event event object
+ */
+ _closeOnBackdrop: function(event) {
+ if (event.target !== _container) {
+ return true;
+ }
+
+ if (elData(_container, 'close-on-click') === 'true') {
+ this._close(event);
+ }
+ else {
+ event.preventDefault();
+ }
+ },
+
+ /**
+ * Closes a dialog identified by given id.
+ *
+ * @param {(string|object)} id element id or callback object
+ */
+ close: function(id) {
+ id = this._getDialogId(id);
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ elAttr(data.dialog, 'aria-hidden', 'true');
+
+ // avoid keyboard focus on a now hidden element
+ if (document.activeElement.closest('.dialogContainer') === data.dialog) {
+ document.activeElement.blur();
+ }
+
+ if (typeof data.onClose === 'function') {
+ data.onClose(id);
+ }
+
+ // get next active dialog
+ _activeDialog = null;
+ for (var i = 0; i < _container.childElementCount; i++) {
+ var child = _container.children[i];
+ if (elAttr(child, 'aria-hidden') === 'false') {
+ _activeDialog = elData(child, 'id');
+ break;
+ }
+ }
+
+ if (_activeDialog === null) {
+ elAttr(_container, 'aria-hidden', 'true');
+ elData(_container, 'close-on-click', 'false');
+
+ if (data.closable) {
+ window.removeEventListener('keyup', _keyupListener);
+ }
+
+ UiScreen.pageOverlayClose();
+ }
+ else {
+ data = _dialogs.get(_activeDialog);
+ elData(_container, 'close-on-click', (data.backdropCloseOnClick ? 'true' : 'false'));
+ }
+
+ if (Environment.platform() !== 'desktop') {
+ UiScreen.scrollEnable();
+ }
+ },
+
+ /**
+ * Returns the dialog data for given element id.
+ *
+ * @param {(string|object)} id element id or callback object
+ * @return {(object|undefined)} dialog data or undefined if element id is unknown
+ */
+ getDialog: function(id) {
+ return _dialogs.get(this._getDialogId(id));
+ },
+
+ /**
+ * Returns true for open dialogs.
+ *
+ * @param {(string|object)} id element id or callback object
+ * @return {boolean}
+ */
+ isOpen: function(id) {
+ var data = this.getDialog(id);
+ return (data !== undefined && elAttr(data.dialog, 'aria-hidden') === 'false');
+ },
+
+ /**
+ * Destroys a dialog instance.
+ *
+ * @param {Object} callbackObject the same object that was used to invoke `_dialogSetup()` on first call
+ */
+ destroy: function(callbackObject) {
+ if (typeof callbackObject !== 'object' || callbackObject instanceof String) {
+ throw new TypeError("Expected the callback object as parameter.");
+ }
+
+ if (_dialogObjects.has(callbackObject)) {
+ var id = _dialogObjects.get(callbackObject).id;
+ if (this.isOpen(id)) {
+ this.close(id);
+ }
+
+ _dialogs.delete(id);
+ _dialogObjects.delete(callbackObject);
+ }
+ },
+
+ /**
+ * Returns a dialog's id.
+ *
+ * @param {(string|object)} id element id or callback object
+ * @return {string}
+ * @protected
+ */
+ _getDialogId: function(id) {
+ if (typeof id === 'object') {
+ var dialogData = _dialogObjects.get(id);
+ if (dialogData !== undefined) {
+ return dialogData.id;
+ }
+ }
+
+ return id.toString();
+ },
+
+ _ajaxSetup: function() {
+ return {};
+ }
+ };
+});
+
+/**
+ * Provides the AJAX status overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax/Status
+ */
+define('WoltLabSuite/Core/Ajax/Status',['Language'], function(Language) {
+ "use strict";
+
+ var _activeRequests = 0;
+ var _overlay = null;
+ var _timeoutShow = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ajax/Status
+ */
+ var AjaxStatus = {
+ /**
+ * Initializes the status overlay on first usage.
+ */
+ _init: function() {
+ _overlay = elCreate('div');
+ _overlay.classList.add('spinner');
+ elAttr(_overlay, 'role', 'status');
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ _overlay.appendChild(icon);
+
+ var title = elCreate('span');
+ title.textContent = Language.get('wcf.global.loading');
+ _overlay.appendChild(title);
+
+ document.body.appendChild(_overlay);
+ },
+
+ /**
+ * Shows the loading overlay.
+ */
+ show: function() {
+ if (_overlay === null) {
+ this._init();
+ }
+
+ _activeRequests++;
+
+ if (_timeoutShow === null) {
+ _timeoutShow = window.setTimeout(function() {
+ if (_activeRequests) {
+ _overlay.classList.add('active');
+ }
+
+ _timeoutShow = null;
+ }, 250);
+ }
+ },
+
+ /**
+ * Hides the loading overlay.
+ */
+ hide: function() {
+ _activeRequests--;
+
+ if (_activeRequests === 0) {
+ if (_timeoutShow !== null) {
+ window.clearTimeout(_timeoutShow);
+ }
+
+ _overlay.classList.remove('active');
+ }
+ }
+ };
+
+ return AjaxStatus;
+});
+
+/**
+ * Versatile AJAX request handling.
+ *
+ * In case you want to issue JSONP requests, please use `AjaxJsonp` instead.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax/Request
+ */
+define('WoltLabSuite/Core/Ajax/Request',['Core', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Ui/Dialog', 'WoltLabSuite/Core/Ajax/Status'], function(Core, Language, DomChangeListener, DomUtil, UiDialog, AjaxStatus) {
+ "use strict";
+
+ var _didInit = false;
+ var _ignoreAllErrors = false;
+
+ /**
+ * @constructor
+ */
+ function AjaxRequest(options) {
+ this._data = null;
+ this._options = {};
+ this._previousXhr = null;
+ this._xhr = null;
+
+ this._init(options);
+ }
+ AjaxRequest.prototype = {
+ /**
+ * Initializes the request options.
+ *
+ * @param {Object} options request options
+ */
+ _init: function(options) {
+ this._options = Core.extend({
+ // request data
+ data: {},
+ contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
+ responseType: 'application/json',
+ type: 'POST',
+ url: '',
+ withCredentials: false,
+
+ // behavior
+ autoAbort: false,
+ ignoreError: false,
+ pinData: false,
+ silent: false,
+ includeRequestedWith: true,
+
+ // callbacks
+ failure: null,
+ finalize: null,
+ success: null,
+ progress: null,
+ uploadProgress: null,
+
+ callbackObject: null
+ }, options);
+
+ if (typeof options.callbackObject === 'object') {
+ this._options.callbackObject = options.callbackObject;
+ }
+
+ this._options.url = Core.convertLegacyUrl(this._options.url);
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
+ }
+
+ if (this._options.url.indexOf(WSC_API_URL) === 0) {
+ this._options.includeRequestedWith = true;
+ // always include credentials when querying the very own server
+ this._options.withCredentials = true;
+ }
+
+ if (this._options.pinData) {
+ this._data = Core.extend({}, this._options.data);
+ }
+
+ if (this._options.callbackObject !== null) {
+ if (typeof this._options.callbackObject._ajaxFailure === 'function') this._options.failure = this._options.callbackObject._ajaxFailure.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxFinalize === 'function') this._options.finalize = this._options.callbackObject._ajaxFinalize.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxSuccess === 'function') this._options.success = this._options.callbackObject._ajaxSuccess.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxProgress === 'function') this._options.progress = this._options.callbackObject._ajaxProgress.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxUploadProgress === 'function') this._options.uploadProgress = this._options.callbackObject._ajaxUploadProgress.bind(this._options.callbackObject);
+ }
+
+ if (_didInit === false) {
+ _didInit = true;
+
+ window.addEventListener('beforeunload', function() { _ignoreAllErrors = true; });
+ }
+ },
+
+ /**
+ * Dispatches a request, optionally aborting a currently active request.
+ *
+ * @param {boolean} abortPrevious abort currently active request
+ */
+ sendRequest: function(abortPrevious) {
+ if (abortPrevious === true || this._options.autoAbort) {
+ this.abortPrevious();
+ }
+
+ if (!this._options.silent) {
+ AjaxStatus.show();
+ }
+
+ if (this._xhr instanceof XMLHttpRequest) {
+ this._previousXhr = this._xhr;
+ }
+
+ this._xhr = new XMLHttpRequest();
+ this._xhr.open(this._options.type, this._options.url, true);
+ if (this._options.contentType) {
+ this._xhr.setRequestHeader('Content-Type', this._options.contentType);
+ }
+ if (this._options.withCredentials || this._options.includeRequestedWith) {
+ this._xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ }
+ if (this._options.withCredentials) {
+ this._xhr.withCredentials = true;
+ }
+
+ var self = this;
+ var options = Core.clone(this._options);
+ this._xhr.onload = function() {
+ if (this.readyState === XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300 || this.status === 304) {
+ if (options.responseType && this.getResponseHeader('Content-Type').indexOf(options.responseType) !== 0) {
+ // request succeeded but invalid response type
+ self._failure(this, options);
+ }
+ else {
+ self._success(this, options);
+ }
+ }
+ else {
+ self._failure(this, options);
+ }
+ }
+ };
+ this._xhr.onerror = function() {
+ self._failure(this, options);
+ };
+
+ if (this._options.progress) {
+ this._xhr.onprogress = this._options.progress;
+ }
+ if (this._options.uploadProgress) {
+ this._xhr.upload.onprogress = this._options.uploadProgress;
+ }
+
+ if (this._options.type === 'POST') {
+ var data = this._options.data;
+ if (typeof data === 'object' && Core.getType(data) !== 'FormData') {
+ data = Core.serialize(data);
+ }
+
+ this._xhr.send(data);
+ }
+ else {
+ this._xhr.send();
+ }
+ },
+
+ /**
+ * Aborts a previous request.
+ */
+ abortPrevious: function() {
+ if (this._previousXhr === null) {
+ return;
+ }
+
+ this._previousXhr.abort();
+ this._previousXhr = null;
+
+ if (!this._options.silent) {
+ AjaxStatus.hide();
+ }
+ },
+
+ /**
+ * Sets a specific option.
+ *
+ * @param {string} key option name
+ * @param {?} value option value
+ */
+ setOption: function(key, value) {
+ this._options[key] = value;
+ },
+
+ /**
+ * Returns an option by key or undefined.
+ *
+ * @param {string} key option name
+ * @return {(*|null)} option value or null
+ */
+ getOption: function(key) {
+ if (objOwns(this._options, key)) {
+ return this._options[key];
+ }
+
+ return null;
+ },
+
+ /**
+ * Sets request data while honoring pinned data from setup callback.
+ *
+ * @param {Object} data request data
+ */
+ setData: function(data) {
+ if (this._data !== null && Core.getType(data) !== 'FormData') {
+ data = Core.extend(this._data, data);
+ }
+
+ this._options.data = data;
+ },
+
+ /**
+ * Handles a successful request.
+ *
+ * @param {XMLHttpRequest} xhr request object
+ * @param {Object} options request options
+ */
+ _success: function(xhr, options) {
+ if (!options.silent) {
+ AjaxStatus.hide();
+ }
+
+ if (typeof options.success === 'function') {
+ var data = null;
+ if (xhr.getResponseHeader('Content-Type') === 'application/json') {
+ try {
+ data = JSON.parse(xhr.responseText);
+ }
+ catch (e) {
+ // invalid JSON
+ this._failure(xhr, options);
+
+ return;
+ }
+
+ // trim HTML before processing, see http://jquery.com/upgrade-guide/1.9/#jquery-htmlstring-versus-jquery-selectorstring
+ if (data && data.returnValues && data.returnValues.template !== undefined) {
+ data.returnValues.template = data.returnValues.template.trim();
+ }
+
+ // force-invoke the background queue
+ if (data && data.forceBackgroundQueuePerform) {
+ require(['WoltLabSuite/Core/BackgroundQueue'], function(BackgroundQueue) {
+ BackgroundQueue.invoke();
+ });
+ }
+ }
+
+ options.success(data, xhr.responseText, xhr, options.data);
+ }
+
+ this._finalize(options);
+ },
+
+ /**
+ * Handles failed requests, this can be both a successful request with
+ * a non-success status code or an entirely failed request.
+ *
+ * @param {XMLHttpRequest} xhr request object
+ * @param {Object} options request options
+ */
+ _failure: function (xhr, options) {
+ if (_ignoreAllErrors) {
+ return;
+ }
+
+ if (!options.silent) {
+ AjaxStatus.hide();
+ }
+
+ var data = null;
+ try {
+ data = JSON.parse(xhr.responseText);
+ }
+ catch (e) {}
+
+ var showError = true;
+ if (typeof options.failure === 'function') {
+ showError = options.failure((data || {}), (xhr.responseText || ''), xhr, options.data);
+ }
+
+ if (options.ignoreError !== true && showError !== false) {
+ var html = this.getErrorHtml(data, xhr);
+
+ if (html) {
+ if (UiDialog === undefined) UiDialog = require('Ui/Dialog');
+ UiDialog.openStatic(DomUtil.getUniqueId(), html, {
+ title: Language.get('wcf.global.error.title')
+ });
+ }
+ }
+
+ this._finalize(options);
+ },
+
+ /**
+ * Returns the inner HTML for an error/exception display.
+ *
+ * @param {Object} data
+ * @param {XMLHttpRequest} xhr
+ * @return {string}
+ */
+ getErrorHtml: function(data, xhr) {
+ var details = '';
+ var message = '';
+
+ if (data !== null) {
+ if (data.file && data.line) {
+ details += '<br><p>File:</p><p>' + data.file + ' in line ' + data.line + '</p>';
+ }
+
+ if (data.stacktrace) details += '<br><p>Stacktrace:</p><p>' + data.stacktrace + '</p>';
+ else if (data.exceptionID) details += '<br><p>Exception ID: <code>' + data.exceptionID + '</code></p>';
+
+ message = data.message;
+
+ data.previous.forEach(function(previous) {
+ details += '<hr><p>' + previous.message + '</p>';
+ details += '<br><p>Stacktrace</p><p>' + previous.stacktrace + '</p>';
+ });
+ }
+ else {
+ message = xhr.responseText;
+ }
+
+ if (!message || message === 'undefined') {
+ if (!ENABLE_DEBUG_MODE && !ENABLE_PRODUCTION_DEBUG_MODE) return null;
+
+ message = 'XMLHttpRequest failed without a responseText. Check your browser console.'
+ }
+
+ return '<div class="ajaxDebugMessage"><p>' + message + '</p>' + details + '</div>';
+ },
+
+ /**
+ * Finalizes a request.
+ *
+ * @param {Object} options request options
+ */
+ _finalize: function(options) {
+ if (typeof options.finalize === 'function') {
+ options.finalize(this._xhr);
+ }
+
+ this._previousXhr = null;
+
+ DomChangeListener.trigger();
+
+ // fix anchor tags generated through WCF::getAnchor()
+ var links = elBySelAll('a[href*="#"]');
+ for (var i = 0, length = links.length; i < length; i++) {
+ var link = links[i];
+ var href = elAttr(link, 'href');
+ if (href.indexOf('AJAXProxy') !== -1 || href.indexOf('ajax-proxy') !== -1) {
+ href = href.substr(href.indexOf('#'));
+ elAttr(link, 'href', document.location.toString().replace(/#.*/, '') + href);
+ }
+ }
+ }
+ };
+
+ return AjaxRequest;
+});
+
+/**
+ * Handles AJAX requests.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax
+ */
+define('WoltLabSuite/Core/Ajax',['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectMap) {
+ "use strict";
+
+ var _requests = new ObjectMap();
+
+ /**
+ * @exports WoltLabSuite/Core/Ajax
+ */
+ return {
+ /**
+ * Shorthand function to perform a request against the WCF-API with overrides
+ * for success and failure callbacks.
+ *
+ * @param {object} callbackObject callback object
+ * @param {object<string, *>=} data request data
+ * @param {function=} success success callback
+ * @param {function=} failure failure callback
+ * @return {AjaxRequest}
+ */
+ api: function(callbackObject, data, success, failure) {
+ // Fetch AjaxRequest, as it cannot be provided because of a circular dependency
+ if (AjaxRequest === undefined) AjaxRequest = require('AjaxRequest');
+
+ if (typeof data !== 'object') data = {};
+
+ var request = _requests.get(callbackObject);
+ if (request === undefined) {
+ if (typeof callbackObject._ajaxSetup !== 'function') {
+ throw new TypeError("Callback object must implement at least _ajaxSetup().");
+ }
+
+ var options = callbackObject._ajaxSetup();
+
+ options.pinData = true;
+ options.callbackObject = callbackObject;
+
+ if (!options.url) {
+ options.url = 'index.php?ajax-proxy/&t=' + SECURITY_TOKEN;
+ options.withCredentials = true;
+ }
+
+ request = new AjaxRequest(options);
+
+ _requests.set(callbackObject, request);
+ }
+
+ var oldSuccess = null;
+ var oldFailure = null;
+
+ if (typeof success === 'function') {
+ oldSuccess = request.getOption('success');
+ request.setOption('success', success);
+ }
+ if (typeof failure === 'function') {
+ oldFailure = request.getOption('failure');
+ request.setOption('failure', failure);
+ }
+
+ request.setData(data);
+ request.sendRequest();
+
+ // restore callbacks
+ if (oldSuccess !== null) request.setOption('success', oldSuccess);
+ if (oldFailure !== null) request.setOption('failure', oldFailure);
+
+ return request;
+ },
+
+ /**
+ * Shorthand function to perform a single request against the WCF-API.
+ *
+ * Please use `Ajax.api` if you're about to repeatedly send requests because this
+ * method will spawn an new and rather expensive `AjaxRequest` with each call.
+ *
+ * @param {object<string, *>} options request options
+ */
+ apiOnce: function(options) {
+ // Fetch AjaxRequest, as it cannot be provided because of a circular dependency
+ if (AjaxRequest === undefined) AjaxRequest = require('AjaxRequest');
+
+ options.pinData = false;
+ options.callbackObject = null;
+ if (!options.url) {
+ options.url = 'index.php?ajax-proxy/&t=' + SECURITY_TOKEN;
+ options.withCredentials = true;
+ }
+
+ var request = new AjaxRequest(options);
+ request.sendRequest(false);
+ },
+
+ /**
+ * Returns the request object used for an earlier call to `api()`.
+ *
+ * @param {Object} callbackObject callback object
+ * @return {AjaxRequest}
+ */
+ getRequestObject: function(callbackObject) {
+ if (!_requests.has(callbackObject)) {
+ throw new Error('Expected a previously used callback object, provided object is unknown.');
+ }
+
+ return _requests.get(callbackObject);
+ }
+ };
+});
+
+/**
+ * Manages the invocation of the background queue.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/BackgroundQueue
+ */
+define('WoltLabSuite/Core/BackgroundQueue',['Ajax'], function(Ajax) {
+ "use strict";
+
+ var _invocations = 0;
+ var _isBusy = false;
+ var _url = '';
+
+ /**
+ * @exports WoltLabSuite/Core/BackgroundQueue
+ */
+ return {
+ /**
+ * Sets the url of the background queue perform action.
+ *
+ * @param {string} url background queue perform url
+ */
+ setUrl: function (url) {
+ _url = url;
+ },
+
+ /**
+ * Invokes the background queue.
+ */
+ invoke: function () {
+ if (_url === '') {
+ console.error('The background queue has not been initialized yet.');
+ return;
+ }
+
+ if (_isBusy) return;
+
+ _isBusy = true;
+
+ Ajax.api(this);
+ },
+
+ _ajaxSuccess: function (data) {
+ _invocations++;
+
+ // invoke the queue up to 5 times in a row
+ if (data > 0 && _invocations < 5) {
+ window.setTimeout(function () {
+ _isBusy = false;
+ this.invoke();
+ }.bind(this), 1000);
+ }
+ else {
+ _isBusy = false;
+ _invocations = 0;
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ url: _url,
+ ignoreError: true,
+ silent: true
+ }
+ }
+ }
+});
+
+/**
+ * @license MIT or GPL-2.0
+ * @fileOverview Favico animations
+ * @author Miroslav Magda, http://blog.ejci.net
+ * @source: https://github.com/ejci/favico.js
+ * @version 0.3.10
+ */
+
+/**
+ * Create new favico instance
+ * @param {Object} Options
+ * @return {Object} Favico object
+ * @example
+ * var favico = new Favico({
+ * bgColor : '#d00',
+ * textColor : '#fff',
+ * fontFamily : 'sans-serif',
+ * fontStyle : 'bold',
+ * type : 'circle',
+ * position : 'down',
+ * animation : 'slide',
+ * elementId: false,
+ * element: null,
+ * dataUrl: function(url){},
+ * win: window
+ * });
+ */
+(function () {
+
+ var Favico = (function (opt) {
+ 'use strict';
+ opt = (opt) ? opt : {};
+ var _def = {
+ bgColor: '#d00',
+ textColor: '#fff',
+ fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,...
+ fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900
+ type: 'circle',
+ position: 'down', // down, up, left, leftup (upleft)
+ animation: 'slide',
+ elementId: false,
+ element: null,
+ dataUrl: false,
+ win: window
+ };
+ var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc;
+
+ _browser = {};
+ _browser.ff = typeof InstallTrigger != 'undefined';
+ _browser.chrome = !!window.chrome;
+ _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0;
+ _browser.ie = /*@cc_on!@*/false;
+ _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
+ _browser.supported = (_browser.chrome || _browser.ff || _browser.opera);
+
+ var _queue = [];
+ _readyCb = function () {
+ };
+ _ready = _stop = false;
+ /**
+ * Initialize favico
+ */
+ var init = function () {
+ //merge initial options
+ _opt = merge(_def, opt);
+ _opt.bgColor = hexToRgb(_opt.bgColor);
+ _opt.textColor = hexToRgb(_opt.textColor);
+ _opt.position = _opt.position.toLowerCase();
+ _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation;
+
+ _doc = _opt.win.document;
+
+ var isUp = _opt.position.indexOf('up') > -1;
+ var isLeft = _opt.position.indexOf('left') > -1;
+
+ //transform the animations
+ if (isUp || isLeft) {
+ for (var a in animation.types) {
+ for (var i = 0; i < animation.types[a].length; i++) {
+ var step = animation.types[a][i];
+
+ if (isUp) {
+ if (step.y < 0.6) {
+ step.y = step.y - 0.4;
+ } else {
+ step.y = step.y - 2 * step.y + (1 - step.w);
+ }
+ }
+
+ if (isLeft) {
+ if (step.x < 0.6) {
+ step.x = step.x - 0.4;
+ } else {
+ step.x = step.x - 2 * step.x + (1 - step.h);
+ }
+ }
+
+ animation.types[a][i] = step;
+ }
+ }
+ }
+ _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type;
+
+ _orig = link. getIcons();
+ //create temp canvas
+ _canvas = document.createElement('canvas');
+ //create temp image
+ _img = document.createElement('img');
+ var lastIcon = _orig[_orig.length - 1];
+ if (lastIcon.hasAttribute('href')) {
+ _img.setAttribute('crossOrigin', 'anonymous');
+ //get width/height
+ _img.onload = function () {
+ _h = (_img.height > 0) ? _img.height : 32;
+ _w = (_img.width > 0) ? _img.width : 32;
+ _canvas.height = _h;
+ _canvas.width = _w;
+ _context = _canvas.getContext('2d');
+ icon.ready();
+ };
+ _img.setAttribute('src', lastIcon.getAttribute('href'));
+ } else {
+ _h = 32;
+ _w = 32;
+ _img.height = _h;
+ _img.width = _w;
+ _canvas.height = _h;
+ _canvas.width = _w;
+ _context = _canvas.getContext('2d');
+ icon.ready();
+ }
+
+ };
+ /**
+ * Icon namespace
+ */
+ var icon = {};
+ /**
+ * Icon is ready (reset icon) and start animation (if ther is any)
+ */
+ icon.ready = function () {
+ _ready = true;
+ icon.reset();
+ _readyCb();
+ };
+ /**
+ * Reset icon to default state
+ */
+ icon.reset = function () {
+ //reset
+ if (!_ready) {
+ return;
+ }
+ _queue = [];
+ _lastBadge = false;
+ _running = false;
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(_img, 0, 0, _w, _h);
+ //_stop=true;
+ link.setIcon(_canvas);
+ //webcam('stop');
+ //video('stop');
+ window.clearTimeout(_animTimeout);
+ window.clearTimeout(_drawTimeout);
+ };
+ /**
+ * Start animation
+ */
+ icon.start = function () {
+ if (!_ready || _running) {
+ return;
+ }
+ var finished = function () {
+ _lastBadge = _queue[0];
+ _running = false;
+ if (_queue.length > 0) {
+ _queue.shift();
+ icon.start();
+ } else {
+
+ }
+ };
+ if (_queue.length > 0) {
+ _running = true;
+ var run = function () {
+ // apply options for this animation
+ ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function (a) {
+ if (a in _queue[0].options) {
+ _opt[a] = _queue[0].options[a];
+ }
+ });
+ animation.run(_queue[0].options, function () {
+ finished();
+ }, false);
+ };
+ if (_lastBadge) {
+ animation.run(_lastBadge.options, function () {
+ run();
+ }, true);
+ } else {
+ run();
+ }
+ }
+ };
+
+ /**
+ * Badge types
+ */
+ var type = {};
+ var options = function (opt) {
+ opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n;
+ opt.x = _w * opt.x;
+ opt.y = _h * opt.y;
+ opt.w = _w * opt.w;
+ opt.h = _h * opt.h;
+ opt.len = ("" + opt.n).length;
+ return opt;
+ };
+ /**
+ * Generate circle
+ * @param {Object} opt Badge options
+ */
+ type.circle = function (opt) {
+ opt = options(opt);
+ var more = false;
+ if (opt.len === 2) {
+ opt.x = opt.x - opt.w * 0.4;
+ opt.w = opt.w * 1.4;
+ more = true;
+ } else if (opt.len >= 3) {
+ opt.x = opt.x - opt.w * 0.65;
+ opt.w = opt.w * 1.65;
+ more = true;
+ }
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(_img, 0, 0, _w, _h);
+ _context.beginPath();
+ _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px " + _opt.fontFamily;
+ _context.textAlign = 'center';
+ if (more) {
+ _context.moveTo(opt.x + opt.w / 2, opt.y);
+ _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y);
+ _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2);
+ _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2);
+ _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h);
+ _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h);
+ _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2);
+ _context.lineTo(opt.x, opt.y + opt.h / 2);
+ _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y);
+ } else {
+ _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI);
+ }
+ _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
+ _context.fill();
+ _context.closePath();
+ _context.beginPath();
+ _context.stroke();
+ _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
+ //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ if ((typeof opt.n) === 'number' && opt.n > 999) {
+ _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
+ } else {
+ _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ }
+ _context.closePath();
+ };
+ /**
+ * Generate rectangle
+ * @param {Object} opt Badge options
+ */
+ type.rectangle = function (opt) {
+ opt = options(opt);
+ var more = false;
+ if (opt.len === 2) {
+ opt.x = opt.x - opt.w * 0.4;
+ opt.w = opt.w * 1.4;
+ more = true;
+ } else if (opt.len >= 3) {
+ opt.x = opt.x - opt.w * 0.65;
+ opt.w = opt.w * 1.65;
+ more = true;
+ }
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(_img, 0, 0, _w, _h);
+ _context.beginPath();
+ _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + "px " + _opt.fontFamily;
+ _context.textAlign = 'center';
+ _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
+ _context.fillRect(opt.x, opt.y, opt.w, opt.h);
+ _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
+ //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ if ((typeof opt.n) === 'number' && opt.n > 999) {
+ _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
+ } else {
+ _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ }
+ _context.closePath();
+ };
+
+ /**
+ * Set badge
+ */
+ var badge = function (number, opts) {
+ opts = ((typeof opts) === 'string' ? {
+ animation: opts
+ } : opts) || {};
+ _readyCb = function () {
+ try {
+ if (typeof (number) === 'number' ? (number > 0) : (number !== '')) {
+ var q = {
+ type: 'badge',
+ options: {
+ n: number
+ }
+ };
+ if ('animation' in opts && animation.types['' + opts.animation]) {
+ q.options.animation = '' + opts.animation;
+ }
+ if ('type' in opts && type['' + opts.type]) {
+ q.options.type = '' + opts.type;
+ }
+ ['bgColor', 'textColor'].forEach(function (o) {
+ if (o in opts) {
+ q.options[o] = hexToRgb(opts[o]);
+ }
+ });
+ ['fontStyle', 'fontFamily'].forEach(function (o) {
+ if (o in opts) {
+ q.options[o] = opts[o];
+ }
+ });
+ _queue.push(q);
+ if (_queue.length > 100) {
+ throw new Error('Too many badges requests in queue.');
+ }
+ icon.start();
+ } else {
+ icon.reset();
+ }
+ } catch (e) {
+ throw new Error('Error setting badge. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+
+ /**
+ * Set image as icon
+ */
+ var image = function (imageElement) {
+ _readyCb = function () {
+ try {
+ var w = imageElement.width;
+ var h = imageElement.height;
+ var newImg = document.createElement('img');
+ var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
+ newImg.setAttribute('crossOrigin', 'anonymous');
+ newImg.onload=function(){
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(newImg, 0, 0, _w, _h);
+ link.setIcon(_canvas);
+ };
+ newImg.setAttribute('src', imageElement.getAttribute('src'));
+ newImg.height = (h / ratio);
+ newImg.width = (w / ratio);
+ } catch (e) {
+ throw new Error('Error setting image. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+ /**
+ * Set the icon from a source url. Won't work with badges.
+ */
+ var rawImageSrc = function (url) {
+ _readyCb = function() {
+ link.setIconSrc(url);
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+ /**
+ * Set video as icon
+ */
+ var video = function (videoElement) {
+ _readyCb = function () {
+ try {
+ if (videoElement === 'stop') {
+ _stop = true;
+ icon.reset();
+ _stop = false;
+ return;
+ }
+ //var w = videoElement.width;
+ //var h = videoElement.height;
+ //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
+ videoElement.addEventListener('play', function () {
+ drawVideo(this);
+ }, false);
+
+ } catch (e) {
+ throw new Error('Error setting video. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+ /**
+ * Set video as icon
+ */
+ var webcam = function (action) {
+ //UR
+ if (!window.URL || !window.URL.createObjectURL) {
+ window.URL = window.URL || {};
+ window.URL.createObjectURL = function (obj) {
+ return obj;
+ };
+ }
+ if (_browser.supported) {
+ var newVideo = false;
+ navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
+ _readyCb = function () {
+ try {
+ if (action === 'stop') {
+ _stop = true;
+ icon.reset();
+ _stop = false;
+ return;
+ }
+ newVideo = document.createElement('video');
+ newVideo.width = _w;
+ newVideo.height = _h;
+ navigator.getUserMedia({
+ video: true,
+ audio: false
+ }, function (stream) {
+ newVideo.src = URL.createObjectURL(stream);
+ newVideo.play();
+ drawVideo(newVideo);
+ }, function () {
+ });
+ } catch (e) {
+ throw new Error('Error setting webcam. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ }
+
+ };
+
+ var setOpt = function (key, value) {
+ var opts = key;
+ if (!(value == null && Object.prototype.toString.call(key) == '[object Object]')) {
+ opts = {};
+ opts[key] = value;
+ }
+
+ var keys = Object.keys(opts);
+ for (var i = 0; i < keys.length; i++) {
+ if (keys[i] == 'bgColor' || keys[i] == 'textColor') {
+ _opt[keys[i]] = hexToRgb(opts[keys[i]]);
+ } else {
+ _opt[keys[i]] = opts[keys[i]];
+ }
+ }
+
+ _queue.push(_lastBadge);
+ icon.start();
+ };
+
+ /**
+ * Draw video to context and repeat :)
+ */
+ function drawVideo(video) {
+ if (video.paused || video.ended || _stop) {
+ return false;
+ }
+ //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl)
+ try {
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(video, 0, 0, _w, _h);
+ } catch (e) {
+
+ }
+ _drawTimeout = setTimeout(function () {
+ drawVideo(video);
+ }, animation.duration);
+ link.setIcon(_canvas);
+ }
+
+ var link = {};
+ /**
+ * Get icons from HEAD tag or create a new <link> element
+ */
+ link.getIcons = function () {
+ var elms = [];
+ //get link element
+ var getLinks = function () {
+ var icons = [];
+ var links = _doc.getElementsByTagName('head')[0].getElementsByTagName('link');
+ for (var i = 0; i < links.length; i++) {
+ if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute('rel'))) {
+ icons.push(links[i]);
+ }
+ }
+ return icons;
+ };
+ if (_opt.element) {
+ elms = [_opt.element];
+ } else if (_opt.elementId) {
+ //if img element identified by elementId
+ elms = [_doc.getElementById(_opt.elementId)];
+ elms[0].setAttribute('href', elms[0].getAttribute('src'));
+ } else {
+ //if link element
+ elms = getLinks();
+ if (elms.length === 0) {
+ elms = [_doc.createElement('link')];
+ elms[0].setAttribute('rel', 'icon');
+ _doc.getElementsByTagName('head')[0].appendChild(elms[0]);
+ }
+ }
+ elms.forEach(function(item) {
+ item.setAttribute('type', 'image/png');
+ });
+ return elms;
+ };
+ link.setIcon = function (canvas) {
+ var url = canvas.toDataURL('image/png');
+ link.setIconSrc(url);
+ };
+ link.setIconSrc = function (url) {
+ if (_opt.dataUrl) {
+ //if using custom exporter
+ _opt.dataUrl(url);
+ }
+ if (_opt.element) {
+ _opt.element.setAttribute('href', url);
+ _opt.element.setAttribute('src', url);
+ } else if (_opt.elementId) {
+ //if is attached to element (image)
+ var elm = _doc.getElementById(_opt.elementId);
+ elm.setAttribute('href', url);
+ elm.setAttribute('src', url);
+ } else {
+ //if is attached to fav icon
+ if (_browser.ff || _browser.opera) {
+ //for FF we need to "recreate" element, atach to dom and remove old <link>
+ //var originalType = _orig.getAttribute('rel');
+ var old = _orig[_orig.length - 1];
+ var newIcon = _doc.createElement('link');
+ _orig = [newIcon];
+ //_orig.setAttribute('rel', originalType);
+ if (_browser.opera) {
+ newIcon.setAttribute('rel', 'icon');
+ }
+ newIcon.setAttribute('rel', 'icon');
+ newIcon.setAttribute('type', 'image/png');
+ _doc.getElementsByTagName('head')[0].appendChild(newIcon);
+ newIcon.setAttribute('href', url);
+ if (old.parentNode) {
+ old.parentNode.removeChild(old);
+ }
+ } else {
+ _orig.forEach(function(icon) {
+ icon.setAttribute('href', url);
+ });
+ }
+ }
+ };
+
+ //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139
+ //HEX to RGB convertor
+ function hexToRgb(hex) {
+ var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+ hex = hex.replace(shorthandRegex, function (m, r, g, b) {
+ return r + r + g + g + b + b;
+ });
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : false;
+ }
+
+ /**
+ * Merge options
+ */
+ function merge(def, opt) {
+ var mergedOpt = {};
+ var attrname;
+ for (attrname in def) {
+ mergedOpt[attrname] = def[attrname];
+ }
+ for (attrname in opt) {
+ mergedOpt[attrname] = opt[attrname];
+ }
+ return mergedOpt;
+ }
+
+ /**
+ * Cross-browser page visibility shim
+ * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible
+ */
+ function isPageHidden() {
+ return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden;
+ }
+
+ /**
+ * @namespace animation
+ */
+ var animation = {};
+ /**
+ * Animation "frame" duration
+ */
+ animation.duration = 40;
+ /**
+ * Animation types (none,fade,pop,slide)
+ */
+ animation.types = {};
+ animation.types.fade = [{
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.0
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.1
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.2
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.3
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.4
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.5
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.6
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.7
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.8
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.9
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1.0
+ }];
+ animation.types.none = [{
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ animation.types.pop = [{
+ x: 1,
+ y: 1,
+ w: 0,
+ h: 0,
+ o: 1
+ }, {
+ x: 0.9,
+ y: 0.9,
+ w: 0.1,
+ h: 0.1,
+ o: 1
+ }, {
+ x: 0.8,
+ y: 0.8,
+ w: 0.2,
+ h: 0.2,
+ o: 1
+ }, {
+ x: 0.7,
+ y: 0.7,
+ w: 0.3,
+ h: 0.3,
+ o: 1
+ }, {
+ x: 0.6,
+ y: 0.6,
+ w: 0.4,
+ h: 0.4,
+ o: 1
+ }, {
+ x: 0.5,
+ y: 0.5,
+ w: 0.5,
+ h: 0.5,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ animation.types.popFade = [{
+ x: 0.75,
+ y: 0.75,
+ w: 0,
+ h: 0,
+ o: 0
+ }, {
+ x: 0.65,
+ y: 0.65,
+ w: 0.1,
+ h: 0.1,
+ o: 0.2
+ }, {
+ x: 0.6,
+ y: 0.6,
+ w: 0.2,
+ h: 0.2,
+ o: 0.4
+ }, {
+ x: 0.55,
+ y: 0.55,
+ w: 0.3,
+ h: 0.3,
+ o: 0.6
+ }, {
+ x: 0.50,
+ y: 0.50,
+ w: 0.4,
+ h: 0.4,
+ o: 0.8
+ }, {
+ x: 0.45,
+ y: 0.45,
+ w: 0.5,
+ h: 0.5,
+ o: 0.9
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ animation.types.slide = [{
+ x: 0.4,
+ y: 1,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.9,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.9,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.8,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.7,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.6,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.5,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ /**
+ * Run animation
+ * @param {Object} opt Animation options
+ * @param {Object} cb Callabak after all steps are done
+ * @param {Object} revert Reverse order? true|false
+ * @param {Object} step Optional step number (frame bumber)
+ */
+ animation.run = function (opt, cb, revert, step) {
+ var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation];
+ if (revert === true) {
+ step = (typeof step !== 'undefined') ? step : animationType.length - 1;
+ } else {
+ step = (typeof step !== 'undefined') ? step : 0;
+ }
+ cb = (cb) ? cb : function () {
+ };
+ if ((step < animationType.length) && (step >= 0)) {
+ type[_opt.type](merge(opt, animationType[step]));
+ _animTimeout = setTimeout(function () {
+ if (revert) {
+ step = step - 1;
+ } else {
+ step = step + 1;
+ }
+ animation.run(opt, cb, revert, step);
+ }, animation.duration);
+
+ link.setIcon(_canvas);
+ } else {
+ cb();
+ return;
+ }
+ };
+ //auto init
+ init();
+ return {
+ badge: badge,
+ video: video,
+ image: image,
+ rawImageSrc: rawImageSrc,
+ webcam: webcam,
+ setOpt: setOpt,
+ reset: icon.reset,
+ browser: {
+ supported: _browser.supported
+ }
+ };
+ });
+
+ // AMD / RequireJS
+ if (typeof define !== 'undefined' && define.amd) {
+ define('favico',[], function () {
+ return Favico;
+ });
+ }
+ // CommonJS
+ else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Favico;
+ }
+ // included directly via <script> tag
+ else {
+ this.Favico = Favico;
+ }
+
+})();
+
+/*!
+ * enquire.js v2.1.2 - Awesome Media Queries in JavaScript
+ * Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/enquire.js
+ * License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+;(function (name, context, factory) {
+ var matchMedia = window.matchMedia;
+
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = factory(matchMedia);
+ }
+ else if (typeof define === 'function' && define.amd) {
+ define('enquire',[],function() {
+ return (context[name] = factory(matchMedia));
+ });
+ }
+ else {
+ context[name] = factory(matchMedia);
+ }
+}('enquire', this, function (matchMedia) {
+
+ 'use strict';
+
+ /*jshint unused:false */
+ /**
+ * Helper function for iterating over a collection
+ *
+ * @param collection
+ * @param fn
+ */
+ function each(collection, fn) {
+ var i = 0,
+ length = collection.length,
+ cont;
+
+ for(i; i < length; i++) {
+ cont = fn(collection[i], i);
+ if(cont === false) {
+ break; //allow early exit
+ }
+ }
+ }
+
+ /**
+ * Helper function for determining whether target object is an array
+ *
+ * @param target the object under test
+ * @return {Boolean} true if array, false otherwise
+ */
+ function isArray(target) {
+ return Object.prototype.toString.apply(target) === '[object Array]';
+ }
+
+ /**
+ * Helper function for determining whether target object is a function
+ *
+ * @param target the object under test
+ * @return {Boolean} true if function, false otherwise
+ */
+ function isFunction(target) {
+ return typeof target === 'function';
+ }
+
+ /**
+ * Delegate to handle a media query being matched and unmatched.
+ *
+ * @param {object} options
+ * @param {function} options.match callback for when the media query is matched
+ * @param {function} [options.unmatch] callback for when the media query is unmatched
+ * @param {function} [options.setup] one-time callback triggered the first time a query is matched
+ * @param {boolean} [options.deferSetup=false] should the setup callback be run immediately, rather than first time query is matched?
+ * @constructor
+ */
+ function QueryHandler(options) {
+ this.options = options;
+ !options.deferSetup && this.setup();
+ }
+ QueryHandler.prototype = {
+
+ /**
+ * coordinates setup of the handler
+ *
+ * @function
+ */
+ setup : function() {
+ if(this.options.setup) {
+ this.options.setup();
+ }
+ this.initialised = true;
+ },
+
+ /**
+ * coordinates setup and triggering of the handler
+ *
+ * @function
+ */
+ on : function() {
+ !this.initialised && this.setup();
+ this.options.match && this.options.match();
+ },
+
+ /**
+ * coordinates the unmatch event for the handler
+ *
+ * @function
+ */
+ off : function() {
+ this.options.unmatch && this.options.unmatch();
+ },
+
+ /**
+ * called when a handler is to be destroyed.
+ * delegates to the destroy or unmatch callbacks, depending on availability.
+ *
+ * @function
+ */
+ destroy : function() {
+ this.options.destroy ? this.options.destroy() : this.off();
+ },
+
+ /**
+ * determines equality by reference.
+ * if object is supplied compare options, if function, compare match callback
+ *
+ * @function
+ * @param {object || function} [target] the target for comparison
+ */
+ equals : function(target) {
+ return this.options === target || this.options.match === target;
+ }
+
+ };
+ /**
+ * Represents a single media query, manages it's state and registered handlers for this query
+ *
+ * @constructor
+ * @param {string} query the media query string
+ * @param {boolean} [isUnconditional=false] whether the media query should run regardless of whether the conditions are met. Primarily for helping older browsers deal with mobile-first design
+ */
+ function MediaQuery(query, isUnconditional) {
+ this.query = query;
+ this.isUnconditional = isUnconditional;
+ this.handlers = [];
+ this.mql = matchMedia(query);
+
+ var self = this;
+ this.listener = function(mql) {
+ self.mql = mql;
+ self.assess();
+ };
+ this.mql.addListener(this.listener);
+ }
+ MediaQuery.prototype = {
+
+ /**
+ * add a handler for this query, triggering if already active
+ *
+ * @param {object} handler
+ * @param {function} handler.match callback for when query is activated
+ * @param {function} [handler.unmatch] callback for when query is deactivated
+ * @param {function} [handler.setup] callback for immediate execution when a query handler is registered
+ * @param {boolean} [handler.deferSetup=false] should the setup callback be deferred until the first time the handler is matched?
+ */
+ addHandler : function(handler) {
+ var qh = new QueryHandler(handler);
+ this.handlers.push(qh);
+
+ this.matches() && qh.on();
+ },
+
+ /**
+ * removes the given handler from the collection, and calls it's destroy methods
+ *
+ * @param {object || function} handler the handler to remove
+ */
+ removeHandler : function(handler) {
+ var handlers = this.handlers;
+ each(handlers, function(h, i) {
+ if(h.equals(handler)) {
+ h.destroy();
+ return !handlers.splice(i,1); //remove from array and exit each early
+ }
+ });
+ },
+
+ /**
+ * Determine whether the media query should be considered a match
+ *
+ * @return {Boolean} true if media query can be considered a match, false otherwise
+ */
+ matches : function() {
+ return this.mql.matches || this.isUnconditional;
+ },
+
+ /**
+ * Clears all handlers and unbinds events
+ */
+ clear : function() {
+ each(this.handlers, function(handler) {
+ handler.destroy();
+ });
+ this.mql.removeListener(this.listener);
+ this.handlers.length = 0; //clear array
+ },
+
+ /*
+ * Assesses the query, turning on all handlers if it matches, turning them off if it doesn't match
+ */
+ assess : function() {
+ var action = this.matches() ? 'on' : 'off';
+
+ each(this.handlers, function(handler) {
+ handler[action]();
+ });
+ }
+ };
+ /**
+ * Allows for registration of query handlers.
+ * Manages the query handler's state and is responsible for wiring up browser events
+ *
+ * @constructor
+ */
+ function MediaQueryDispatch () {
+ if(!matchMedia) {
+ throw new Error('matchMedia not present, legacy browsers require a polyfill');
+ }
+
+ this.queries = {};
+ this.browserIsIncapable = !matchMedia('only all').matches;
+ }
+
+ MediaQueryDispatch.prototype = {
+
+ /**
+ * Registers a handler for the given media query
+ *
+ * @param {string} q the media query
+ * @param {object || Array || Function} options either a single query handler object, a function, or an array of query handlers
+ * @param {function} options.match fired when query matched
+ * @param {function} [options.unmatch] fired when a query is no longer matched
+ * @param {function} [options.setup] fired when handler first triggered
+ * @param {boolean} [options.deferSetup=false] whether setup should be run immediately or deferred until query is first matched
+ * @param {boolean} [shouldDegrade=false] whether this particular media query should always run on incapable browsers
+ */
+ register : function(q, options, shouldDegrade) {
+ var queries = this.queries,
+ isUnconditional = shouldDegrade && this.browserIsIncapable;
+
+ if(!queries[q]) {
+ queries[q] = new MediaQuery(q, isUnconditional);
+ }
+
+ //normalise to object in an array
+ if(isFunction(options)) {
+ options = { match : options };
+ }
+ if(!isArray(options)) {
+ options = [options];
+ }
+ each(options, function(handler) {
+ if (isFunction(handler)) {
+ handler = { match : handler };
+ }
+ queries[q].addHandler(handler);
+ });
+
+ return this;
+ },
+
+ /**
+ * unregisters a query and all it's handlers, or a specific handler for a query
+ *
+ * @param {string} q the media query to target
+ * @param {object || function} [handler] specific handler to unregister
+ */
+ unregister : function(q, handler) {
+ var query = this.queries[q];
+
+ if(query) {
+ if(handler) {
+ query.removeHandler(handler);
+ }
+ else {
+ query.clear();
+ delete this.queries[q];
+ }
+ }
+
+ return this;
+ }
+ };
+
+ return new MediaQueryDispatch();
+
+}));
+/* perfect-scrollbar v0.6.16 */
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+'use strict';
+
+var ps = require('../main');
+
+if (typeof define === 'function' && define.amd) {
+ // AMD
+ define('perfect-scrollbar',ps);
+} else {
+ // Add to a global object.
+ window.PerfectScrollbar = ps;
+ if (typeof window.Ps === 'undefined') {
+ window.Ps = ps;
+ }
+}
+
+},{"../main":7}],2:[function(require,module,exports){
+'use strict';
+
+function oldAdd(element, className) {
+ var classes = element.className.split(' ');
+ if (classes.indexOf(className) < 0) {
+ classes.push(className);
+ }
+ element.className = classes.join(' ');
+}
+
+function oldRemove(element, className) {
+ var classes = element.className.split(' ');
+ var idx = classes.indexOf(className);
+ if (idx >= 0) {
+ classes.splice(idx, 1);
+ }
+ element.className = classes.join(' ');
+}
+
+exports.add = function (element, className) {
+ if (element.classList) {
+ element.classList.add(className);
+ } else {
+ oldAdd(element, className);
+ }
+};
+
+exports.remove = function (element, className) {
+ if (element.classList) {
+ element.classList.remove(className);
+ } else {
+ oldRemove(element, className);
+ }
+};
+
+exports.list = function (element) {
+ if (element.classList) {
+ return Array.prototype.slice.apply(element.classList);
+ } else {
+ return element.className.split(' ');
+ }
+};
+
+},{}],3:[function(require,module,exports){
+'use strict';
+
+var DOM = {};
+
+DOM.e = function (tagName, className) {
+ var element = document.createElement(tagName);
+ element.className = className;
+ return element;
+};
+
+DOM.appendTo = function (child, parent) {
+ parent.appendChild(child);
+ return child;
+};
+
+function cssGet(element, styleName) {
+ return window.getComputedStyle(element)[styleName];
+}
+
+function cssSet(element, styleName, styleValue) {
+ if (typeof styleValue === 'number') {
+ styleValue = styleValue.toString() + 'px';
+ }
+ element.style[styleName] = styleValue;
+ return element;
+}
+
+function cssMultiSet(element, obj) {
+ for (var key in obj) {
+ var val = obj[key];
+ if (typeof val === 'number') {
+ val = val.toString() + 'px';
+ }
+ element.style[key] = val;
+ }
+ return element;
+}
+
+DOM.css = function (element, styleNameOrObject, styleValue) {
+ if (typeof styleNameOrObject === 'object') {
+ // multiple set with object
+ return cssMultiSet(element, styleNameOrObject);
+ } else {
+ if (typeof styleValue === 'undefined') {
+ return cssGet(element, styleNameOrObject);
+ } else {
+ return cssSet(element, styleNameOrObject, styleValue);
+ }
+ }
+};
+
+DOM.matches = function (element, query) {
+ if (typeof element.matches !== 'undefined') {
+ return element.matches(query);
+ } else {
+ if (typeof element.matchesSelector !== 'undefined') {
+ return element.matchesSelector(query);
+ } else if (typeof element.webkitMatchesSelector !== 'undefined') {
+ return element.webkitMatchesSelector(query);
+ } else if (typeof element.mozMatchesSelector !== 'undefined') {
+ return element.mozMatchesSelector(query);
+ } else if (typeof element.msMatchesSelector !== 'undefined') {
+ return element.msMatchesSelector(query);
+ }
+ }
+};
+
+DOM.remove = function (element) {
+ if (typeof element.remove !== 'undefined') {
+ element.remove();
+ } else {
+ if (element.parentNode) {
+ element.parentNode.removeChild(element);
+ }
+ }
+};
+
+DOM.queryChildren = function (element, selector) {
+ return Array.prototype.filter.call(element.childNodes, function (child) {
+ return DOM.matches(child, selector);
+ });
+};
+
+module.exports = DOM;
+
+},{}],4:[function(require,module,exports){
+'use strict';
+
+var EventElement = function (element) {
+ this.element = element;
+ this.events = {};
+};
+
+EventElement.prototype.bind = function (eventName, handler) {
+ if (typeof this.events[eventName] === 'undefined') {
+ this.events[eventName] = [];
+ }
+ this.events[eventName].push(handler);
+ this.element.addEventListener(eventName, handler, false);
+};
+
+EventElement.prototype.unbind = function (eventName, handler) {
+ var isHandlerProvided = (typeof handler !== 'undefined');
+ this.events[eventName] = this.events[eventName].filter(function (hdlr) {
+ if (isHandlerProvided && hdlr !== handler) {
+ return true;
+ }
+ this.element.removeEventListener(eventName, hdlr, false);
+ return false;
+ }, this);
+};
+
+EventElement.prototype.unbindAll = function () {
+ for (var name in this.events) {
+ this.unbind(name);
+ }
+};
+
+var EventManager = function () {
+ this.eventElements = [];
+};
+
+EventManager.prototype.eventElement = function (element) {
+ var ee = this.eventElements.filter(function (eventElement) {
+ return eventElement.element === element;
+ })[0];
+ if (typeof ee === 'undefined') {
+ ee = new EventElement(element);
+ this.eventElements.push(ee);
+ }
+ return ee;
+};
+
+EventManager.prototype.bind = function (element, eventName, handler) {
+ this.eventElement(element).bind(eventName, handler);
+};
+
+EventManager.prototype.unbind = function (element, eventName, handler) {
+ this.eventElement(element).unbind(eventName, handler);
+};
+
+EventManager.prototype.unbindAll = function () {
+ for (var i = 0; i < this.eventElements.length; i++) {
+ this.eventElements[i].unbindAll();
+ }
+};
+
+EventManager.prototype.once = function (element, eventName, handler) {
+ var ee = this.eventElement(element);
+ var onceHandler = function (e) {
+ ee.unbind(eventName, onceHandler);
+ handler(e);
+ };
+ ee.bind(eventName, onceHandler);
+};
+
+module.exports = EventManager;
+
+},{}],5:[function(require,module,exports){
+'use strict';
+
+module.exports = (function () {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return function () {
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+ s4() + '-' + s4() + s4() + s4();
+ };
+})();
+
+},{}],6:[function(require,module,exports){
+'use strict';
+
+var cls = require('./class');
+var dom = require('./dom');
+
+var toInt = exports.toInt = function (x) {
+ return parseInt(x, 10) || 0;
+};
+
+var clone = exports.clone = function (obj) {
+ if (!obj) {
+ return null;
+ } else if (obj.constructor === Array) {
+ return obj.map(clone);
+ } else if (typeof obj === 'object') {
+ var result = {};
+ for (var key in obj) {
+ result[key] = clone(obj[key]);
+ }
+ return result;
+ } else {
+ return obj;
+ }
+};
+
+exports.extend = function (original, source) {
+ var result = clone(original);
+ for (var key in source) {
+ result[key] = clone(source[key]);
+ }
+ return result;
+};
+
+exports.isEditable = function (el) {
+ return dom.matches(el, "input,[contenteditable]") ||
+ dom.matches(el, "select,[contenteditable]") ||
+ dom.matches(el, "textarea,[contenteditable]") ||
+ dom.matches(el, "button,[contenteditable]");
+};
+
+exports.removePsClasses = function (element) {
+ var clsList = cls.list(element);
+ for (var i = 0; i < clsList.length; i++) {
+ var className = clsList[i];
+ if (className.indexOf('ps-') === 0) {
+ cls.remove(element, className);
+ }
+ }
+};
+
+exports.outerWidth = function (element) {
+ return toInt(dom.css(element, 'width')) +
+ toInt(dom.css(element, 'paddingLeft')) +
+ toInt(dom.css(element, 'paddingRight')) +
+ toInt(dom.css(element, 'borderLeftWidth')) +
+ toInt(dom.css(element, 'borderRightWidth'));
+};
+
+exports.startScrolling = function (element, axis) {
+ cls.add(element, 'ps-in-scrolling');
+ if (typeof axis !== 'undefined') {
+ cls.add(element, 'ps-' + axis);
+ } else {
+ cls.add(element, 'ps-x');
+ cls.add(element, 'ps-y');
+ }
+};
+
+exports.stopScrolling = function (element, axis) {
+ cls.remove(element, 'ps-in-scrolling');
+ if (typeof axis !== 'undefined') {
+ cls.remove(element, 'ps-' + axis);
+ } else {
+ cls.remove(element, 'ps-x');
+ cls.remove(element, 'ps-y');
+ }
+};
+
+exports.env = {
+ isWebKit: 'WebkitAppearance' in document.documentElement.style,
+ supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch),
+ supportsIePointer: window.navigator.msMaxTouchPoints !== null
+};
+
+},{"./class":2,"./dom":3}],7:[function(require,module,exports){
+'use strict';
+
+var destroy = require('./plugin/destroy');
+var initialize = require('./plugin/initialize');
+var update = require('./plugin/update');
+
+module.exports = {
+ initialize: initialize,
+ update: update,
+ destroy: destroy
+};
+
+},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":21}],8:[function(require,module,exports){
+'use strict';
+
+module.exports = {
+ handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'],
+ maxScrollbarLength: null,
+ minScrollbarLength: null,
+ scrollXMarginOffset: 0,
+ scrollYMarginOffset: 0,
+ suppressScrollX: false,
+ suppressScrollY: false,
+ swipePropagation: true,
+ useBothWheelAxes: false,
+ wheelPropagation: false,
+ wheelSpeed: 1,
+ theme: 'default'
+};
+
+},{}],9:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var dom = require('../lib/dom');
+var instances = require('./instances');
+
+module.exports = function (element) {
+ var i = instances.get(element);
+
+ if (!i) {
+ return;
+ }
+
+ i.event.unbindAll();
+ dom.remove(i.scrollbarX);
+ dom.remove(i.scrollbarY);
+ dom.remove(i.scrollbarXRail);
+ dom.remove(i.scrollbarYRail);
+ _.removePsClasses(element);
+
+ instances.remove(element);
+};
+
+},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(require,module,exports){
+'use strict';
+
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindClickRailHandler(element, i) {
+ function pageOffset(el) {
+ return el.getBoundingClientRect();
+ }
+ var stopPropagation = function (e) { e.stopPropagation(); };
+
+ i.event.bind(i.scrollbarY, 'click', stopPropagation);
+ i.event.bind(i.scrollbarYRail, 'click', function (e) {
+ var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top;
+ var direction = positionTop > i.scrollbarYTop ? 1 : -1;
+
+ updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight);
+ updateGeometry(element);
+
+ e.stopPropagation();
+ });
+
+ i.event.bind(i.scrollbarX, 'click', stopPropagation);
+ i.event.bind(i.scrollbarXRail, 'click', function (e) {
+ var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left;
+ var direction = positionLeft > i.scrollbarXLeft ? 1 : -1;
+
+ updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth);
+ updateGeometry(element);
+
+ e.stopPropagation();
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindClickRailHandler(element, i);
+};
+
+},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var dom = require('../../lib/dom');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindMouseScrollXHandler(element, i) {
+ var currentLeft = null;
+ var currentPageX = null;
+
+ function updateScrollLeft(deltaX) {
+ var newLeft = currentLeft + (deltaX * i.railXRatio);
+ var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth));
+
+ if (newLeft < 0) {
+ i.scrollbarXLeft = 0;
+ } else if (newLeft > maxLeft) {
+ i.scrollbarXLeft = maxLeft;
+ } else {
+ i.scrollbarXLeft = newLeft;
+ }
+
+ var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment;
+ updateScroll(element, 'left', scrollLeft);
+ }
+
+ var mouseMoveHandler = function (e) {
+ updateScrollLeft(e.pageX - currentPageX);
+ updateGeometry(element);
+ e.stopPropagation();
+ e.preventDefault();
+ };
+
+ var mouseUpHandler = function () {
+ _.stopScrolling(element, 'x');
+ i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ };
+
+ i.event.bind(i.scrollbarX, 'mousedown', function (e) {
+ currentPageX = e.pageX;
+ currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio;
+ _.startScrolling(element, 'x');
+
+ i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);
+
+ e.stopPropagation();
+ e.preventDefault();
+ });
+}
+
+function bindMouseScrollYHandler(element, i) {
+ var currentTop = null;
+ var currentPageY = null;
+
+ function updateScrollTop(deltaY) {
+ var newTop = currentTop + (deltaY * i.railYRatio);
+ var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight));
+
+ if (newTop < 0) {
+ i.scrollbarYTop = 0;
+ } else if (newTop > maxTop) {
+ i.scrollbarYTop = maxTop;
+ } else {
+ i.scrollbarYTop = newTop;
+ }
+
+ var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight)));
+ updateScroll(element, 'top', scrollTop);
+ }
+
+ var mouseMoveHandler = function (e) {
+ updateScrollTop(e.pageY - currentPageY);
+ updateGeometry(element);
+ e.stopPropagation();
+ e.preventDefault();
+ };
+
+ var mouseUpHandler = function () {
+ _.stopScrolling(element, 'y');
+ i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ };
+
+ i.event.bind(i.scrollbarY, 'mousedown', function (e) {
+ currentPageY = e.pageY;
+ currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio;
+ _.startScrolling(element, 'y');
+
+ i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);
+
+ e.stopPropagation();
+ e.preventDefault();
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindMouseScrollXHandler(element, i);
+ bindMouseScrollYHandler(element, i);
+};
+
+},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var dom = require('../../lib/dom');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindKeyboardHandler(element, i) {
+ var hovered = false;
+ i.event.bind(element, 'mouseenter', function () {
+ hovered = true;
+ });
+ i.event.bind(element, 'mouseleave', function () {
+ hovered = false;
+ });
+
+ var shouldPrevent = false;
+ function shouldPreventDefault(deltaX, deltaY) {
+ var scrollTop = element.scrollTop;
+ if (deltaX === 0) {
+ if (!i.scrollbarYActive) {
+ return false;
+ }
+ if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+
+ var scrollLeft = element.scrollLeft;
+ if (deltaY === 0) {
+ if (!i.scrollbarXActive) {
+ return false;
+ }
+ if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+ return true;
+ }
+
+ i.event.bind(i.ownerDocument, 'keydown', function (e) {
+ if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) {
+ return;
+ }
+
+ var focused = dom.matches(i.scrollbarX, ':focus') ||
+ dom.matches(i.scrollbarY, ':focus');
+
+ if (!hovered && !focused) {
+ return;
+ }
+
+ var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement;
+ if (activeElement) {
+ if (activeElement.tagName === 'IFRAME') {
+ activeElement = activeElement.contentDocument.activeElement;
+ } else {
+ // go deeper if element is a webcomponent
+ while (activeElement.shadowRoot) {
+ activeElement = activeElement.shadowRoot.activeElement;
+ }
+ }
+ if (_.isEditable(activeElement)) {
+ return;
+ }
+ }
+
+ var deltaX = 0;
+ var deltaY = 0;
+
+ switch (e.which) {
+ case 37: // left
+ if (e.metaKey) {
+ deltaX = -i.contentWidth;
+ } else if (e.altKey) {
+ deltaX = -i.containerWidth;
+ } else {
+ deltaX = -30;
+ }
+ break;
+ case 38: // up
+ if (e.metaKey) {
+ deltaY = i.contentHeight;
+ } else if (e.altKey) {
+ deltaY = i.containerHeight;
+ } else {
+ deltaY = 30;
+ }
+ break;
+ case 39: // right
+ if (e.metaKey) {
+ deltaX = i.contentWidth;
+ } else if (e.altKey) {
+ deltaX = i.containerWidth;
+ } else {
+ deltaX = 30;
+ }
+ break;
+ case 40: // down
+ if (e.metaKey) {
+ deltaY = -i.contentHeight;
+ } else if (e.altKey) {
+ deltaY = -i.containerHeight;
+ } else {
+ deltaY = -30;
+ }
+ break;
+ case 33: // page up
+ deltaY = 90;
+ break;
+ case 32: // space bar
+ if (e.shiftKey) {
+ deltaY = 90;
+ } else {
+ deltaY = -90;
+ }
+ break;
+ case 34: // page down
+ deltaY = -90;
+ break;
+ case 35: // end
+ if (e.ctrlKey) {
+ deltaY = -i.contentHeight;
+ } else {
+ deltaY = -i.containerHeight;
+ }
+ break;
+ case 36: // home
+ if (e.ctrlKey) {
+ deltaY = element.scrollTop;
+ } else {
+ deltaY = i.containerHeight;
+ }
+ break;
+ default:
+ return;
+ }
+
+ updateScroll(element, 'top', element.scrollTop - deltaY);
+ updateScroll(element, 'left', element.scrollLeft + deltaX);
+ updateGeometry(element);
+
+ shouldPrevent = shouldPreventDefault(deltaX, deltaY);
+ if (shouldPrevent) {
+ e.preventDefault();
+ }
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindKeyboardHandler(element, i);
+};
+
+},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(require,module,exports){
+'use strict';
+
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindMouseWheelHandler(element, i) {
+ var shouldPrevent = false;
+
+ function shouldPreventDefault(deltaX, deltaY) {
+ var scrollTop = element.scrollTop;
+ if (deltaX === 0) {
+ if (!i.scrollbarYActive) {
+ return false;
+ }
+ if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+
+ var scrollLeft = element.scrollLeft;
+ if (deltaY === 0) {
+ if (!i.scrollbarXActive) {
+ return false;
+ }
+ if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+ return true;
+ }
+
+ function getDeltaFromEvent(e) {
+ var deltaX = e.deltaX;
+ var deltaY = -1 * e.deltaY;
+
+ if (typeof deltaX === "undefined" || typeof deltaY === "undefined") {
+ // OS X Safari
+ deltaX = -1 * e.wheelDeltaX / 6;
+ deltaY = e.wheelDeltaY / 6;
+ }
+
+ if (e.deltaMode && e.deltaMode === 1) {
+ // Firefox in deltaMode 1: Line scrolling
+ deltaX *= 10;
+ deltaY *= 10;
+ }
+
+ if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) {
+ // IE in some mouse drivers
+ deltaX = 0;
+ deltaY = e.wheelDelta;
+ }
+
+ if (e.shiftKey) {
+ // reverse axis with shift key
+ return [-deltaY, -deltaX];
+ }
+ return [deltaX, deltaY];
+ }
+
+ function shouldBeConsumedByChild(deltaX, deltaY) {
+ var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover');
+ if (child) {
+ if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) {
+ // if not scrollable
+ return false;
+ }
+
+ var maxScrollTop = child.scrollHeight - child.clientHeight;
+ if (maxScrollTop > 0) {
+ if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) {
+ return true;
+ }
+ }
+ var maxScrollLeft = child.scrollLeft - child.clientWidth;
+ if (maxScrollLeft > 0) {
+ if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ function mousewheelHandler(e) {
+ var delta = getDeltaFromEvent(e);
+
+ var deltaX = delta[0];
+ var deltaY = delta[1];
+
+ if (shouldBeConsumedByChild(deltaX, deltaY)) {
+ return;
+ }
+
+ shouldPrevent = false;
+ if (!i.settings.useBothWheelAxes) {
+ // deltaX will only be used for horizontal scrolling and deltaY will
+ // only be used for vertical scrolling - this is the default
+ updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed));
+ updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed));
+ } else if (i.scrollbarYActive && !i.scrollbarXActive) {
+ // only vertical scrollbar is active and useBothWheelAxes option is
+ // active, so let's scroll vertical bar using both mouse wheel axes
+ if (deltaY) {
+ updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed));
+ } else {
+ updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed));
+ }
+ shouldPrevent = true;
+ } else if (i.scrollbarXActive && !i.scrollbarYActive) {
+ // useBothWheelAxes and only horizontal bar is active, so use both
+ // wheel axes for horizontal bar
+ if (deltaX) {
+ updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed));
+ } else {
+ updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed));
+ }
+ shouldPrevent = true;
+ }
+
+ updateGeometry(element);
+
+ shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY));
+ if (shouldPrevent) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ if (typeof window.onwheel !== "undefined") {
+ i.event.bind(element, 'wheel', mousewheelHandler);
+ } else if (typeof window.onmousewheel !== "undefined") {
+ i.event.bind(element, 'mousewheel', mousewheelHandler);
+ }
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindMouseWheelHandler(element, i);
+};
+
+},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(require,module,exports){
+'use strict';
+
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+
+function bindNativeScrollHandler(element, i) {
+ i.event.bind(element, 'scroll', function () {
+ updateGeometry(element);
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindNativeScrollHandler(element, i);
+};
+
+},{"../instances":18,"../update-geometry":19}],15:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindSelectionHandler(element, i) {
+ function getRangeNode() {
+ var selection = window.getSelection ? window.getSelection() :
+ document.getSelection ? document.getSelection() : '';
+ if (selection.toString().length === 0) {
+ return null;
+ } else {
+ return selection.getRangeAt(0).commonAncestorContainer;
+ }
+ }
+
+ var scrollingLoop = null;
+ var scrollDiff = {top: 0, left: 0};
+ function startScrolling() {
+ if (!scrollingLoop) {
+ scrollingLoop = setInterval(function () {
+ if (!instances.get(element)) {
+ clearInterval(scrollingLoop);
+ return;
+ }
+
+ updateScroll(element, 'top', element.scrollTop + scrollDiff.top);
+ updateScroll(element, 'left', element.scrollLeft + scrollDiff.left);
+ updateGeometry(element);
+ }, 50); // every .1 sec
+ }
+ }
+ function stopScrolling() {
+ if (scrollingLoop) {
+ clearInterval(scrollingLoop);
+ scrollingLoop = null;
+ }
+ _.stopScrolling(element);
+ }
+
+ var isSelected = false;
+ i.event.bind(i.ownerDocument, 'selectionchange', function () {
+ if (element.contains(getRangeNode())) {
+ isSelected = true;
+ } else {
+ isSelected = false;
+ stopScrolling();
+ }
+ });
+ i.event.bind(window, 'mouseup', function () {
+ if (isSelected) {
+ isSelected = false;
+ stopScrolling();
+ }
+ });
+ i.event.bind(window, 'keyup', function () {
+ if (isSelected) {
+ isSelected = false;
+ stopScrolling();
+ }
+ });
+
+ i.event.bind(window, 'mousemove', function (e) {
+ if (isSelected) {
+ var mousePosition = {x: e.pageX, y: e.pageY};
+ var containerGeometry = {
+ left: element.offsetLeft,
+ right: element.offsetLeft + element.offsetWidth,
+ top: element.offsetTop,
+ bottom: element.offsetTop + element.offsetHeight
+ };
+
+ if (mousePosition.x < containerGeometry.left + 3) {
+ scrollDiff.left = -5;
+ _.startScrolling(element, 'x');
+ } else if (mousePosition.x > containerGeometry.right - 3) {
+ scrollDiff.left = 5;
+ _.startScrolling(element, 'x');
+ } else {
+ scrollDiff.left = 0;
+ }
+
+ if (mousePosition.y < containerGeometry.top + 3) {
+ if (containerGeometry.top + 3 - mousePosition.y < 5) {
+ scrollDiff.top = -5;
+ } else {
+ scrollDiff.top = -20;
+ }
+ _.startScrolling(element, 'y');
+ } else if (mousePosition.y > containerGeometry.bottom - 3) {
+ if (mousePosition.y - containerGeometry.bottom + 3 < 5) {
+ scrollDiff.top = 5;
+ } else {
+ scrollDiff.top = 20;
+ }
+ _.startScrolling(element, 'y');
+ } else {
+ scrollDiff.top = 0;
+ }
+
+ if (scrollDiff.top === 0 && scrollDiff.left === 0) {
+ stopScrolling();
+ } else {
+ startScrolling();
+ }
+ }
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindSelectionHandler(element, i);
+};
+
+},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindTouchHandler(element, i, supportsTouch, supportsIePointer) {
+ function shouldPreventDefault(deltaX, deltaY) {
+ var scrollTop = element.scrollTop;
+ var scrollLeft = element.scrollLeft;
+ var magnitudeX = Math.abs(deltaX);
+ var magnitudeY = Math.abs(deltaY);
+
+ if (magnitudeY > magnitudeX) {
+ // user is perhaps trying to swipe up/down the page
+
+ if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) ||
+ ((deltaY > 0) && (scrollTop === 0))) {
+ return !i.settings.swipePropagation;
+ }
+ } else if (magnitudeX > magnitudeY) {
+ // user is perhaps trying to swipe left/right across the page
+
+ if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) ||
+ ((deltaX > 0) && (scrollLeft === 0))) {
+ return !i.settings.swipePropagation;
+ }
+ }
+
+ return true;
+ }
+
+ function applyTouchMove(differenceX, differenceY) {
+ updateScroll(element, 'top', element.scrollTop - differenceY);
+ updateScroll(element, 'left', element.scrollLeft - differenceX);
+
+ updateGeometry(element);
+ }
+
+ var startOffset = {};
+ var startTime = 0;
+ var speed = {};
+ var easingLoop = null;
+ var inGlobalTouch = false;
+ var inLocalTouch = false;
+
+ function globalTouchStart() {
+ inGlobalTouch = true;
+ }
+ function globalTouchEnd() {
+ inGlobalTouch = false;
+ }
+
+ function getTouch(e) {
+ if (e.targetTouches) {
+ return e.targetTouches[0];
+ } else {
+ // Maybe IE pointer
+ return e;
+ }
+ }
+ function shouldHandle(e) {
+ if (e.targetTouches && e.targetTouches.length === 1) {
+ return true;
+ }
+ if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
+ return true;
+ }
+ return false;
+ }
+ function touchStart(e) {
+ if (shouldHandle(e)) {
+ inLocalTouch = true;
+
+ var touch = getTouch(e);
+
+ startOffset.pageX = touch.pageX;
+ startOffset.pageY = touch.pageY;
+
+ startTime = (new Date()).getTime();
+
+ if (easingLoop !== null) {
+ clearInterval(easingLoop);
+ }
+
+ e.stopPropagation();
+ }
+ }
+ function touchMove(e) {
+ if (!inLocalTouch && i.settings.swipePropagation) {
+ touchStart(e);
+ }
+ if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) {
+ var touch = getTouch(e);
+
+ var currentOffset = {pageX: touch.pageX, pageY: touch.pageY};
+
+ var differenceX = currentOffset.pageX - startOffset.pageX;
+ var differenceY = currentOffset.pageY - startOffset.pageY;
+
+ applyTouchMove(differenceX, differenceY);
+ startOffset = currentOffset;
+
+ var currentTime = (new Date()).getTime();
+
+ var timeGap = currentTime - startTime;
+ if (timeGap > 0) {
+ speed.x = differenceX / timeGap;
+ speed.y = differenceY / timeGap;
+ startTime = currentTime;
+ }
+
+ if (shouldPreventDefault(differenceX, differenceY)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+ }
+ function touchEnd() {
+ if (!inGlobalTouch && inLocalTouch) {
+ inLocalTouch = false;
+
+ clearInterval(easingLoop);
+ easingLoop = setInterval(function () {
+ if (!instances.get(element)) {
+ clearInterval(easingLoop);
+ return;
+ }
+
+ if (!speed.x && !speed.y) {
+ clearInterval(easingLoop);
+ return;
+ }
+
+ if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {
+ clearInterval(easingLoop);
+ return;
+ }
+
+ applyTouchMove(speed.x * 30, speed.y * 30);
+
+ speed.x *= 0.8;
+ speed.y *= 0.8;
+ }, 10);
+ }
+ }
+
+ if (supportsTouch) {
+ i.event.bind(window, 'touchstart', globalTouchStart);
+ i.event.bind(window, 'touchend', globalTouchEnd);
+ i.event.bind(element, 'touchstart', touchStart);
+ i.event.bind(element, 'touchmove', touchMove);
+ i.event.bind(element, 'touchend', touchEnd);
+ } else if (supportsIePointer) {
+ if (window.PointerEvent) {
+ i.event.bind(window, 'pointerdown', globalTouchStart);
+ i.event.bind(window, 'pointerup', globalTouchEnd);
+ i.event.bind(element, 'pointerdown', touchStart);
+ i.event.bind(element, 'pointermove', touchMove);
+ i.event.bind(element, 'pointerup', touchEnd);
+ } else if (window.MSPointerEvent) {
+ i.event.bind(window, 'MSPointerDown', globalTouchStart);
+ i.event.bind(window, 'MSPointerUp', globalTouchEnd);
+ i.event.bind(element, 'MSPointerDown', touchStart);
+ i.event.bind(element, 'MSPointerMove', touchMove);
+ i.event.bind(element, 'MSPointerUp', touchEnd);
+ }
+ }
+}
+
+module.exports = function (element) {
+ if (!_.env.supportsTouch && !_.env.supportsIePointer) {
+ return;
+ }
+
+ var i = instances.get(element);
+ bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer);
+};
+
+},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var cls = require('../lib/class');
+var instances = require('./instances');
+var updateGeometry = require('./update-geometry');
+
+// Handlers
+var handlers = {
+ 'click-rail': require('./handler/click-rail'),
+ 'drag-scrollbar': require('./handler/drag-scrollbar'),
+ 'keyboard': require('./handler/keyboard'),
+ 'wheel': require('./handler/mouse-wheel'),
+ 'touch': require('./handler/touch'),
+ 'selection': require('./handler/selection')
+};
+var nativeScrollHandler = require('./handler/native-scroll');
+
+module.exports = function (element, userSettings) {
+ userSettings = typeof userSettings === 'object' ? userSettings : {};
+
+ cls.add(element, 'ps-container');
+
+ // Create a plugin instance.
+ var i = instances.add(element);
+
+ i.settings = _.extend(i.settings, userSettings);
+ cls.add(element, 'ps-theme-' + i.settings.theme);
+
+ i.settings.handlers.forEach(function (handlerName) {
+ handlers[handlerName](element);
+ });
+
+ nativeScrollHandler(element);
+
+ updateGeometry(element);
+};
+
+},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var cls = require('../lib/class');
+var defaultSettings = require('./default-setting');
+var dom = require('../lib/dom');
+var EventManager = require('../lib/event-manager');
+var guid = require('../lib/guid');
+
+var instances = {};
+
+function Instance(element) {
+ var i = this;
+
+ i.settings = _.clone(defaultSettings);
+ i.containerWidth = null;
+ i.containerHeight = null;
+ i.contentWidth = null;
+ i.contentHeight = null;
+
+ i.isRtl = dom.css(element, 'direction') === "rtl";
+ i.isNegativeScroll = (function () {
+ var originalScrollLeft = element.scrollLeft;
+ var result = null;
+ element.scrollLeft = -1;
+ result = element.scrollLeft < 0;
+ element.scrollLeft = originalScrollLeft;
+ return result;
+ })();
+ i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0;
+ i.event = new EventManager();
+ i.ownerDocument = element.ownerDocument || document;
+
+ function focus() {
+ cls.add(element, 'ps-focus');
+ }
+
+ function blur() {
+ cls.remove(element, 'ps-focus');
+ }
+
+ i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element);
+ i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail);
+ i.scrollbarX.setAttribute('tabindex', 0);
+ i.event.bind(i.scrollbarX, 'focus', focus);
+ i.event.bind(i.scrollbarX, 'blur', blur);
+ i.scrollbarXActive = null;
+ i.scrollbarXWidth = null;
+ i.scrollbarXLeft = null;
+ i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom'));
+ i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN
+ i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top'));
+ i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth'));
+ // Set rail to display:block to calculate margins
+ dom.css(i.scrollbarXRail, 'display', 'block');
+ i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight'));
+ dom.css(i.scrollbarXRail, 'display', '');
+ i.railXWidth = null;
+ i.railXRatio = null;
+
+ i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element);
+ i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail);
+ i.scrollbarY.setAttribute('tabindex', 0);
+ i.event.bind(i.scrollbarY, 'focus', focus);
+ i.event.bind(i.scrollbarY, 'blur', blur);
+ i.scrollbarYActive = null;
+ i.scrollbarYHeight = null;
+ i.scrollbarYTop = null;
+ i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right'));
+ i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN
+ i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left'));
+ i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null;
+ i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth'));
+ dom.css(i.scrollbarYRail, 'display', 'block');
+ i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom'));
+ dom.css(i.scrollbarYRail, 'display', '');
+ i.railYHeight = null;
+ i.railYRatio = null;
+}
+
+function getId(element) {
+ return element.getAttribute('data-ps-id');
+}
+
+function setId(element, id) {
+ element.setAttribute('data-ps-id', id);
+}
+
+function removeId(element) {
+ element.removeAttribute('data-ps-id');
+}
+
+exports.add = function (element) {
+ var newId = guid();
+ setId(element, newId);
+ instances[newId] = new Instance(element);
+ return instances[newId];
+};
+
+exports.remove = function (element) {
+ delete instances[getId(element)];
+ removeId(element);
+};
+
+exports.get = function (element) {
+ return instances[getId(element)];
+};
+
+},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var cls = require('../lib/class');
+var dom = require('../lib/dom');
+var instances = require('./instances');
+var updateScroll = require('./update-scroll');
+
+function getThumbSize(i, thumbSize) {
+ if (i.settings.minScrollbarLength) {
+ thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength);
+ }
+ if (i.settings.maxScrollbarLength) {
+ thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength);
+ }
+ return thumbSize;
+}
+
+function updateCss(element, i) {
+ var xRailOffset = {width: i.railXWidth};
+ if (i.isRtl) {
+ xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth;
+ } else {
+ xRailOffset.left = element.scrollLeft;
+ }
+ if (i.isScrollbarXUsingBottom) {
+ xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop;
+ } else {
+ xRailOffset.top = i.scrollbarXTop + element.scrollTop;
+ }
+ dom.css(i.scrollbarXRail, xRailOffset);
+
+ var yRailOffset = {top: element.scrollTop, height: i.railYHeight};
+ if (i.isScrollbarYUsingRight) {
+ if (i.isRtl) {
+ yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth;
+ } else {
+ yRailOffset.right = i.scrollbarYRight - element.scrollLeft;
+ }
+ } else {
+ if (i.isRtl) {
+ yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth;
+ } else {
+ yRailOffset.left = i.scrollbarYLeft + element.scrollLeft;
+ }
+ }
+ dom.css(i.scrollbarYRail, yRailOffset);
+
+ dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth});
+ dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth});
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+
+ i.containerWidth = element.clientWidth;
+ i.containerHeight = element.clientHeight;
+ i.contentWidth = element.scrollWidth;
+ i.contentHeight = element.scrollHeight;
+
+ var existingRails;
+ if (!element.contains(i.scrollbarXRail)) {
+ existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail');
+ if (existingRails.length > 0) {
+ existingRails.forEach(function (rail) {
+ dom.remove(rail);
+ });
+ }
+ dom.appendTo(i.scrollbarXRail, element);
+ }
+ if (!element.contains(i.scrollbarYRail)) {
+ existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail');
+ if (existingRails.length > 0) {
+ existingRails.forEach(function (rail) {
+ dom.remove(rail);
+ });
+ }
+ dom.appendTo(i.scrollbarYRail, element);
+ }
+
+ if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) {
+ i.scrollbarXActive = true;
+ i.railXWidth = i.containerWidth - i.railXMarginWidth;
+ i.railXRatio = i.containerWidth / i.railXWidth;
+ i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth));
+ i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth));
+ } else {
+ i.scrollbarXActive = false;
+ }
+
+ if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) {
+ i.scrollbarYActive = true;
+ i.railYHeight = i.containerHeight - i.railYMarginHeight;
+ i.railYRatio = i.containerHeight / i.railYHeight;
+ i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight));
+ i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight));
+ } else {
+ i.scrollbarYActive = false;
+ }
+
+ if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) {
+ i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth;
+ }
+ if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) {
+ i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight;
+ }
+
+ updateCss(element, i);
+
+ if (i.scrollbarXActive) {
+ cls.add(element, 'ps-active-x');
+ } else {
+ cls.remove(element, 'ps-active-x');
+ i.scrollbarXWidth = 0;
+ i.scrollbarXLeft = 0;
+ updateScroll(element, 'left', 0);
+ }
+ if (i.scrollbarYActive) {
+ cls.add(element, 'ps-active-y');
+ } else {
+ cls.remove(element, 'ps-active-y');
+ i.scrollbarYHeight = 0;
+ i.scrollbarYTop = 0;
+ updateScroll(element, 'top', 0);
+ }
+};
+
+},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(require,module,exports){
+'use strict';
+
+var instances = require('./instances');
+
+var lastTop;
+var lastLeft;
+
+var createDOMEvent = function (name) {
+ var event = document.createEvent("Event");
+ event.initEvent(name, true, true);
+ return event;
+};
+
+module.exports = function (element, axis, value) {
+ if (typeof element === 'undefined') {
+ throw 'You must provide an element to the update-scroll function';
+ }
+
+ if (typeof axis === 'undefined') {
+ throw 'You must provide an axis to the update-scroll function';
+ }
+
+ if (typeof value === 'undefined') {
+ throw 'You must provide a value to the update-scroll function';
+ }
+
+ if (axis === 'top' && value <= 0) {
+ element.scrollTop = value = 0; // don't allow negative scroll
+ element.dispatchEvent(createDOMEvent('ps-y-reach-start'));
+ }
+
+ if (axis === 'left' && value <= 0) {
+ element.scrollLeft = value = 0; // don't allow negative scroll
+ element.dispatchEvent(createDOMEvent('ps-x-reach-start'));
+ }
+
+ var i = instances.get(element);
+
+ if (axis === 'top' && value >= i.contentHeight - i.containerHeight) {
+ // don't allow scroll past container
+ value = i.contentHeight - i.containerHeight;
+ if (value - element.scrollTop <= 1) {
+ // mitigates rounding errors on non-subpixel scroll values
+ value = element.scrollTop;
+ } else {
+ element.scrollTop = value;
+ }
+ element.dispatchEvent(createDOMEvent('ps-y-reach-end'));
+ }
+
+ if (axis === 'left' && value >= i.contentWidth - i.containerWidth) {
+ // don't allow scroll past container
+ value = i.contentWidth - i.containerWidth;
+ if (value - element.scrollLeft <= 1) {
+ // mitigates rounding errors on non-subpixel scroll values
+ value = element.scrollLeft;
+ } else {
+ element.scrollLeft = value;
+ }
+ element.dispatchEvent(createDOMEvent('ps-x-reach-end'));
+ }
+
+ if (!lastTop) {
+ lastTop = element.scrollTop;
+ }
+
+ if (!lastLeft) {
+ lastLeft = element.scrollLeft;
+ }
+
+ if (axis === 'top' && value < lastTop) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-up'));
+ }
+
+ if (axis === 'top' && value > lastTop) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-down'));
+ }
+
+ if (axis === 'left' && value < lastLeft) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-left'));
+ }
+
+ if (axis === 'left' && value > lastLeft) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-right'));
+ }
+
+ if (axis === 'top') {
+ element.scrollTop = lastTop = value;
+ element.dispatchEvent(createDOMEvent('ps-scroll-y'));
+ }
+
+ if (axis === 'left') {
+ element.scrollLeft = lastLeft = value;
+ element.dispatchEvent(createDOMEvent('ps-scroll-x'));
+ }
+
+};
+
+},{"./instances":18}],21:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var dom = require('../lib/dom');
+var instances = require('./instances');
+var updateGeometry = require('./update-geometry');
+var updateScroll = require('./update-scroll');
+
+module.exports = function (element) {
+ var i = instances.get(element);
+
+ if (!i) {
+ return;
+ }
+
+ // Recalcuate negative scrollLeft adjustment
+ i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0;
+
+ // Recalculate rail margins
+ dom.css(i.scrollbarXRail, 'display', 'block');
+ dom.css(i.scrollbarYRail, 'display', 'block');
+ i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight'));
+ i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom'));
+
+ // Hide scrollbars not to affect scrollWidth and scrollHeight
+ dom.css(i.scrollbarXRail, 'display', 'none');
+ dom.css(i.scrollbarYRail, 'display', 'none');
+
+ updateGeometry(element);
+
+ // Update top/left scroll to trigger events
+ updateScroll(element, 'top', element.scrollTop);
+ updateScroll(element, 'left', element.scrollLeft);
+
+ dom.css(i.scrollbarXRail, 'display', '');
+ dom.css(i.scrollbarYRail, 'display', '');
+};
+
+},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]);
+
+/**
+ * Provides utility functions for date operations.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Date/Util
+ */
+define('WoltLabSuite/Core/Date/Util',['Language'], function(Language) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Date/Util
+ */
+ var DateUtil = {
+ /**
+ * Returns the formatted date.
+ *
+ * @param {Date} date date object
+ * @returns {string} formatted date
+ */
+ formatDate: function(date) {
+ return this.format(date, Language.get('wcf.date.dateFormat'));
+ },
+
+ /**
+ * Returns the formatted time.
+ *
+ * @param {Date} date date object
+ * @returns {string} formatted time
+ */
+ formatTime: function(date) {
+ return this.format(date, Language.get('wcf.date.timeFormat'));
+ },
+
+ /**
+ * Returns the formatted date time.
+ *
+ * @param {Date} date date object
+ * @returns {string} formatted date time
+ */
+ formatDateTime: function(date) {
+ return this.format(date, Language.get('wcf.date.dateTimeFormat').replace(/%date%/, Language.get('wcf.date.dateFormat')).replace(/%time%/, Language.get('wcf.date.timeFormat')));
+ },
+
+ /**
+ * Formats a date using PHP's `date()` modifiers.
+ *
+ * @param {Date} date date object
+ * @param {string} format output format
+ * @returns {string} formatted date
+ */
+ format: function(date, format) {
+ var char;
+ var out = '';
+
+ // ISO 8601 date, best recognition by PHP's strtotime()
+ if (format === 'c') {
+ format = 'Y-m-dTH:i:sP';
+ }
+
+ for (var i = 0, length = format.length; i < length; i++) {
+ switch (format[i]) {
+ // seconds
+ case 's':
+ // `00` through `59`
+ char = ('0' + date.getSeconds().toString()).slice(-2);
+ break;
+
+ // minutes
+ case 'i':
+ // `00` through `59`
+ char = date.getMinutes();
+ if (char < 10) char = "0" + char;
+ break;
+
+ // hours
+ case 'a':
+ // `am` or `pm`
+ char = (date.getHours() > 11) ? 'pm' : 'am';
+ break;
+ case 'g':
+ // `1` through `12`
+ char = date.getHours();
+ if (char === 0) char = 12;
+ else if (char > 12) char -= 12;
+ break;
+ case 'h':
+ // `01` through `12`
+ char = date.getHours();
+ if (char === 0) char = 12;
+ else if (char > 12) char -= 12;
+
+ char = ('0' + char.toString()).slice(-2);
+ break;
+ case 'A':
+ // `AM` or `PM`
+ char = (date.getHours() > 11) ? 'PM' : 'AM';
+ break;
+ case 'G':
+ // `0` through `23`
+ char = date.getHours();
+ break;
+ case 'H':
+ // `00` through `23`
+ char = date.getHours();
+ char = ('0' + char.toString()).slice(-2);
+ break;
+
+ // day
+ case 'd':
+ // `01` through `31`
+ char = date.getDate();
+ char = ('0' + char.toString()).slice(-2);
+ break;
+ case 'j':
+ // `1` through `31`
+ char = date.getDate();
+ break;
+ case 'l':
+ // `Monday` through `Sunday` (localized)
+ char = Language.get('__days')[date.getDay()];
+ break;
+ case 'D':
+ // `Mon` through `Sun` (localized)
+ char = Language.get('__daysShort')[date.getDay()];
+ break;
+ case 'S':
+ // ignore english ordinal suffix
+ char = '';
+ break;
+
+ // month
+ case 'm':
+ // `01` through `12`
+ char = date.getMonth() + 1;
+ char = ('0' + char.toString()).slice(-2);
+ break;
+ case 'n':
+ // `1` through `12`
+ char = date.getMonth() + 1;
+ break;
+ case 'F':
+ // `January` through `December` (localized)
+ char = Language.get('__months')[date.getMonth()];
+ break;
+ case 'M':
+ // `Jan` through `Dec` (localized)
+ char = Language.get('__monthsShort')[date.getMonth()];
+ break;
+
+ // year
+ case 'y':
+ // `00` through `99`
+ char = date.getYear().toString().replace(/^\d{2}/, '');
+ break;
+ case 'Y':
+ // Examples: `1988` or `2015`
+ char = date.getFullYear();
+ break;
+
+ // timezone
+ case 'P':
+ var offset = date.getTimezoneOffset();
+ char = (offset > 0) ? '-' : '+';
+
+ offset = Math.abs(offset);
+
+ char += ('0' + (~~(offset / 60)).toString()).slice(-2);
+ char += ':';
+ char += ('0' + (offset % 60).toString()).slice(-2);
+
+ break;
+
+ // specials
+ case 'r':
+ char = date.toString();
+ break;
+ case 'U':
+ char = Math.round(date.getTime() / 1000);
+ break;
+
+ // escape sequence
+ case '\\':
+ char = '';
+ if (i + 1 < length) {
+ char = format[++i];
+ }
+ break;
+
+ default:
+ char = format[i];
+ break;
+ }
+
+ out += char;
+ }
+
+ return out;
+ },
+
+ /**
+ * Returns UTC timestamp, if date is not given, current time will be used.
+ *
+ * @param {Date} date target date
+ * @return {int} UTC timestamp in seconds
+ */
+ gmdate: function(date) {
+ if (!(date instanceof Date)) {
+ date = new Date();
+ }
+
+ return Math.round(Date.UTC(
+ date.getUTCFullYear(),
+ date.getUTCMonth(),
+ date.getUTCDay(),
+ date.getUTCHours(),
+ date.getUTCMinutes(),
+ date.getUTCSeconds()
+ ) / 1000);
+ },
+
+ /**
+ * Returns a `time` element based on the given date just like a `time`
+ * element created by `wcf\system\template\plugin\TimeModifierTemplatePlugin`.
+ *
+ * Note: The actual content of the element is empty and is expected
+ * to be automatically updated by `WoltLabSuite/Core/Date/Time/Relative`
+ * (for dates not in the future) after the DOM change listener has been triggered.
+ *
+ * @param {Date} date displayed date
+ * @return {HTMLElement} `time` element
+ */
+ getTimeElement: function(date) {
+ var time = elCreate('time');
+ time.className = 'datetime';
+
+ var formattedDate = this.formatDate(date);
+ var formattedTime = this.formatTime(date);
+
+ elAttr(time, 'datetime', this.format(date, 'c'));
+ elData(time, 'timestamp', (date.getTime() - date.getMilliseconds()) / 1000);
+ elData(time, 'date', formattedDate);
+ elData(time, 'time', formattedTime);
+ elData(time, 'offset', date.getTimezoneOffset() * 60); // PHP returns minutes, JavaScript returns seconds
+
+ if (date.getTime() > Date.now()) {
+ elData(time, 'is-future-date', 'true');
+
+ time.textContent = Language.get('wcf.date.dateTimeFormat').replace('%time%', formattedTime).replace('%date%', formattedDate);
+ }
+
+ return time;
+ },
+
+ /**
+ * Returns a Date object with precise offset (including timezone and local timezone).
+ *
+ * @param {int} timestamp timestamp in milliseconds
+ * @param {int} offset timezone offset in milliseconds
+ * @return {Date} localized date
+ */
+ getTimezoneDate: function(timestamp, offset) {
+ var date = new Date(timestamp);
+ var localOffset = date.getTimezoneOffset() * 60000;
+
+ return new Date((timestamp + localOffset + offset));
+ }
+ };
+
+ return DateUtil;
+});
+
+/**
+ * Provides an object oriented API on top of `setInterval`.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Timer/Repeating
+ */
+define('WoltLabSuite/Core/Timer/Repeating',[], function() {
+ "use strict";
+
+ /**
+ * Creates a new timer that executes the given `callback` every `delta` milliseconds.
+ * It will be created in started mode. Call `stop()` if necessary.
+ * The `callback` will be passed the owning instance of `Repeating`.
+ *
+ * @constructor
+ * @param {function(Repeating)} callback
+ * @param {int} delta
+ */
+ function Repeating(callback, delta) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback as first argument.");
+ }
+ if (delta < 0 || delta > 86400 * 1000) {
+ throw new RangeError("Invalid delta " + delta + ". Delta must be in the interval [0, 86400000].");
+ }
+
+ // curry callback with `this` as the first parameter
+ this._callback = callback.bind(undefined, this);
+
+ this._delta = delta;
+ this._timer = undefined;
+
+ this.restart();
+ }
+ Repeating.prototype = {
+ /**
+ * Stops the timer and restarts it. The next call will occur in `delta` milliseconds.
+ */
+ restart: function() {
+ this.stop();
+
+ this._timer = setInterval(this._callback, this._delta);
+ },
+
+ /**
+ * Stops the timer. It will no longer be called until you call `restart`.
+ */
+ stop: function() {
+ if (this._timer !== undefined) {
+ clearInterval(this._timer);
+ this._timer = undefined;
+ }
+ },
+
+ /**
+ * Changes the `delta` of the timer and `restart`s it.
+ *
+ * @param {int} delta New delta of the timer.
+ */
+ setDelta: function(delta) {
+ this._delta = delta;
+
+ this.restart();
+ }
+ };
+
+ return Repeating;
+});
+
+/**
+ * Transforms <time> elements to display the elapsed time relative to the current time.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Date/Time/Relative
+ */
+define('WoltLabSuite/Core/Date/Time/Relative',['Dom/ChangeListener', 'Language', 'WoltLabSuite/Core/Date/Util', 'WoltLabSuite/Core/Timer/Repeating'], function(DomChangeListener, Language, DateUtil, Repeating) {
+ "use strict";
+
+ var _elements = elByTag('time');
+ var _isActive = true;
+ var _isPending = false;
+ var _offset = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Date/Time/Relative
+ */
+ return {
+ /**
+ * Transforms <time> elements on init and binds event listeners.
+ */
+ setup: function() {
+ new Repeating(this._refresh.bind(this), 60000);
+
+ DomChangeListener.add('WoltLabSuite/Core/Date/Time/Relative', this._refresh.bind(this));
+
+ document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
+ },
+
+ _onVisibilityChange: function () {
+ if (document.hidden) {
+ _isActive = false;
+ _isPending = false;
+ }
+ else {
+ _isActive = true;
+
+ // force immediate refresh
+ if (_isPending) {
+ this._refresh();
+ _isPending = false;
+ }
+ }
+ },
+
+ _refresh: function() {
+ // activity is suspended while the tab is hidden, but force an
+ // immediate refresh once the page is active again
+ if (!_isActive) {
+ if (!_isPending) _isPending = true;
+ return;
+ }
+
+ var date = new Date();
+ var timestamp = (date.getTime() - date.getMilliseconds()) / 1000;
+ if (_offset === null) _offset = timestamp - window.TIME_NOW;
+
+ for (var i = 0, length = _elements.length; i < length; i++) {
+ var element = _elements[i];
+
+ if (!element.classList.contains('datetime') || elData(element, 'is-future-date')) continue;
+
+ var elTimestamp = ~~elData(element, 'timestamp') + _offset;
+ var elDate = elData(element, 'date');
+ var elTime = elData(element, 'time');
+ var elOffset = elData(element, 'offset');
+
+ if (!elAttr(element, 'title')) {
+ elAttr(element, 'title', Language.get('wcf.date.dateTimeFormat').replace(/%date%/, elDate).replace(/%time%/, elTime));
+ }
+
+ // timestamp is less than 60 seconds ago
+ if (elTimestamp >= timestamp || timestamp < (elTimestamp + 60)) {
+ element.textContent = Language.get('wcf.date.relative.now');
+ }
+ // timestamp is less than 60 minutes ago (display 1 hour ago rather than 60 minutes ago)
+ else if (timestamp < (elTimestamp + 3540)) {
+ var minutes = Math.max(Math.round((timestamp - elTimestamp) / 60), 1);
+ element.textContent = Language.get('wcf.date.relative.minutes', { minutes: minutes });
+ }
+ // timestamp is less than 24 hours ago
+ else if (timestamp < (elTimestamp + 86400)) {
+ var hours = Math.round((timestamp - elTimestamp) / 3600);
+ element.textContent = Language.get('wcf.date.relative.hours', { hours: hours });
+ }
+ // timestamp is less than 6 days ago
+ else if (timestamp < (elTimestamp + 518400)) {
+ var midnight = new Date(date.getFullYear(), date.getMonth(), date.getDate());
+ var days = Math.ceil((midnight / 1000 - elTimestamp) / 86400);
+
+ // get day of week
+ var dateObj = DateUtil.getTimezoneDate((elTimestamp * 1000), elOffset * 1000);
+ var dow = dateObj.getDay();
+ var day = Language.get('__days')[dow];
+
+ element.textContent = Language.get('wcf.date.relative.pastDays', { days: days, day: day, time: elTime });
+ }
+ // timestamp is between ~700 million years BC and last week
+ else {
+ element.textContent = Language.get('wcf.date.shortDateTimeFormat').replace(/%date%/, elDate).replace(/%time%/, elTime);
+ }
+ }
+ }
+ };
+});
+
+/**
+ * Provides a touch-friendly fullscreen menu.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/Abstract
+ */
+define('WoltLabSuite/Core/Ui/Page/Menu/Abstract',['Core', 'Environment', 'EventHandler', 'Language', 'ObjectMap', 'Dom/Traverse', 'Dom/Util', 'Ui/Screen'], function(Core, Environment, EventHandler, Language, ObjectMap, DomTraverse, DomUtil, UiScreen) {
+ "use strict";
+
+ var _pageContainer = elById('pageContainer');
+
+ /**
+ * Which edge of the menu is touched? Empty string
+ * if no menu is currently touched.
+ *
+ * One 'left', 'right' or ''.
+ */
+ var _androidTouching = '';
+
+ /**
+ * @param {string} eventIdentifier event namespace
+ * @param {string} elementId menu element id
+ * @param {string} buttonSelector CSS selector for toggle button
+ * @constructor
+ */
+ function UiPageMenuAbstract(eventIdentifier, elementId, buttonSelector) { this.init(eventIdentifier, elementId, buttonSelector); }
+ UiPageMenuAbstract.prototype = {
+ /**
+ * Initializes a touch-friendly fullscreen menu.
+ *
+ * @param {string} eventIdentifier event namespace
+ * @param {string} elementId menu element id
+ * @param {string} buttonSelector CSS selector for toggle button
+ */
+ init: function(eventIdentifier, elementId, buttonSelector) {
+ if (elData(document.body, 'template') === 'packageInstallationSetup') {
+ // work-around for WCFSetup on mobile
+ return;
+ }
+
+ this._activeList = [];
+ this._depth = 0;
+ this._enabled = true;
+ this._eventIdentifier = eventIdentifier;
+ this._items = new ObjectMap();
+ this._menu = elById(elementId);
+ this._removeActiveList = false;
+
+ var callbackOpen = this.open.bind(this);
+ this._button = elBySel(buttonSelector);
+ this._button.addEventListener(WCF_CLICK_EVENT, callbackOpen);
+
+ this._initItems();
+ this._initHeader();
+
+ EventHandler.add(this._eventIdentifier, 'open', callbackOpen);
+ EventHandler.add(this._eventIdentifier, 'close', this.close.bind(this));
+ EventHandler.add(this._eventIdentifier, 'updateButtonState', this._updateButtonState.bind(this));
+
+ var itemList, itemLists = elByClass('menuOverlayItemList', this._menu);
+ this._menu.addEventListener('animationend', (function() {
+ if (!this._menu.classList.contains('open')) {
+ for (var i = 0, length = itemLists.length; i < length; i++) {
+ itemList = itemLists[i];
+
+ // force the main list to be displayed
+ itemList.classList.remove('active');
+ itemList.classList.remove('hidden');
+ }
+ }
+ }).bind(this));
+
+ this._menu.children[0].addEventListener('transitionend', (function() {
+ this._menu.classList.add('allowScroll');
+
+ if (this._removeActiveList) {
+ this._removeActiveList = false;
+
+ var list = this._activeList.pop();
+ if (list) {
+ list.classList.remove('activeList');
+ }
+ }
+ }).bind(this));
+
+ var backdrop = elCreate('div');
+ backdrop.className = 'menuOverlayMobileBackdrop';
+ backdrop.addEventListener(WCF_CLICK_EVENT, this.close.bind(this));
+
+ DomUtil.insertAfter(backdrop, this._menu);
+
+ this._updateButtonState();
+
+ if (Environment.platform() === 'android') {
+ this._initializeAndroid();
+ }
+ },
+
+ /**
+ * Opens the menu.
+ *
+ * @param {Event} event event object
+ * @return {boolean} true if menu has been opened
+ */
+ open: function(event) {
+ if (!this._enabled) {
+ return false;
+ }
+
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ this._menu.classList.add('open');
+ this._menu.classList.add('allowScroll');
+ this._menu.children[0].classList.add('activeList');
+
+ UiScreen.scrollDisable();
+
+ _pageContainer.classList.add('menuOverlay-' + this._menu.id);
+
+ UiScreen.pageOverlayOpen();
+
+ return true;
+ },
+
+ /**
+ * Closes the menu.
+ *
+ * @param {(Event|boolean)} event event object or boolean true to force close the menu
+ * @return {boolean} true if menu was open
+ */
+ close: function(event) {
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ if (this._menu.classList.contains('open')) {
+ this._menu.classList.remove('open');
+
+ UiScreen.scrollEnable();
+ UiScreen.pageOverlayClose();
+
+ _pageContainer.classList.remove('menuOverlay-' + this._menu.id);
+
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Enables the touch menu.
+ */
+ enable: function() {
+ this._enabled = true;
+ },
+
+ /**
+ * Disables the touch menu.
+ */
+ disable: function() {
+ this._enabled = false;
+
+ this.close(true);
+ },
+
+ /**
+ * Initializes the Android Touch Menu.
+ */
+ _initializeAndroid: function() {
+ var appearsAt, backdrop, touchStart;
+ /** @const */ var AT_EDGE = 20;
+ /** @const */ var MOVED_HORIZONTALLY = 5;
+ /** @const */ var MOVED_VERTICALLY = 20;
+
+ // specify on which side of the page the menu appears
+ switch (this._menu.id) {
+ case 'pageUserMenuMobile':
+ appearsAt = 'right';
+ break;
+ case 'pageMainMenuMobile':
+ appearsAt = 'left';
+ break;
+ default:
+ return;
+ }
+
+ backdrop = this._menu.nextElementSibling;
+
+ // horizontal position of the touch start
+ touchStart = null;
+
+ document.addEventListener('touchstart', (function(event) {
+ var touches, isOpen, isLeftEdge, isRightEdge;
+ touches = event.touches;
+
+ isOpen = this._menu.classList.contains('open');
+
+ // check whether we touch the edges of the menu
+ if (appearsAt === 'left') {
+ isLeftEdge = !isOpen && (touches[0].clientX < AT_EDGE);
+ isRightEdge = isOpen && (Math.abs(this._menu.offsetWidth - touches[0].clientX) < AT_EDGE);
+ }
+ else if (appearsAt === 'right') {
+ isLeftEdge = isOpen && (Math.abs(document.body.clientWidth - this._menu.offsetWidth - touches[0].clientX) < AT_EDGE);
+ isRightEdge = !isOpen && ((document.body.clientWidth - touches[0].clientX) < AT_EDGE);
+ }
+
+ // abort if more than one touch
+ if (touches.length > 1) {
+ if (_androidTouching) {
+ Core.triggerEvent(document, 'touchend');
+ }
+ return;
+ }
+
+ // break if a touch is in progress
+ if (_androidTouching) return;
+ // break if no edge has been touched
+ if (!isLeftEdge && !isRightEdge) return;
+ // break if a different menu is open
+ if (UiScreen.pageOverlayIsActive()) {
+ var found = false;
+ for (var i = 0; i < _pageContainer.classList.length; i++) {
+ if (_pageContainer.classList[i] === 'menuOverlay-' + this._menu.id) {
+ found = true;
+ }
+ }
+ if (!found) return;
+ }
+ // break if redactor is in use
+ if (document.documentElement.classList.contains('redactorActive')) return;
+
+ touchStart = {
+ x: touches[0].clientX,
+ y: touches[0].clientY
+ };
+
+ if (isLeftEdge) _androidTouching = 'left';
+ if (isRightEdge) _androidTouching = 'right';
+ }).bind(this));
+
+ document.addEventListener('touchend', (function(event) {
+ // break if we did not start a touch
+ if (!_androidTouching || touchStart === null) return;
+
+ // break if the menu did not even start opening
+ if (!this._menu.classList.contains('open')) {
+ // reset
+ touchStart = null;
+ _androidTouching = '';
+ return;
+ }
+
+ // last known position of the finger
+ var position;
+ if (event) {
+ position = event.changedTouches[0].clientX;
+ }
+ else {
+ position = touchStart.x;
+ }
+
+ // clean up touch styles
+ this._menu.classList.add('androidMenuTouchEnd');
+ this._menu.style.removeProperty('transform');
+ backdrop.style.removeProperty(appearsAt);
+ this._menu.addEventListener('transitionend', (function() {
+ this._menu.classList.remove('androidMenuTouchEnd');
+ }).bind(this), { once: true });
+
+ // check whether the user moved the finger far enough
+ if (appearsAt === 'left') {
+ if (_androidTouching === 'left' && position < (touchStart.x + 100)) this.close();
+ if (_androidTouching === 'right' && position < (touchStart.x - 100)) this.close();
+ }
+ else if (appearsAt === 'right') {
+ if (_androidTouching === 'left' && position > (touchStart.x + 100)) this.close();
+ if (_androidTouching === 'right' && position > (touchStart.x - 100)) this.close();
+ }
+
+ // reset
+ touchStart = null;
+ _androidTouching = '';
+ }).bind(this));
+
+ document.addEventListener('touchmove', (function(event) {
+ // break if we did not start a touch
+ if (!_androidTouching || touchStart === null) return;
+
+ var touches = event.touches;
+
+ // check whether the user started moving in the correct direction
+ // this avoids false positives, in case the user just wanted to tap
+ var movedFromEdge = false, movedVertically = false;
+ if (_androidTouching === 'left') movedFromEdge = touches[0].clientX > (touchStart.x + MOVED_HORIZONTALLY);
+ if (_androidTouching === 'right') movedFromEdge = touches[0].clientX < (touchStart.x - MOVED_HORIZONTALLY);
+ movedVertically = Math.abs(touches[0].clientY - touchStart.y) > MOVED_VERTICALLY;
+
+ var isOpen = this._menu.classList.contains('open');
+
+ if (!isOpen && movedFromEdge && !movedVertically) {
+ // the menu is not yet open, but the user moved into the right direction
+ this.open();
+ isOpen = true;
+ }
+
+ if (isOpen) {
+ // update CSS to the new finger position
+ var position = touches[0].clientX;
+ if (appearsAt === 'right') position = document.body.clientWidth - position;
+ if (position > this._menu.offsetWidth) position = this._menu.offsetWidth;
+ if (position < 0) position = 0;
+ this._menu.style.setProperty('transform', 'translateX(' + (appearsAt === 'left' ? 1 : -1) * (position - this._menu.offsetWidth) + 'px)');
+ backdrop.style.setProperty(appearsAt, Math.min(this._menu.offsetWidth, position) + 'px');
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Initializes all menu items.
+ *
+ * @protected
+ */
+ _initItems: function() {
+ elBySelAll('.menuOverlayItemLink', this._menu, this._initItem.bind(this));
+ },
+
+ /**
+ * Initializes a single menu item.
+ *
+ * @param {Element} item menu item
+ * @protected
+ */
+ _initItem: function(item) {
+ // check if it should contain a 'more' link w/ an external callback
+ var parent = item.parentNode;
+ var more = elData(parent, 'more');
+ if (more) {
+ item.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ EventHandler.fire(this._eventIdentifier, 'more', {
+ handler: this,
+ identifier: more,
+ item: item,
+ parent: parent
+ });
+ }).bind(this));
+
+ return;
+ }
+
+ var itemList = item.nextElementSibling, wrapper;
+ if (itemList === null) {
+ return;
+ }
+
+ // handle static items with an icon-type button next to it (acp menu)
+ if (itemList.nodeName !== 'OL' && itemList.classList.contains('menuOverlayItemLinkIcon')) {
+ // add wrapper
+ wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+ parent.insertBefore(wrapper, item);
+ wrapper.appendChild(item);
+
+ while (wrapper.nextElementSibling) {
+ wrapper.appendChild(wrapper.nextElementSibling);
+ }
+
+ return;
+ }
+
+ var isLink = (elAttr(item, 'href') !== '#');
+ var parentItemList = parent.parentNode;
+ var itemTitle = elData(itemList, 'title');
+
+ this._items.set(item, {
+ itemList: itemList,
+ parentItemList: parentItemList
+ });
+
+ if (itemTitle === '') {
+ itemTitle = DomTraverse.childByClass(item, 'menuOverlayItemTitle').textContent;
+ elData(itemList, 'title', itemTitle);
+ }
+
+ var callbackLink = this._showItemList.bind(this, item);
+ if (isLink) {
+ wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+ parent.insertBefore(wrapper, item);
+ wrapper.appendChild(item);
+
+ var moreLink = elCreate('a');
+ elAttr(moreLink, 'href', '#');
+ moreLink.className = 'menuOverlayItemLinkIcon' + (item.classList.contains('active') ? ' active' : '');
+ moreLink.innerHTML = '<span class="icon icon24 fa-angle-right"></span>';
+ moreLink.addEventListener(WCF_CLICK_EVENT, callbackLink);
+ wrapper.appendChild(moreLink);
+ }
+ else {
+ item.classList.add('menuOverlayItemLinkMore');
+ item.addEventListener(WCF_CLICK_EVENT, callbackLink);
+ }
+
+ var backLinkItem = elCreate('li');
+ backLinkItem.className = 'menuOverlayHeader';
+
+ wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+
+ var backLink = elCreate('a');
+ elAttr(backLink, 'href', '#');
+ backLink.className = 'menuOverlayItemLink menuOverlayBackLink';
+ backLink.textContent = elData(parentItemList, 'title');
+ backLink.addEventListener(WCF_CLICK_EVENT, this._hideItemList.bind(this, item));
+
+ var closeLink = elCreate('a');
+ elAttr(closeLink, 'href', '#');
+ closeLink.className = 'menuOverlayItemLinkIcon';
+ closeLink.innerHTML = '<span class="icon icon24 fa-times"></span>';
+ closeLink.addEventListener(WCF_CLICK_EVENT, this.close.bind(this));
+
+ wrapper.appendChild(backLink);
+ wrapper.appendChild(closeLink);
+ backLinkItem.appendChild(wrapper);
+
+ itemList.insertBefore(backLinkItem, itemList.firstElementChild);
+
+ if (!backLinkItem.nextElementSibling.classList.contains('menuOverlayTitle')) {
+ var titleItem = elCreate('li');
+ titleItem.className = 'menuOverlayTitle';
+ var title = elCreate('span');
+ title.textContent = itemTitle;
+ titleItem.appendChild(title);
+
+ itemList.insertBefore(titleItem, backLinkItem.nextElementSibling);
+ }
+ },
+
+ /**
+ * Renders the menu item list header.
+ *
+ * @protected
+ */
+ _initHeader: function() {
+ var listItem = elCreate('li');
+ listItem.className = 'menuOverlayHeader';
+
+ var wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+ listItem.appendChild(wrapper);
+
+ var logoWrapper = elCreate('span');
+ logoWrapper.className = 'menuOverlayLogoWrapper';
+ wrapper.appendChild(logoWrapper);
+
+ var logo = elCreate('span');
+ logo.className = 'menuOverlayLogo';
+ logo.style.setProperty('background-image', 'url("' + elData(this._menu, 'page-logo') + '")', '');
+ logoWrapper.appendChild(logo);
+
+ var closeLink = elCreate('a');
+ elAttr(closeLink, 'href', '#');
+ closeLink.className = 'menuOverlayItemLinkIcon';
+ closeLink.innerHTML = '<span class="icon icon24 fa-times"></span>';
+ closeLink.addEventListener(WCF_CLICK_EVENT, this.close.bind(this));
+ wrapper.appendChild(closeLink);
+
+ var list = DomTraverse.childByClass(this._menu, 'menuOverlayItemList');
+ list.insertBefore(listItem, list.firstElementChild);
+ },
+
+ /**
+ * Hides an item list, return to the parent item list.
+ *
+ * @param {Element} item menu item
+ * @param {Event} event event object
+ * @protected
+ */
+ _hideItemList: function(item, event) {
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ this._menu.classList.remove('allowScroll');
+ this._removeActiveList = true;
+
+ var data = this._items.get(item);
+ data.parentItemList.classList.remove('hidden');
+
+ this._updateDepth(false);
+ },
+
+ /**
+ * Shows the child item list.
+ *
+ * @param {Element} item menu item
+ * @param event
+ * @private
+ */
+ _showItemList: function(item, event) {
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ var data = this._items.get(item);
+
+ var load = elData(data.itemList, 'load');
+ if (load) {
+ if (!elDataBool(item, 'loaded')) {
+ var icon = event.currentTarget.firstElementChild;
+ if (icon.classList.contains('fa-angle-right')) {
+ icon.classList.remove('fa-angle-right');
+ icon.classList.add('fa-spinner');
+ }
+
+ EventHandler.fire(this._eventIdentifier, 'load_' + load);
+
+ return;
+ }
+ }
+
+ this._menu.classList.remove('allowScroll');
+
+ data.itemList.classList.add('activeList');
+ data.parentItemList.classList.add('hidden');
+
+ this._activeList.push(data.itemList);
+
+ this._updateDepth(true);
+ },
+
+ _updateDepth: function(increase) {
+ this._depth += (increase) ? 1 : -1;
+
+ var offset = this._depth * -100;
+ if (Language.get('wcf.global.pageDirection') === 'rtl') {
+ // reverse logic for RTL
+ offset *= -1;
+ }
+
+ this._menu.children[0].style.setProperty('transform', 'translateX(' + offset + '%)', '');
+ },
+
+ _updateButtonState: function() {
+ var hasNewContent = false;
+ elBySelAll('.badgeUpdate', this._menu, function (badge) {
+ if (~~badge.textContent > 0) {
+ hasNewContent = true;
+ }
+ });
+
+ this._button.classList[(hasNewContent ? 'add' : 'remove')]('pageMenuMobileButtonHasContent');
+ }
+ };
+
+ return UiPageMenuAbstract;
+});
+
+/**
+ * Provides the touch-friendly fullscreen main menu.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/Main
+ */
+define('WoltLabSuite/Core/Ui/Page/Menu/Main',['Core', 'Language', 'Dom/Traverse', './Abstract'], function(Core, Language, DomTraverse, UiPageMenuAbstract) {
+ "use strict";
+
+ var _optionsTitle = null, _hasItems = null, _list = null, _navigationList = null, _callbackClose = null;
+
+ /**
+ * @constructor
+ */
+ function UiPageMenuMain() { this.init(); }
+ Core.inherit(UiPageMenuMain, UiPageMenuAbstract, {
+ /**
+ * Initializes the touch-friendly fullscreen main menu.
+ */
+ init: function() {
+ UiPageMenuMain._super.prototype.init.call(
+ this,
+ 'com.woltlab.wcf.MainMenuMobile',
+ 'pageMainMenuMobile',
+ '#pageHeader .mainMenu'
+ );
+
+ _optionsTitle = elById('pageMainMenuMobilePageOptionsTitle');
+ if (_optionsTitle !== null) {
+ _list = DomTraverse.childByClass(_optionsTitle, 'menuOverlayItemList');
+ _navigationList = elBySel('.jsPageNavigationIcons');
+
+ _callbackClose = (function(event) {
+ this.close();
+ event.stopPropagation();
+ }).bind(this);
+ }
+
+ elAttr(this._button, 'aria-label', Language.get('wcf.menu.page'));
+ elAttr(this._button, 'role', 'button');
+ },
+
+ open: function (event) {
+ if (!UiPageMenuMain._super.prototype.open.call(this, event)) {
+ return false;
+ }
+
+ if (_optionsTitle === null) {
+ return true;
+ }
+
+ _hasItems = _navigationList && _navigationList.childElementCount > 0;
+
+ if (_hasItems) {
+ var item, link;
+ while (_navigationList.childElementCount) {
+ item = _navigationList.children[0];
+
+ item.classList.add('menuOverlayItem');
+ item.classList.add('menuOverlayItemOption');
+ item.addEventListener(WCF_CLICK_EVENT, _callbackClose);
+
+ link = item.children[0];
+ link.classList.add('menuOverlayItemLink');
+ link.classList.add('box24');
+
+ link.children[1].classList.remove('invisible');
+ link.children[1].classList.add('menuOverlayItemTitle');
+
+ _optionsTitle.parentNode.insertBefore(item, _optionsTitle.nextSibling);
+ }
+
+ elShow(_optionsTitle);
+ }
+ else {
+ elHide(_optionsTitle);
+ }
+
+ return true;
+ },
+
+ close: function(event) {
+ if (!UiPageMenuMain._super.prototype.close.call(this, event)) {
+ return false;
+ }
+
+ if (_hasItems) {
+ elHide(_optionsTitle);
+
+ var item = _optionsTitle.nextElementSibling;
+ var link;
+ while (item && item.classList.contains('menuOverlayItemOption')) {
+ item.classList.remove('menuOverlayItem');
+ item.classList.remove('menuOverlayItemOption');
+ item.removeEventListener(WCF_CLICK_EVENT, _callbackClose);
+
+ link = item.children[0];
+ link.classList.remove('menuOverlayItemLink');
+ link.classList.remove('box24');
+
+ link.children[1].classList.add('invisible');
+ link.children[1].classList.remove('menuOverlayItemTitle');
+
+ _navigationList.appendChild(item);
+
+ item = item.nextElementSibling;
+ }
+ }
+
+ return true;
+ }
+ });
+
+ return UiPageMenuMain;
+});
+
+/**
+ * Provides the touch-friendly fullscreen user menu.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/User
+ */
+define('WoltLabSuite/Core/Ui/Page/Menu/User',['Core', 'EventHandler', 'Language', './Abstract'], function(Core, EventHandler, Language, UiPageMenuAbstract) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiPageMenuUser() { this.init(); }
+ Core.inherit(UiPageMenuUser, UiPageMenuAbstract, {
+ /**
+ * Initializes the touch-friendly fullscreen user menu.
+ */
+ init: function() {
+ // check if user menu is actually empty
+ var menu = elBySel('#pageUserMenuMobile > .menuOverlayItemList');
+ if (menu.childElementCount === 1 && menu.children[0].classList.contains('menuOverlayTitle')) {
+ elBySel('#pageHeader .userPanel').classList.add('hideUserPanel');
+ return;
+ }
+
+ UiPageMenuUser._super.prototype.init.call(
+ this,
+ 'com.woltlab.wcf.UserMenuMobile',
+ 'pageUserMenuMobile',
+ '#pageHeader .userPanel'
+ );
+
+ EventHandler.add('com.woltlab.wcf.userMenu', 'updateBadge', (function (data) {
+ elBySelAll('.menuOverlayItemBadge', this._menu, (function (item) {
+ if (elData(item, 'badge-identifier') === data.identifier) {
+ var badge = elBySel('.badge', item);
+ if (data.count) {
+ if (badge === null) {
+ badge = elCreate('span');
+ badge.className = 'badge badgeUpdate';
+ item.appendChild(badge);
+ }
+
+ badge.textContent = data.count;
+ }
+ else if (badge !== null) {
+ elRemove(badge);
+ }
+
+ this._updateButtonState();
+ }
+ }).bind(this));
+ }).bind(this));
+
+ elAttr(this._button, 'aria-label', Language.get('wcf.menu.user'));
+ elAttr(this._button, 'role', 'button');
+ },
+
+ close: function (event) {
+ var dropdown = WCF.Dropdown.Interactive.Handler.getOpenDropdown();
+ if (dropdown) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ dropdown.close();
+ }
+ else {
+ UiPageMenuUser._super.prototype.close.call(this, event);
+ }
+ }
+ });
+
+ return UiPageMenuUser;
+});
+
+/**
+ * Simple interface to work with reusable dropdowns that are not bound to a specific item.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dropdown/Reusable
+ */
+define('WoltLabSuite/Core/Ui/Dropdown/Reusable',['Dictionary', 'Ui/SimpleDropdown'], function(Dictionary, UiSimpleDropdown) {
+ "use strict";
+
+ var _dropdowns = new Dictionary();
+ var _ghostElementId = 0;
+
+ /**
+ * Returns dropdown name by internal identifier.
+ *
+ * @param {string} identifier internal identifier
+ * @returns {string} dropdown name
+ */
+ function _getDropdownName(identifier) {
+ if (!_dropdowns.has(identifier)) {
+ throw new Error("Unknown dropdown identifier '" + identifier + "'");
+ }
+
+ return _dropdowns.get(identifier);
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dropdown/Reusable
+ */
+ return {
+ /**
+ * Initializes a new reusable dropdown.
+ *
+ * @param {string} identifier internal identifier
+ * @param {Element} menu dropdown menu element
+ */
+ init: function(identifier, menu) {
+ if (_dropdowns.has(identifier)) {
+ return;
+ }
+
+ var ghostElement = elCreate('div');
+ ghostElement.id = 'reusableDropdownGhost' + _ghostElementId++;
+
+ UiSimpleDropdown.initFragment(ghostElement, menu);
+
+ _dropdowns.set(identifier, ghostElement.id);
+ },
+
+ /**
+ * Returns the dropdown menu element.
+ *
+ * @param {string} identifier internal identifier
+ * @returns {Element} dropdown menu element
+ */
+ getDropdownMenu: function(identifier) {
+ return UiSimpleDropdown.getDropdownMenu(_getDropdownName(identifier));
+ },
+
+ /**
+ * Registers a callback invoked upon open and close.
+ *
+ * @param {string} identifier internal identifier
+ * @param {function} callback callback function
+ */
+ registerCallback: function(identifier, callback) {
+ UiSimpleDropdown.registerCallback(_getDropdownName(identifier), callback);
+ },
+
+ /**
+ * Toggles a dropdown.
+ *
+ * @param {string} identifier internal identifier
+ * @param {Element} referenceElement reference element used for alignment
+ */
+ toggleDropdown: function(identifier, referenceElement) {
+ UiSimpleDropdown.toggleDropdown(_getDropdownName(identifier), referenceElement);
+ }
+ };
+});
+
+/**
+ * Modifies the interface to provide a better usability for mobile devices.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Mobile
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Mobile',[ 'Core', 'Environment', 'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse', 'Ui/Alignment', 'Ui/CloseOverlay', 'Ui/Screen', './Page/Menu/Main', './Page/Menu/User', 'WoltLabSuite/Core/Ui/Dropdown/Reusable'],
+ function(Core, Environment, EventHandler, Language, List, DomChangeListener, DomTraverse, UiAlignment, UiCloseOverlay, UiScreen, UiPageMenuMain, UiPageMenuUser, UiDropdownReusable)
+{
+ "use strict";
+
+ var _buttonGroupNavigations = elByClass('buttonGroupNavigation');
+ var _callbackCloseDropdown = null;
+ var _dropdownMenu = null;
+ var _dropdownMenuMessage = null;
+ var _enabled = false;
+ var _knownMessages = new List();
+ var _main = null;
+ var _messages = elByClass('message');
+ var _options = {};
+ var _pageMenuMain = null;
+ var _pageMenuUser = null;
+ var _messageGroups = null;
+ var _sidebars = [];
+ var _sidebarXsEnabled = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Mobile
+ */
+ return {
+ /**
+ * Initializes the mobile UI.
+ *
+ * @param {Object=} options initialization options
+ */
+ setup: function(options) {
+ _options = Core.extend({
+ enableMobileMenu: true
+ }, options);
+
+ _main = elById('main');
+
+ elBySelAll('.sidebar', undefined, function (sidebar) {
+ _sidebars.push(sidebar);
+ });
+
+ if (Environment.touch()) {
+ document.documentElement.classList.add('touch');
+ }
+
+ if (Environment.platform() !== 'desktop') {
+ document.documentElement.classList.add('mobile');
+ }
+
+ var messageGroupList = elBySel('.messageGroupList');
+ if (messageGroupList) _messageGroups = elByClass('messageGroup', messageGroupList);
+
+ UiScreen.on('screen-md-down', {
+ match: this.enable.bind(this),
+ unmatch: this.disable.bind(this),
+ setup: this._init.bind(this)
+ });
+
+ UiScreen.on('screen-sm-down', {
+ match: this.enableShadow.bind(this),
+ unmatch: this.disableShadow.bind(this),
+ setup: this.enableShadow.bind(this)
+ });
+
+ UiScreen.on('screen-xs', {
+ match: this._enableSidebarXS.bind(this),
+ unmatch: this._disableSidebarXS.bind(this),
+ setup: this._setupSidebarXS.bind(this)
+ });
+ },
+
+ /**
+ * Enables the mobile UI.
+ */
+ enable: function() {
+ _enabled = true;
+
+ if (_options.enableMobileMenu) {
+ _pageMenuMain.enable();
+ _pageMenuUser.enable();
+ }
+ },
+
+ /**
+ * Enables shadow links for larger click areas on messages.
+ */
+ enableShadow: function () {
+ if (_messageGroups) this.rebuildShadow(_messageGroups, '.messageGroupLink');
+ },
+
+ /**
+ * Disables the mobile UI.
+ */
+ disable: function() {
+ _enabled = false;
+
+ if (_options.enableMobileMenu) {
+ _pageMenuMain.disable();
+ _pageMenuUser.disable();
+ }
+ },
+
+ /**
+ * Disables shadow links.
+ */
+ disableShadow: function () {
+ if (_messageGroups) this.removeShadow(_messageGroups);
+
+ if (_dropdownMenu) _callbackCloseDropdown();
+ },
+
+ _init: function() {
+ _enabled = true;
+
+ this._initSearchBar();
+ this._initButtonGroupNavigation();
+ this._initMessages();
+ this._initMobileMenu();
+
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Mobile', this._closeAllMenus.bind(this));
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Mobile', (function() {
+ this._initButtonGroupNavigation();
+ this._initMessages();
+ }).bind(this));
+ },
+
+ _initSearchBar: function() {
+ var _searchBar = elById('pageHeaderSearch');
+ var _searchInput = elById('pageHeaderSearchInput');
+
+ var scrollTop = null;
+
+ EventHandler.add('com.woltlab.wcf.MainMenuMobile', 'more', function(data) {
+ if (data.identifier === 'com.woltlab.wcf.search') {
+ data.handler.close(true);
+
+ if (Environment.platform() === 'ios') {
+ scrollTop = document.body.scrollTop;
+ UiScreen.scrollDisable();
+ }
+
+ _searchBar.style.setProperty('top', elById('pageHeader').offsetHeight + 'px', '');
+ _searchBar.classList.add('open');
+ _searchInput.focus();
+
+ if (Environment.platform() === 'ios') {
+ document.body.scrollTop = 0;
+ }
+ }
+ });
+
+ _main.addEventListener(WCF_CLICK_EVENT, function() {
+ if (_searchBar) _searchBar.classList.remove('open');
+
+ if (Environment.platform() === 'ios' && scrollTop !== null) {
+ UiScreen.scrollEnable();
+ document.body.scrollTop = scrollTop;
+
+ scrollTop = null;
+ }
+ });
+ },
+
+ _initButtonGroupNavigation: function() {
+ for (var i = 0, length = _buttonGroupNavigations.length; i < length; i++) {
+ var navigation = _buttonGroupNavigations[i];
+
+ if (navigation.classList.contains('jsMobileButtonGroupNavigation')) continue;
+ else navigation.classList.add('jsMobileButtonGroupNavigation');
+
+ var list = elBySel('.buttonList', navigation);
+ if (list.childElementCount === 0) {
+ // ignore objects without options
+ continue;
+ }
+
+ navigation.parentNode.classList.add('hasMobileNavigation');
+
+ var button = elCreate('a');
+ button.className = 'dropdownLabel';
+
+ var span = elCreate('span');
+ span.className = 'icon icon24 fa-ellipsis-v';
+ button.appendChild(span);
+
+ (function(navigation, button, list) {
+ button.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ navigation.classList.toggle('open');
+ });
+
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.stopPropagation();
+
+ navigation.classList.remove('open');
+ });
+ })(navigation, button, list);
+
+ navigation.insertBefore(button, navigation.firstChild);
+ }
+ },
+
+ _initMessages: function() {
+ Array.prototype.forEach.call(_messages, (function(message) {
+ if (_knownMessages.has(message)) {
+ return;
+ }
+
+ var navigation = elBySel('.jsMobileNavigation', message);
+ if (navigation) {
+ navigation.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.stopPropagation();
+
+ // mimic dropdown behavior
+ window.setTimeout(function () {
+ navigation.classList.remove('open');
+ }, 10);
+ });
+
+ var quickOptions = elBySel('.messageQuickOptions', message);
+ if (quickOptions && navigation.childElementCount) {
+ quickOptions.classList.add('active');
+ quickOptions.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ if (_enabled && event.target.nodeName !== 'LABEL' && event.target.nodeName !== 'INPUT') {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this._toggleMobileNavigation(message, quickOptions, navigation);
+ }
+ }).bind(this));
+ }
+ }
+
+ _knownMessages.add(message);
+ }).bind(this));
+ },
+
+ _initMobileMenu: function() {
+ if (_options.enableMobileMenu) {
+ _pageMenuMain = new UiPageMenuMain();
+ _pageMenuUser = new UiPageMenuUser();
+ }
+ },
+
+ _closeAllMenus: function() {
+ elBySelAll('.jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open', null, function (menu) {
+ menu.classList.remove('open');
+ });
+
+ if (_enabled && _dropdownMenu) _callbackCloseDropdown();
+ },
+
+ rebuildShadow: function(elements, linkSelector) {
+ var element, parent, shadow;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ parent = element.parentNode;
+
+ shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
+ if (shadow === null) {
+ if (elBySel(linkSelector, element).href) {
+ shadow = elCreate('a');
+ shadow.className = 'mobileLinkShadow';
+ shadow.href = elBySel(linkSelector, element).href;
+
+ parent.appendChild(shadow);
+ parent.classList.add('mobileLinkShadowContainer');
+ }
+ }
+ }
+ },
+
+ removeShadow: function(elements) {
+ var element, parent, shadow;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ parent = element.parentNode;
+
+ if (parent.classList.contains('mobileLinkShadowContainer')) {
+ shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
+ if (shadow !== null) {
+ elRemove(shadow);
+ }
+
+ parent.classList.remove('mobileLinkShadowContainer');
+ }
+ }
+ },
+
+ _enableSidebarXS: function() {
+ _sidebarXsEnabled = true;
+ },
+
+ _disableSidebarXS: function() {
+ _sidebarXsEnabled = false;
+
+ _sidebars.forEach(function (sidebar) {
+ sidebar.classList.remove('open');
+ });
+ },
+
+ _setupSidebarXS: function() {
+ _sidebars.forEach(function (sidebar) {
+ sidebar.addEventListener('mousedown', function(event) {
+ if (_sidebarXsEnabled && event.target === sidebar) {
+ event.preventDefault();
+
+ sidebar.classList.toggle('open');
+ }
+ });
+ });
+
+ _sidebarXsEnabled = true;
+ },
+
+ _toggleMobileNavigation: function (message, quickOptions, navigation) {
+ if (_dropdownMenu === null) {
+ _dropdownMenu = elCreate('ul');
+ _dropdownMenu.className = 'dropdownMenu';
+
+ UiDropdownReusable.init('com.woltlab.wcf.jsMobileNavigation', _dropdownMenu);
+
+ _callbackCloseDropdown = function () {
+ _dropdownMenu.classList.remove('dropdownOpen');
+ }
+ }
+ else if (_dropdownMenu.classList.contains('dropdownOpen')) {
+ _callbackCloseDropdown();
+
+ if (_dropdownMenuMessage === message) {
+ // toggle behavior
+ return;
+ }
+ }
+
+ _dropdownMenu.innerHTML = '';
+ UiCloseOverlay.execute();
+
+ this._rebuildMobileNavigation(navigation);
+
+ var previousNavigation = navigation.previousElementSibling;
+ if (previousNavigation && previousNavigation.classList.contains('messageFooterButtonsExtra')) {
+ var divider = elCreate('li');
+ divider.className = 'dropdownDivider';
+ _dropdownMenu.appendChild(divider);
+
+ this._rebuildMobileNavigation(previousNavigation);
+ }
+
+ UiAlignment.set(_dropdownMenu, quickOptions, {
+ horizontal: 'right',
+ allowFlip: 'vertical'
+ });
+ _dropdownMenu.classList.add('dropdownOpen');
+
+ _dropdownMenuMessage = message;
+ },
+
+ _rebuildMobileNavigation: function (navigation) {
+ elBySelAll('.button:not(.ignoreMobileNavigation)', navigation, function (button) {
+ var item = elCreate('li');
+ if (button.classList.contains('active')) item.className = 'active';
+ item.innerHTML = '<a href="#">' + elBySel('span:not(.icon)', button).textContent + '</a>';
+ item.children[0].addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (button.nodeName === 'A') button.click();
+ else Core.triggerEvent(button, WCF_CLICK_EVENT);
+
+ _callbackCloseDropdown();
+ });
+
+ _dropdownMenu.appendChild(item);
+ });
+ }
+ };
+});
+
+/**
+ * Simple tab menu implementation with a straight-forward logic.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/TabMenu/Simple
+ */
+define('WoltLabSuite/Core/Ui/TabMenu/Simple',['Dictionary', 'EventHandler', 'Dom/Traverse', 'Dom/Util'], function(Dictionary, EventHandler, DomTraverse, DomUtil) {
+ "use strict";
+
+ /**
+ * @param {Element} container container element
+ * @constructor
+ */
+ function TabMenuSimple(container) {
+ this._container = container;
+ this._containers = new Dictionary();
+ this._isLegacy = null;
+ this._store = null;
+ this._tabs = new Dictionary();
+ }
+
+ TabMenuSimple.prototype = {
+ /**
+ * Validates the properties and DOM structure of this container.
+ *
+ * Expected DOM:
+ * <div class="tabMenuContainer">
+ * <nav>
+ * <ul>
+ * <li data-name="foo"><a>bar</a></li>
+ * </ul>
+ * </nav>
+ *
+ * <div id="foo">baz</div>
+ * </div>
+ *
+ * @return {boolean} false if any properties are invalid or the DOM does not match the expectations
+ */
+ validate: function() {
+ if (!this._container.classList.contains('tabMenuContainer')) {
+ return false;
+ }
+
+ var nav = DomTraverse.childByTag(this._container, 'NAV');
+ if (nav === null) {
+ return false;
+ }
+
+ // get children
+ var tabs = elByTag('li', nav);
+ if (tabs.length === 0) {
+ return false;
+ }
+
+ var container, containers = DomTraverse.childrenByTag(this._container, 'DIV'), name, i, length;
+ for (i = 0, length = containers.length; i < length; i++) {
+ container = containers[i];
+ name = elData(container, 'name');
+
+ if (!name) {
+ name = DomUtil.identify(container);
+ }
+
+ elData(container, 'name', name);
+ this._containers.set(name, container);
+ }
+
+ var containerId = this._container.id, tab;
+ for (i = 0, length = tabs.length; i < length; i++) {
+ tab = tabs[i];
+ name = this._getTabName(tab);
+
+ if (!name) {
+ continue;
+ }
+
+ if (this._tabs.has(name)) {
+ throw new Error("Tab names must be unique, li[data-name='" + name + "'] (tab menu id: '" + containerId + "') exists more than once.");
+ }
+
+ container = this._containers.get(name);
+ if (container === undefined) {
+ throw new Error("Expected content element for li[data-name='" + name + "'] (tab menu id: '" + containerId + "').");
+ }
+ else if (container.parentNode !== this._container) {
+ throw new Error("Expected content element '" + name + "' (tab menu id: '" + containerId + "') to be a direct children.");
+ }
+
+ // check if tab holds exactly one children which is an anchor element
+ if (tab.childElementCount !== 1 || tab.children[0].nodeName !== 'A') {
+ throw new Error("Expected exactly one <a> as children for li[data-name='" + name + "'] (tab menu id: '" + containerId + "').");
+ }
+
+ this._tabs.set(name, tab);
+ }
+
+ if (!this._tabs.size) {
+ throw new Error("Expected at least one tab (tab menu id: '" + containerId + "').");
+ }
+
+ if (this._isLegacy) {
+ elData(this._container, 'is-legacy', true);
+
+ this._tabs.forEach(function(tab, name) {
+ elAttr(tab, 'aria-controls', name);
+ });
+ }
+
+ return true;
+ },
+
+ /**
+ * Initializes this tab menu.
+ *
+ * @param {Dictionary=} oldTabs previous list of tabs
+ * @return {?Element} parent tab for selection or null
+ */
+ init: function(oldTabs) {
+ oldTabs = oldTabs || null;
+
+ // bind listeners
+ this._tabs.forEach((function(tab) {
+ if (!oldTabs || oldTabs.get(elData(tab, 'name')) !== tab) {
+ tab.children[0].addEventListener(WCF_CLICK_EVENT, this._onClick.bind(this));
+ }
+ }).bind(this));
+
+ var returnValue = null;
+ if (!oldTabs) {
+ var hash = TabMenuSimple.getIdentifierFromHash();
+ var selectTab = null;
+ if (hash !== '') {
+ selectTab = this._tabs.get(hash);
+
+ // check for parent tab menu
+ if (selectTab && this._container.parentNode.classList.contains('tabMenuContainer')) {
+ returnValue = this._container;
+ }
+ }
+
+ if (!selectTab) {
+ var preselect = elData(this._container, 'preselect') || elData(this._container, 'active');
+ if (preselect === "true" || !preselect) preselect = true;
+
+ if (preselect === true) {
+ this._tabs.forEach(function(tab) {
+ if (!selectTab && !elIsHidden(tab) && (!tab.previousElementSibling || elIsHidden(tab.previousElementSibling))) {
+ selectTab = tab;
+ }
+ });
+ }
+ else if (preselect !== "false") {
+ selectTab = this._tabs.get(preselect);
+ }
+ }
+
+ if (selectTab) {
+ this._containers.forEach(function(container) {
+ container.classList.add('hidden');
+ });
+
+ this.select(null, selectTab, true);
+ }
+
+ var store = elData(this._container, 'store');
+ if (store) {
+ var input = elCreate('input');
+ input.type = 'hidden';
+ input.name = store;
+ input.value = elData(this.getActiveTab(), 'name');
+
+ this._container.appendChild(input);
+
+ this._store = input;
+ }
+ }
+
+ return returnValue;
+ },
+
+ /**
+ * Selects a tab.
+ *
+ * @param {?(string|int)} name tab name or sequence no
+ * @param {Element=} tab tab element
+ * @param {boolean=} disableEvent suppress event handling
+ */
+ select: function(name, tab, disableEvent) {
+ tab = tab || this._tabs.get(name);
+
+ if (!tab) {
+ // check if name is an integer
+ if (~~name == name) {
+ name = ~~name;
+
+ var i = 0;
+ this._tabs.forEach(function(item) {
+ if (i === name) {
+ tab = item;
+ }
+
+ i++;
+ });
+ }
+
+ if (!tab) {
+ throw new Error("Expected a valid tab name, '" + name + "' given (tab menu id: '" + this._container.id + "').");
+ }
+ }
+
+ name = name || elData(tab, 'name');
+
+ // unmark active tab
+ var oldTab = this.getActiveTab();
+ var oldContent = null;
+ if (oldTab) {
+ var oldTabName = elData(oldTab, 'name');
+ if (oldTabName === name) {
+ // same tab
+ return;
+ }
+
+ if (!disableEvent) {
+ EventHandler.fire('com.woltlab.wcf.simpleTabMenu_' + this._container.id, 'beforeSelect', {
+ tab: oldTab,
+ tabName: oldTabName
+ });
+ }
+
+ oldTab.classList.remove('active');
+ oldContent = this._containers.get(elData(oldTab, 'name'));
+ oldContent.classList.remove('active');
+ oldContent.classList.add('hidden');
+
+ if (this._isLegacy) {
+ oldTab.classList.remove('ui-state-active');
+ oldContent.classList.remove('ui-state-active');
+ }
+ }
+
+ tab.classList.add('active');
+ var newContent = this._containers.get(name);
+ newContent.classList.add('active');
+ newContent.classList.remove('hidden');
+
+ if (this._isLegacy) {
+ tab.classList.add('ui-state-active');
+ newContent.classList.add('ui-state-active');
+ }
+
+ if (this._store) {
+ this._store.value = name;
+ }
+
+ if (!disableEvent) {
+ EventHandler.fire('com.woltlab.wcf.simpleTabMenu_' + this._container.id, 'select', {
+ active: tab,
+ activeName: name,
+ previous: oldTab,
+ previousName: oldTab ? elData(oldTab, 'name') : null
+ });
+
+ var jQuery = (this._isLegacy && typeof window.jQuery === 'function') ? window.jQuery : null;
+ if (jQuery) {
+ // simulate jQuery UI Tabs event
+ jQuery(this._container).trigger('wcftabsbeforeactivate', {
+ newTab: jQuery(tab),
+ oldTab: jQuery(oldTab),
+ newPanel: jQuery(newContent),
+ oldPanel: jQuery(oldContent)
+ });
+ }
+
+ var location = window.location.href.replace(/#+[^#]*$/, '');
+ if (TabMenuSimple.getIdentifierFromHash() === name) {
+ location += window.location.hash;
+ }
+ else {
+ location += '#' + name;
+ }
+
+ // update history
+ //noinspection JSCheckFunctionSignatures
+ window.history.replaceState(
+ undefined,
+ undefined,
+ location
+ );
+ }
+
+ require(['WoltLabSuite/Core/Ui/TabMenu'], function (UiTabMenu) {
+ //noinspection JSUnresolvedFunction
+ UiTabMenu.scrollToTab(tab);
+ });
+ },
+
+ /**
+ * Selects the first visible tab of the tab menu and return `true`. If there is no
+ * visible tab, `false` is returned.
+ *
+ * The visibility of a tab is determined by calling `elIsHidden` with the tab menu
+ * item as the parameter.
+ *
+ * @return {boolean}
+ */
+ selectFirstVisible: function() {
+ var selectTab;
+ this._tabs.forEach(function(tab) {
+ if (!selectTab && !elIsHidden(tab)) {
+ selectTab = tab;
+ }
+ }.bind(this));
+
+ if (selectTab) {
+ this.select(undefined, selectTab, false);
+ }
+
+ return !!selectTab;
+ },
+
+ /**
+ * Rebuilds all tabs, must be invoked after adding or removing of tabs.
+ *
+ * Warning: Do not remove tabs if you plan to add these later again or at least clone the nodes
+ * to prevent issues with already bound event listeners. Consider hiding them via CSS.
+ */
+ rebuild: function() {
+ var oldTabs = new Dictionary();
+ oldTabs.merge(this._tabs);
+
+ this.validate();
+ this.init(oldTabs);
+ },
+
+ /**
+ * Returns true if this tab menu has a tab with provided name.
+ *
+ * @param {string} name tab name
+ * @return {boolean} true if tab name matches
+ */
+ hasTab: function (name) {
+ return this._tabs.has(name);
+ },
+
+ /**
+ * Handles clicks on a tab.
+ *
+ * @param {object} event event object
+ */
+ _onClick: function(event) {
+ event.preventDefault();
+
+ this.select(null, event.currentTarget.parentNode);
+ },
+
+ /**
+ * Returns the tab name.
+ *
+ * @param {Element} tab tab element
+ * @return {string} tab name
+ */
+ _getTabName: function(tab) {
+ var name = elData(tab, 'name');
+
+ // handle legacy tab menus
+ if (!name) {
+ if (tab.childElementCount === 1 && tab.children[0].nodeName === 'A') {
+ if (tab.children[0].href.match(/#([^#]+)$/)) {
+ name = RegExp.$1;
+
+ if (elById(name) === null) {
+ name = null;
+ }
+ else {
+ this._isLegacy = true;
+ elData(tab, 'name', name);
+ }
+ }
+ }
+ }
+
+ return name;
+ },
+
+ /**
+ * Returns the currently active tab.
+ *
+ * @return {Element} active tab
+ */
+ getActiveTab: function() {
+ return elBySel('#' + this._container.id + ' > nav > ul > li.active');
+ },
+
+ /**
+ * Returns the list of registered content containers.
+ *
+ * @returns {Dictionary} content containers
+ */
+ getContainers: function() {
+ return this._containers;
+ },
+
+ /**
+ * Returns the list of registered tabs.
+ *
+ * @returns {Dictionary} tab items
+ */
+ getTabs: function() {
+ return this._tabs;
+ }
+ };
+
+ TabMenuSimple.getIdentifierFromHash = function () {
+ if (window.location.hash.match(/^#+([^\/]+)+(?:\/.+)?/)) {
+ return RegExp.$1;
+ }
+
+ return '';
+ };
+
+ return TabMenuSimple;
+});
+
+/**
+ * Common interface for tab menu access.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/TabMenu
+ */
+define('WoltLabSuite/Core/Ui/TabMenu',['Dictionary', 'EventHandler', 'Dom/ChangeListener', 'Dom/Util', 'Ui/CloseOverlay', 'Ui/Screen', './TabMenu/Simple'], function(Dictionary, EventHandler, DomChangeListener, DomUtil, UiCloseOverlay, UiScreen, SimpleTabMenu) {
+ "use strict";
+
+ var _activeList = null;
+ var _enableTabScroll = false;
+ var _tabMenus = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/TabMenu
+ */
+ return {
+ /**
+ * Sets up tab menus and binds listeners.
+ */
+ setup: function() {
+ this._init();
+ this._selectErroneousTabs();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/TabMenu', this._init.bind(this));
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/TabMenu', function() {
+ if (_activeList) {
+ _activeList.classList.remove('active');
+
+ _activeList = null;
+ }
+ });
+
+ //noinspection JSUnresolvedVariable
+ UiScreen.on('screen-sm-down', {
+ enable: this._scrollEnable.bind(this, false),
+ disable: this._scrollDisable.bind(this),
+ setup: this._scrollEnable.bind(this, true)
+ });
+
+ window.addEventListener('hashchange', function () {
+ var hash = SimpleTabMenu.getIdentifierFromHash();
+ var element = (hash) ? elById(hash) : null;
+ if (element !== null && element.classList.contains('tabMenuContent')) {
+ _tabMenus.forEach(function (tabMenu) {
+ if (tabMenu.hasTab(hash)) {
+ tabMenu.select(hash);
+ }
+ });
+ }
+ });
+
+ var hash = SimpleTabMenu.getIdentifierFromHash();
+ if (hash) {
+ window.setTimeout(function () {
+ // check if page was initially scrolled using a tab id
+ var tabMenuContent = elById(hash);
+ if (tabMenuContent && tabMenuContent.classList.contains('tabMenuContent')) {
+ var scrollY = (window.scrollY || window.pageYOffset);
+ if (scrollY > 0) {
+ var parent = tabMenuContent.parentNode;
+ var offsetTop = parent.offsetTop - 50;
+ if (offsetTop < 0) offsetTop = 0;
+
+ if (scrollY > offsetTop) {
+ var y = DomUtil.offset(parent).top;
+
+ if (y <= 50) {
+ y = 0;
+ }
+ else {
+ y -= 50;
+ }
+
+ window.scrollTo(0, y);
+ }
+ }
+ }
+ }, 100);
+ }
+ },
+
+ /**
+ * Initializes available tab menus.
+ */
+ _init: function() {
+ var container, containerId, list, returnValue, tabMenu, tabMenus = elBySelAll('.tabMenuContainer:not(.staticTabMenuContainer)');
+ for (var i = 0, length = tabMenus.length; i < length; i++) {
+ container = tabMenus[i];
+ containerId = DomUtil.identify(container);
+
+ if (_tabMenus.has(containerId)) {
+ continue;
+ }
+
+ tabMenu = new SimpleTabMenu(container);
+ if (tabMenu.validate()) {
+ returnValue = tabMenu.init();
+
+ _tabMenus.set(containerId, tabMenu);
+
+ if (returnValue instanceof Element) {
+ tabMenu = this.getTabMenu(returnValue.parentNode.id);
+ tabMenu.select(returnValue.id, null, true);
+ }
+
+ list = elBySel('#' + containerId + ' > nav > ul');
+ (function(list) {
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (event.target === list) {
+ list.classList.add('active');
+
+ _activeList = list;
+ }
+ else {
+ list.classList.remove('active');
+
+ _activeList = null;
+ }
+ });
+ })(list);
+
+ // bind scroll listener
+ elBySelAll('.tabMenu, .menu', container, (function(menu) {
+ var callback = this._rebuildMenuOverflow.bind(this, menu);
+
+ var timeout = null;
+ elBySel('ul', menu).addEventListener('scroll', function () {
+ if (timeout !== null) {
+ window.clearTimeout(timeout);
+ }
+
+ // slight delay to avoid calling this function too often
+ timeout = window.setTimeout(callback, 10);
+ });
+ }).bind(this));
+ }
+ }
+ },
+
+ /**
+ * Selects the first tab containing an element with class `formError`.
+ */
+ _selectErroneousTabs: function() {
+ _tabMenus.forEach(function(tabMenu) {
+ var foundError = false;
+ tabMenu.getContainers().forEach(function(container) {
+ if (!foundError && elByClass('formError', container).length) {
+ foundError = true;
+
+ tabMenu.select(container.id);
+ }
+ });
+ });
+ },
+
+ /**
+ * Returns a SimpleTabMenu instance for given container id.
+ *
+ * @param {string} containerId tab menu container id
+ * @return {(SimpleTabMenu|undefined)} tab menu object
+ */
+ getTabMenu: function(containerId) {
+ return _tabMenus.get(containerId);
+ },
+
+ _scrollEnable: function (isSetup) {
+ _enableTabScroll = true;
+
+ _tabMenus.forEach((function (tabMenu) {
+ var activeTab = tabMenu.getActiveTab();
+ if (isSetup) {
+ this._rebuildMenuOverflow(activeTab.closest('.menu, .tabMenu'));
+ }
+ else {
+ this.scrollToTab(activeTab);
+ }
+ }).bind(this));
+ },
+
+ _scrollDisable: function () {
+ _enableTabScroll = false;
+ },
+
+ scrollToTab: function (tab) {
+ if (!_enableTabScroll) {
+ return;
+ }
+
+ var list = tab.closest('ul');
+ var width = list.clientWidth;
+ var scrollLeft = list.scrollLeft;
+ var scrollWidth = list.scrollWidth;
+ if (width === scrollWidth) {
+ // no overflow, ignore
+ return;
+ }
+
+ // check if tab is currently visible
+ var left = tab.offsetLeft;
+ var shouldScroll = false;
+ if (left < scrollLeft) {
+ shouldScroll = true;
+ }
+
+ var paddingRight = false;
+ if (!shouldScroll) {
+ var visibleWidth = width - (left - scrollLeft);
+ var virtualWidth = tab.clientWidth;
+ if (tab.nextElementSibling !== null) {
+ paddingRight = true;
+ virtualWidth += 20;
+ }
+
+ if (visibleWidth < virtualWidth) {
+ shouldScroll = true;
+ }
+ }
+
+ if (shouldScroll) {
+ this._scrollMenu(list, left, scrollLeft, scrollWidth, width, paddingRight);
+ }
+ },
+
+ _scrollMenu: function (list, left, scrollLeft, scrollWidth, width, paddingRight) {
+ // allow some padding to indicate overflow
+ if (paddingRight) {
+ left -= 15;
+ }
+ else if (left > 0) {
+ left -= 15;
+ }
+
+ if (left < 0) {
+ left = 0;
+ }
+ else {
+ // ensure that our left value is always within the boundaries
+ left = Math.min(left, scrollWidth - width);
+ }
+
+ if (scrollLeft === left) {
+ return;
+ }
+
+ list.classList.add('enableAnimation');
+
+ // new value is larger, we're scrolling towards the end
+ if (scrollLeft < left) {
+ list.firstElementChild.style.setProperty('margin-left', (scrollLeft - left) + 'px', '');
+ }
+ else {
+ // new value is smaller, we're scrolling towards the start
+ list.style.setProperty('padding-left', (scrollLeft - left) + 'px', '');
+ }
+
+ setTimeout(function () {
+ list.classList.remove('enableAnimation');
+
+ list.firstElementChild.style.removeProperty('margin-left');
+ list.style.removeProperty('padding-left');
+
+ list.scrollLeft = left;
+ }, 300);
+ },
+
+ _rebuildMenuOverflow: function (menu) {
+ if (!_enableTabScroll) {
+ return;
+ }
+
+ var width = menu.clientWidth;
+ var list = elBySel('ul', menu);
+ var scrollLeft = list.scrollLeft;
+ var scrollWidth = list.scrollWidth;
+
+ var overflowLeft = (scrollLeft > 0);
+ var overlayLeft = elBySel('.tabMenuOverlayLeft', menu);
+ if (overflowLeft) {
+ if (overlayLeft === null) {
+ overlayLeft = elCreate('span');
+ overlayLeft.className = 'tabMenuOverlayLeft icon icon24 fa-angle-left';
+ overlayLeft.addEventListener(WCF_CLICK_EVENT, (function () {
+ var listWidth = list.clientWidth;
+
+ this._scrollMenu(
+ list,
+ list.scrollLeft - ~~(listWidth / 2),
+ list.scrollLeft,
+ list.scrollWidth,
+ listWidth,
+ 0
+ );
+ }).bind(this));
+
+ menu.insertBefore(overlayLeft, menu.firstChild);
+ }
+
+ overlayLeft.classList.add('active');
+ }
+ else if (overlayLeft !== null) {
+ overlayLeft.classList.remove('active');
+ }
+
+ var overflowRight = (width + scrollLeft < scrollWidth);
+ var overlayRight = elBySel('.tabMenuOverlayRight', menu);
+ if (overflowRight) {
+ if (overlayRight === null) {
+ overlayRight = elCreate('span');
+ overlayRight.className = 'tabMenuOverlayRight icon icon24 fa-angle-right';
+ overlayRight.addEventListener(WCF_CLICK_EVENT, (function () {
+ var listWidth = list.clientWidth;
+
+ this._scrollMenu(
+ list,
+ list.scrollLeft + ~~(listWidth / 2),
+ list.scrollLeft,
+ list.scrollWidth,
+ listWidth,
+ 0
+ );
+ }).bind(this));
+
+ menu.appendChild(overlayRight);
+ }
+
+ overlayRight.classList.add('active');
+ }
+ else if (overlayRight !== null) {
+ overlayRight.classList.remove('active');
+ }
+ }
+ };
+});
+
+/**
+ * Dynamically transforms menu-like structures to handle items exceeding the available width
+ * by moving them into a separate dropdown.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/FlexibleMenu
+ */
+define('WoltLabSuite/Core/Ui/FlexibleMenu',['Core', 'Dictionary', 'Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'Ui/SimpleDropdown'], function(Core, Dictionary, DomChangeListener, DomTraverse, DomUtil, SimpleDropdown) {
+ "use strict";
+
+ var _containers = new Dictionary();
+ var _dropdowns = new Dictionary();
+ var _dropdownMenus = new Dictionary();
+ var _itemLists = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/FlexibleMenu
+ */
+ var UiFlexibleMenu = {
+ /**
+ * Register default menus and set up event listeners.
+ */
+ setup: function() {
+ if (elById('mainMenu') !== null) this.register('mainMenu');
+ var navigationHeader = elBySel('.navigationHeader');
+ if (navigationHeader !== null) this.register(DomUtil.identify(navigationHeader));
+
+ window.addEventListener('resize', this.rebuildAll.bind(this));
+ DomChangeListener.add('WoltLabSuite/Core/Ui/FlexibleMenu', this.registerTabMenus.bind(this));
+ },
+
+ /**
+ * Registers a menu by element id.
+ *
+ * @param {string} containerId element id
+ */
+ register: function(containerId) {
+ var container = elById(containerId);
+ if (container === null) {
+ throw "Expected a valid element id, '" + containerId + "' does not exist.";
+ }
+
+ if (_containers.has(containerId)) {
+ return;
+ }
+
+ var list = DomTraverse.childByTag(container, 'UL');
+ if (list === null) {
+ throw "Expected an <ul> element as child of container '" + containerId + "'.";
+ }
+
+ _containers.set(containerId, container);
+ _itemLists.set(containerId, list);
+
+ this.rebuild(containerId);
+ },
+
+ /**
+ * Registers tab menus.
+ */
+ registerTabMenus: function() {
+ var tabMenus = elBySelAll('.tabMenuContainer:not(.jsFlexibleMenuEnabled), .messageTabMenu:not(.jsFlexibleMenuEnabled)');
+ for (var i = 0, length = tabMenus.length; i < length; i++) {
+ var tabMenu = tabMenus[i];
+ var nav = DomTraverse.childByTag(tabMenu, 'NAV');
+ if (nav !== null) {
+ tabMenu.classList.add('jsFlexibleMenuEnabled');
+ this.register(DomUtil.identify(nav));
+ }
+ }
+ },
+
+ /**
+ * Rebuilds all menus, e.g. on window resize.
+ */
+ rebuildAll: function() {
+ _containers.forEach((function(container, containerId) {
+ this.rebuild(containerId);
+ }).bind(this));
+ },
+
+ /**
+ * Rebuild the menu identified by given element id.
+ *
+ * @param {string} containerId element id
+ */
+ rebuild: function(containerId) {
+ var container = _containers.get(containerId);
+ if (container === undefined) {
+ throw "Expected a valid element id, '" + containerId + "' is unknown.";
+ }
+
+ var styles = window.getComputedStyle(container);
+
+ var availableWidth = container.parentNode.clientWidth;
+ availableWidth -= DomUtil.styleAsInt(styles, 'margin-left');
+ availableWidth -= DomUtil.styleAsInt(styles, 'margin-right');
+
+ var list = _itemLists.get(containerId);
+ var items = DomTraverse.childrenByTag(list, 'LI');
+ var dropdown = _dropdowns.get(containerId);
+ var dropdownWidth = 0;
+ if (dropdown !== undefined) {
+ // show all items for calculation
+ for (var i = 0, length = items.length; i < length; i++) {
+ var item = items[i];
+ if (item.classList.contains('dropdown')) {
+ continue;
+ }
+
+ elShow(item);
+ }
+
+ if (dropdown.parentNode !== null) {
+ dropdownWidth = DomUtil.outerWidth(dropdown);
+ }
+ }
+
+ var currentWidth = list.scrollWidth - dropdownWidth;
+ var hiddenItems = [];
+ if (currentWidth > availableWidth) {
+ // hide items starting with the last one
+ for (var i = items.length - 1; i >= 0; i--) {
+ var item = items[i];
+
+ // ignore dropdown and active item
+ if (item.classList.contains('dropdown') || item.classList.contains('active') || item.classList.contains('ui-state-active')) {
+ continue;
+ }
+
+ hiddenItems.push(item);
+ elHide(item);
+
+ if (list.scrollWidth < availableWidth) {
+ break;
+ }
+ }
+ }
+
+ if (hiddenItems.length) {
+ var dropdownMenu;
+ if (dropdown === undefined) {
+ dropdown = elCreate('li');
+ dropdown.className = 'dropdown jsFlexibleMenuDropdown';
+ var icon = elCreate('a');
+ icon.className = 'icon icon16 fa-list';
+ dropdown.appendChild(icon);
+
+ dropdownMenu = elCreate('ul');
+ dropdownMenu.classList.add('dropdownMenu');
+ dropdown.appendChild(dropdownMenu);
+
+ _dropdowns.set(containerId, dropdown);
+ _dropdownMenus.set(containerId, dropdownMenu);
+
+ SimpleDropdown.init(icon);
+ }
+ else {
+ dropdownMenu = _dropdownMenus.get(containerId);
+ }
+
+ if (dropdown.parentNode === null) {
+ list.appendChild(dropdown);
+ }
+
+ // build dropdown menu
+ var fragment = document.createDocumentFragment();
+
+ var self = this;
+ hiddenItems.forEach(function(hiddenItem) {
+ var item = elCreate('li');
+ item.innerHTML = hiddenItem.innerHTML;
+
+ item.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+
+ Core.triggerEvent(elBySel('a', hiddenItem), WCF_CLICK_EVENT);
+
+ // force a rebuild to guarantee the active item being visible
+ setTimeout(function() {
+ self.rebuild(containerId);
+ }, 59);
+ }).bind(this));
+
+ fragment.appendChild(item);
+ });
+
+ dropdownMenu.innerHTML = '';
+ dropdownMenu.appendChild(fragment);
+ }
+ else if (dropdown !== undefined && dropdown.parentNode !== null) {
+ elRemove(dropdown);
+ }
+ }
+ };
+
+ return UiFlexibleMenu;
+});
+
+/**
+ * Provides enhanced tooltips.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Tooltip
+ */
+define('WoltLabSuite/Core/Ui/Tooltip',['Environment', 'Dom/ChangeListener', 'Ui/Alignment'], function(Environment, DomChangeListener, UiAlignment) {
+ "use strict";
+
+ var _callbackMouseEnter = null;
+ var _callbackMouseLeave = null;
+ var _elements = null;
+ var _pointer = null;
+ var _text = null;
+ var _tooltip = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Tooltip
+ */
+ return {
+ /**
+ * Initializes the tooltip element and binds event listener.
+ */
+ setup: function() {
+ if (Environment.platform() !== 'desktop') return;
+
+ _tooltip = elCreate('div');
+ elAttr(_tooltip, 'id', 'balloonTooltip');
+ _tooltip.classList.add('balloonTooltip');
+ _tooltip.addEventListener('transitionend', function () {
+ if (!_tooltip.classList.contains('active')) {
+ // reset back to the upper left corner, prevent it from staying outside
+ // the viewport if the body overflow was previously hidden
+ ['bottom', 'left', 'right', 'top'].forEach(function(property) {
+ _tooltip.style.removeProperty(property);
+ });
+ }
+ });
+
+ _text = elCreate('span');
+ elAttr(_text, 'id', 'balloonTooltipText');
+ _tooltip.appendChild(_text);
+
+ _pointer = elCreate('span');
+ _pointer.classList.add('elementPointer');
+ _pointer.appendChild(elCreate('span'));
+ _tooltip.appendChild(_pointer);
+
+ document.body.appendChild(_tooltip);
+
+ _elements = elByClass('jsTooltip');
+
+ _callbackMouseEnter = this._mouseEnter.bind(this);
+ _callbackMouseLeave = this._mouseLeave.bind(this);
+
+ this.init();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Tooltip', this.init.bind(this));
+ window.addEventListener('scroll', this._mouseLeave.bind(this));
+ },
+
+ /**
+ * Initializes tooltip elements.
+ */
+ init: function() {
+ if (_elements.length === 0) {
+ return;
+ }
+
+ elBySelAll('.jsTooltip', undefined, function (element) {
+ element.classList.remove('jsTooltip');
+
+ var title = elAttr(element, 'title').trim();
+ if (title.length) {
+ elData(element, 'tooltip', title);
+ element.removeAttribute('title');
+ elAttr(element, 'aria-label', title);
+
+ element.addEventListener('mouseenter', _callbackMouseEnter);
+ element.addEventListener('mouseleave', _callbackMouseLeave);
+ element.addEventListener(WCF_CLICK_EVENT, _callbackMouseLeave);
+ }
+ });
+ },
+
+ /**
+ * Displays the tooltip on mouse enter.
+ *
+ * @param {Event} event event object
+ */
+ _mouseEnter: function(event) {
+ var element = event.currentTarget;
+ var title = elAttr(element, 'title');
+ title = (typeof title === 'string') ? title.trim() : '';
+
+ if (title !== '') {
+ elData(element, 'tooltip', title);
+ element.removeAttribute('title');
+ }
+
+ title = elData(element, 'tooltip');
+
+ // reset tooltip position
+ _tooltip.style.removeProperty('top');
+ _tooltip.style.removeProperty('left');
+
+ // ignore empty tooltip
+ if (!title.length) {
+ _tooltip.classList.remove('active');
+ return;
+ }
+ else {
+ _tooltip.classList.add('active');
+ }
+
+ _text.textContent = title;
+
+ UiAlignment.set(_tooltip, element, {
+ horizontal: 'center',
+ verticalOffset: 4,
+ pointer: true,
+ pointerClassNames: ['inverse'],
+ vertical: 'top'
+ });
+ },
+
+ /**
+ * Hides the tooltip once the mouse leaves the element.
+ */
+ _mouseLeave: function() {
+ _tooltip.classList.remove('active');
+ }
+ };
+});
+
+/**
+ * Date picker with time support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Date/Picker
+ */
+define('WoltLabSuite/Core/Date/Picker',['DateUtil', 'Dom/Traverse', 'Dom/Util', 'EventHandler', 'Language', 'ObjectMap', 'Dom/ChangeListener', 'Ui/Alignment', 'WoltLabSuite/Core/Ui/CloseOverlay'], function(DateUtil, DomTraverse, DomUtil, EventHandler, Language, ObjectMap, DomChangeListener, UiAlignment, UiCloseOverlay) {
+ "use strict";
+
+ var _didInit = false;
+ var _firstDayOfWeek = 0;
+ var _wasInsidePicker = false;
+
+ var _data = new ObjectMap();
+ var _input = null;
+ var _maxDate = 0;
+ var _minDate = 0;
+
+ var _dateCells = [];
+ var _dateGrid = null;
+ var _dateHour = null;
+ var _dateMinute = null;
+ var _dateMonth = null;
+ var _dateMonthNext = null;
+ var _dateMonthPrevious = null;
+ var _dateTime = null;
+ var _dateYear = null;
+ var _datePicker = null;
+
+ var _callbackOpen = null;
+ var _callbackFocus = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Date/Picker
+ */
+ var DatePicker = {
+ /**
+ * Initializes all date and datetime input fields.
+ */
+ init: function() {
+ this._setup();
+
+ var elements = elBySelAll('input[type="date"]:not(.inputDatePicker), input[type="datetime"]:not(.inputDatePicker)');
+ var now = new Date();
+ for (var i = 0, length = elements.length; i < length; i++) {
+ var element = elements[i];
+ element.classList.add('inputDatePicker');
+ element.readOnly = true;
+
+ var isDateTime = (elAttr(element, 'type') === 'datetime');
+ var isTimeOnly = (isDateTime && elDataBool(element, 'time-only'));
+ var disableClear = elDataBool(element, 'disable-clear');
+ var ignoreTimezone = isDateTime && elDataBool(element, 'ignore-timezone');
+ var isBirthday = element.classList.contains('birthday');
+
+ elData(element, 'is-date-time', isDateTime);
+ elData(element, 'is-time-only', isTimeOnly);
+
+ // convert value
+ var date = null, value = elAttr(element, 'value');
+
+ // ignore the timezone, if the value is only a date (YYYY-MM-DD)
+ var isDateOnly = /^\d+-\d+-\d+$/.test(value);
+
+ if (elAttr(element, 'value')) {
+ if (isTimeOnly) {
+ date = new Date();
+ var tmp = value.split(':');
+ date.setHours(tmp[0], tmp[1]);
+ }
+ else {
+ if (ignoreTimezone || isBirthday || isDateOnly) {
+ var timezoneOffset = new Date(value).getTimezoneOffset();
+ var timezone = (timezoneOffset > 0) ? '-' : '+'; // -120 equals GMT+0200
+ timezoneOffset = Math.abs(timezoneOffset);
+ var hours = (Math.floor(timezoneOffset / 60)).toString();
+ var minutes = (timezoneOffset % 60).toString();
+ timezone += (hours.length === 2) ? hours : '0' + hours;
+ timezone += ':';
+ timezone += (minutes.length === 2) ? minutes : '0' + minutes;
+
+ if (isBirthday || isDateOnly) {
+ value += 'T00:00:00' + timezone;
+ }
+ else {
+ value = value.replace(/[+-][0-9]{2}:[0-9]{2}$/, timezone);
+ }
+ }
+
+ date = new Date(value);
+ }
+
+ var time = date.getTime();
+
+ // check for invalid dates
+ if (isNaN(time)) {
+ value = '';
+ }
+ else {
+ elData(element, 'value', time);
+ var format = (isTimeOnly) ? 'formatTime' : ('formatDate' + (isDateTime ? 'Time' : ''));
+ value = DateUtil[format](date);
+ }
+ }
+
+ var isEmpty = (value.length === 0);
+
+ // handle birthday input
+ if (isBirthday) {
+ elData(element, 'min-date', '120');
+
+ // do not use 'now' here, all though it makes sense, it causes bad UX
+ elData(element, 'max-date', new Date().getFullYear() + '-12-31');
+ }
+ else {
+ if (element.min) elData(element, 'min-date', element.min);
+ if (element.max) elData(element, 'max-date', element.max);
+ }
+
+ this._initDateRange(element, now, true);
+ this._initDateRange(element, now, false);
+
+ if (elData(element, 'min-date') === elData(element, 'max-date')) {
+ throw new Error("Minimum and maximum date cannot be the same (element id '" + element.id + "').");
+ }
+
+ // change type to prevent browser's datepicker to trigger
+ element.type = 'text';
+ element.value = value;
+ elData(element, 'empty', isEmpty);
+
+ if (elData(element, 'placeholder')) {
+ elAttr(element, 'placeholder', elData(element, 'placeholder'));
+ }
+
+ // add a hidden element to hold the actual date
+ var shadowElement = elCreate('input');
+ shadowElement.id = element.id + 'DatePicker';
+ shadowElement.name = element.name;
+ shadowElement.type = 'hidden';
+
+ if (date !== null) {
+ if (isTimeOnly) {
+ shadowElement.value = DateUtil.format(date, 'H:i');
+ }
+ else if (ignoreTimezone) {
+ shadowElement.value = DateUtil.format(date, 'Y-m-dTH:i:s');
+ }
+ else {
+ shadowElement.value = DateUtil.format(date, (isDateTime) ? 'c' : 'Y-m-d');
+ }
+ }
+
+ element.parentNode.insertBefore(shadowElement, element);
+ element.removeAttribute('name');
+
+ element.addEventListener(WCF_CLICK_EVENT, _callbackOpen);
+
+ if (!element.disabled) {
+ // create input addon
+ var container = elCreate('div');
+ container.className = 'inputAddon';
+
+ var button = elCreate('a');
+
+ button.className = 'inputSuffix button jsTooltip';
+ button.href = '#';
+ elAttr(button, 'role', 'button');
+ elAttr(button, 'tabindex', '0');
+ elAttr(button, 'title', Language.get('wcf.date.datePicker'));
+ elAttr(button, 'aria-label', Language.get('wcf.date.datePicker'));
+ elAttr(button, 'aria-haspopup', true);
+ elAttr(button, 'aria-expanded', false);
+ button.addEventListener(WCF_CLICK_EVENT, _callbackOpen);
+ container.appendChild(button);
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-calendar';
+ button.appendChild(icon);
+
+ element.parentNode.insertBefore(container, element);
+ container.insertBefore(element, button);
+
+ if (!disableClear) {
+ button = elCreate('a');
+ button.className = 'inputSuffix button';
+ button.addEventListener(WCF_CLICK_EVENT, this.clear.bind(this, element));
+ if (isEmpty) button.style.setProperty('visibility', 'hidden', '');
+
+ container.appendChild(button);
+
+ icon = elCreate('span');
+ icon.className = 'icon icon16 fa-times';
+ button.appendChild(icon);
+ }
+ }
+
+ // check if the date input has one of the following classes set otherwise default to 'short'
+ var hasClass = false, knownClasses = ['tiny', 'short', 'medium', 'long'];
+ for (var j = 0; j < 4; j++) {
+ if (element.classList.contains(knownClasses[j])) {
+ hasClass = true;
+ }
+ }
+
+ if (!hasClass) {
+ element.classList.add('short');
+ }
+
+ _data.set(element, {
+ clearButton: button,
+ shadow: shadowElement,
+
+ disableClear: disableClear,
+ isDateTime: isDateTime,
+ isEmpty: isEmpty,
+ isTimeOnly: isTimeOnly,
+ ignoreTimezone: ignoreTimezone,
+
+ onClose: null
+ });
+ }
+ },
+
+ /**
+ * Initializes the minimum/maximum date range.
+ *
+ * @param {Element} element input element
+ * @param {Date} now current date
+ * @param {boolean} isMinDate true for the minimum date
+ */
+ _initDateRange: function(element, now, isMinDate) {
+ var attribute = 'data-' + (isMinDate ? 'min' : 'max') + '-date';
+ var value = (element.hasAttribute(attribute)) ? elAttr(element, attribute).trim() : '';
+
+ if (value.match(/^(\d{4})-(\d{2})-(\d{2})$/)) {
+ // YYYY-mm-dd
+ value = new Date(value).getTime();
+ }
+ else if (value === 'now') {
+ value = now.getTime();
+ }
+ else if (value.match(/^\d{1,3}$/)) {
+ // relative time span in years
+ var date = new Date(now.getTime());
+ date.setFullYear(date.getFullYear() + ~~value * (isMinDate ? -1 : 1));
+
+ value = date.getTime();
+ }
+ else if (value.match(/^datePicker-(.+)$/)) {
+ // element id, e.g. `datePicker-someOtherElement`
+ value = RegExp.$1;
+
+ if (elById(value) === null) {
+ throw new Error("Reference date picker identified by '" + value + "' does not exists (element id: '" + element.id + "').");
+ }
+ }
+ else if (/^\d{4}\-\d{2}\-\d{2}T/.test(value)) {
+ value = new Date(value).getTime();
+ }
+ else {
+ value = new Date((isMinDate ? 1902 : 2038), 0, 1).getTime();
+ }
+
+ elAttr(element, attribute, value);
+ },
+
+ /**
+ * Sets up callbacks and event listeners.
+ */
+ _setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _firstDayOfWeek = ~~Language.get('wcf.date.firstDayOfTheWeek');
+ _callbackOpen = this._open.bind(this);
+
+ DomChangeListener.add('WoltLabSuite/Core/Date/Picker', this.init.bind(this));
+ UiCloseOverlay.add('WoltLabSuite/Core/Date/Picker', this._close.bind(this));
+ },
+
+ /**
+ * Opens the date picker.
+ *
+ * @param {object} event event object
+ */
+ _open: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this._createPicker();
+
+ if (_callbackFocus === null) {
+ _callbackFocus = this._maintainFocus.bind(this);
+ document.body.addEventListener('focus', _callbackFocus, { capture: true });
+ }
+
+ var input = (event.currentTarget.nodeName === 'INPUT') ? event.currentTarget : event.currentTarget.previousElementSibling;
+ if (input === _input) {
+ this._close();
+ return;
+ }
+
+ var dialogContent = DomTraverse.parentByClass(input, 'dialogContent');
+ if (dialogContent !== null) {
+ if (!elDataBool(dialogContent, 'has-datepicker-scroll-listener')) {
+ dialogContent.addEventListener('scroll', this._onDialogScroll.bind(this));
+ elData(dialogContent, 'has-datepicker-scroll-listener', 1);
+ }
+ }
+
+ _input = input;
+ var data = _data.get(_input), date, value = elData(_input, 'value');
+ if (value) {
+ date = new Date(+value);
+
+ if (date.toString() === 'Invalid Date') {
+ date = new Date();
+ }
+ }
+ else {
+ date = new Date();
+ }
+
+ // set min/max date
+ _minDate = elData(_input, 'min-date');
+ if (_minDate.match(/^datePicker-(.+)$/)) _minDate = elData(elById(RegExp.$1), 'value');
+ _minDate = new Date(+_minDate);
+
+ _maxDate = elData(_input, 'max-date');
+ if (_maxDate.match(/^datePicker-(.+)$/)) _maxDate = elData(elById(RegExp.$1), 'value');
+ _maxDate = new Date(+_maxDate);
+
+ if (data.isDateTime) {
+ _dateHour.value = date.getHours();
+ _dateMinute.value = date.getMinutes();
+
+ _datePicker.classList.add('datePickerTime');
+ }
+ else {
+ _datePicker.classList.remove('datePickerTime');
+ }
+
+ _datePicker.classList[(data.isTimeOnly) ? 'add' : 'remove']('datePickerTimeOnly');
+
+ this._renderPicker(date.getDate(), date.getMonth(), date.getFullYear());
+
+ UiAlignment.set(_datePicker, _input);
+
+ elAttr(_input.nextElementSibling, 'aria-expanded', true);
+
+ _wasInsidePicker = false;
+ },
+
+ /**
+ * Closes the date picker.
+ */
+ _close: function() {
+ if (_datePicker !== null && _datePicker.classList.contains('active')) {
+ _datePicker.classList.remove('active');
+
+ var data = _data.get(_input);
+ if (typeof data.onClose === 'function') {
+ data.onClose();
+ }
+
+ EventHandler.fire('WoltLabSuite/Core/Date/Picker', 'close', {element: _input});
+
+ elAttr(_input.nextElementSibling, 'aria-expanded', false);
+ _input = null;
+ _minDate = 0;
+ _maxDate = 0;
+ }
+ },
+
+ /**
+ * Updates the position of the date picker in a dialog if the dialog content
+ * is scrolled.
+ *
+ * @param {Event} event scroll event
+ */
+ _onDialogScroll: function(event) {
+ if (_input === null) {
+ return;
+ }
+
+ var dialogContent = event.currentTarget;
+
+ var offset = DomUtil.offset(_input);
+ var dialogOffset = DomUtil.offset(dialogContent);
+
+ // check if date picker input field is still (partially) visible
+ if (offset.top + _input.clientHeight <= dialogOffset.top) {
+ // top check
+ this._close();
+ }
+ else if (offset.top >= dialogOffset.top + dialogContent.offsetHeight) {
+ // bottom check
+ this._close();
+ }
+ else if (offset.left <= dialogOffset.left) {
+ // left check
+ this._close();
+ }
+ else if (offset.left >= dialogOffset.left + dialogContent.offsetWidth) {
+ // right check
+ this._close();
+ }
+ else {
+ UiAlignment.set(_datePicker, _input);
+ }
+ },
+
+ /**
+ * Renders the full picker on init.
+ *
+ * @param {int} day
+ * @param {int} month
+ * @param {int} year
+ */
+ _renderPicker: function(day, month, year) {
+ this._renderGrid(day, month, year);
+
+ // create options for month and year
+ var years = '';
+ for (var i = _minDate.getFullYear(), last = _maxDate.getFullYear(); i <= last; i++) {
+ years += '<option value="' + i + '">' + i + '</option>';
+ }
+ _dateYear.innerHTML = years;
+ _dateYear.value = year;
+
+ _dateMonth.value = month;
+
+ _datePicker.classList.add('active');
+ },
+
+ /**
+ * Updates the date grid.
+ *
+ * @param {int} day
+ * @param {int} month
+ * @param {int} year
+ */
+ _renderGrid: function(day, month, year) {
+ var cell, hasDay = (day !== undefined), hasMonth = (month !== undefined), i;
+
+ day = ~~day || ~~elData(_dateGrid, 'day');
+ month = ~~month;
+ year = ~~year;
+
+ // rebuild cells
+ if (hasMonth || year) {
+ var rebuildMonths = (year !== 0);
+
+ // rebuild grid
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild(_dateGrid);
+
+ if (!hasMonth) month = ~~elData(_dateGrid, 'month');
+ year = year || ~~elData(_dateGrid, 'year');
+
+ // check if current selection exceeds min/max date
+ var date = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-' + ('0' + day.toString()).slice(-2));
+ if (date < _minDate) {
+ year = _minDate.getFullYear();
+ month = _minDate.getMonth();
+ day = _minDate.getDate();
+
+ _dateMonth.value = month;
+ _dateYear.value = year;
+
+ rebuildMonths = true;
+ }
+ else if (date > _maxDate) {
+ year = _maxDate.getFullYear();
+ month = _maxDate.getMonth();
+ day = _maxDate.getDate();
+
+ _dateMonth.value = month;
+ _dateYear.value = year;
+
+ rebuildMonths = true;
+ }
+
+ date = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-01');
+
+ // shift until first displayed day equals first day of week
+ while (date.getDay() !== _firstDayOfWeek) {
+ date.setDate(date.getDate() - 1);
+ }
+
+ // show the last row
+ elShow(_dateCells[35].parentNode);
+
+ var selectable;
+ var comparableMinDate = new Date(_minDate.getFullYear(), _minDate.getMonth(), _minDate.getDate());
+ for (i = 0; i < 42; i++) {
+ if (i === 35 && date.getMonth() !== month) {
+ // skip the last row if it only contains the next month
+ elHide(_dateCells[35].parentNode);
+
+ break;
+ }
+
+ cell = _dateCells[i];
+
+ cell.textContent = date.getDate();
+ selectable = (date.getMonth() === month);
+ if (selectable) {
+ if (date < comparableMinDate) selectable = false;
+ else if (date > _maxDate) selectable = false;
+ }
+
+ cell.classList[selectable ? 'remove' : 'add']('otherMonth');
+ if (selectable) {
+ cell.href = '#';
+ elAttr(cell, 'role', 'button');
+ elAttr(cell, 'tabindex', '0');
+ elAttr(cell, 'title', DateUtil.formatDate(date));
+ elAttr(cell, 'aria-label', DateUtil.formatDate(date));
+ }
+
+ date.setDate(date.getDate() + 1);
+ }
+
+ elData(_dateGrid, 'month', month);
+ elData(_dateGrid, 'year', year);
+
+ _datePicker.insertBefore(fragment, _dateTime);
+
+ if (!hasDay) {
+ // check if date is valid
+ date = new Date(year, month, day);
+ if (date.getDate() !== day) {
+ while (date.getMonth() !== month) {
+ date.setDate(date.getDate() - 1);
+ }
+
+ day = date.getDate();
+ }
+ }
+
+ if (rebuildMonths) {
+ for (i = 0; i < 12; i++) {
+ var currentMonth = _dateMonth.children[i];
+
+ currentMonth.disabled = (year === _minDate.getFullYear() && currentMonth.value < _minDate.getMonth()) || (year === _maxDate.getFullYear() && currentMonth.value > _maxDate.getMonth());
+ }
+
+ var nextMonth = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-01');
+ nextMonth.setMonth(nextMonth.getMonth() + 1);
+
+ _dateMonthNext.classList[(nextMonth < _maxDate) ? 'add' : 'remove']('active');
+
+ var previousMonth = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-01');
+ previousMonth.setDate(previousMonth.getDate() - 1);
+
+ _dateMonthPrevious.classList[(previousMonth > _minDate) ? 'add' : 'remove']('active');
+ }
+ }
+
+ // update active day
+ if (day) {
+ for (i = 0; i < 35; i++) {
+ cell = _dateCells[i];
+
+ cell.classList[(!cell.classList.contains('otherMonth') && ~~cell.textContent === day) ? 'add' : 'remove']('active');
+ }
+
+ elData(_dateGrid, 'day', day);
+ }
+
+ this._formatValue();
+ },
+
+ /**
+ * Sets the visible and shadow value
+ */
+ _formatValue: function() {
+ var data = _data.get(_input), date;
+
+ if (elData(_input, 'empty') === 'true') {
+ return;
+ }
+
+ if (data.isDateTime) {
+ date = new Date(
+ elData(_dateGrid, 'year'),
+ elData(_dateGrid, 'month'),
+ elData(_dateGrid, 'day'),
+ _dateHour.value,
+ _dateMinute.value
+ );
+ }
+ else {
+ date = new Date(
+ elData(_dateGrid, 'year'),
+ elData(_dateGrid, 'month'),
+ elData(_dateGrid, 'day')
+ );
+ }
+
+ this.setDate(_input, date);
+ },
+
+ /**
+ * Creates the date picker DOM.
+ */
+ _createPicker: function() {
+ if (_datePicker !== null) {
+ return;
+ }
+
+ _datePicker = elCreate('div');
+ _datePicker.className = 'datePicker';
+ _datePicker.addEventListener(WCF_CLICK_EVENT, function(event) { event.stopPropagation(); });
+
+ var header = elCreate('header');
+ _datePicker.appendChild(header);
+
+ _dateMonthPrevious = elCreate('a');
+ _dateMonthPrevious.className = 'previous jsTooltip';
+ _dateMonthPrevious.href = '#';
+ elAttr(_dateMonthPrevious, 'role', 'button');
+ elAttr(_dateMonthPrevious, 'tabindex', '0');
+ elAttr(_dateMonthPrevious, 'title', Language.get('wcf.date.datePicker.previousMonth'));
+ elAttr(_dateMonthPrevious, 'aria-label', Language.get('wcf.date.datePicker.previousMonth'));
+ _dateMonthPrevious.innerHTML = '<span class="icon icon16 fa-arrow-left"></span>';
+ _dateMonthPrevious.addEventListener(WCF_CLICK_EVENT, this.previousMonth.bind(this));
+ header.appendChild(_dateMonthPrevious);
+
+ var monthYearContainer = elCreate('span');
+ header.appendChild(monthYearContainer);
+
+ _dateMonth = elCreate('select');
+ _dateMonth.className = 'month jsTooltip';
+ elAttr(_dateMonth, 'title', Language.get('wcf.date.datePicker.month'));
+ elAttr(_dateMonth, 'aria-label', Language.get('wcf.date.datePicker.month'));
+ _dateMonth.addEventListener('change', this._changeMonth.bind(this));
+ monthYearContainer.appendChild(_dateMonth);
+
+ var i, months = '', monthNames = Language.get('__monthsShort');
+ for (i = 0; i < 12; i++) {
+ months += '<option value="' + i + '">' + monthNames[i] + '</option>';
+ }
+ _dateMonth.innerHTML = months;
+
+ _dateYear = elCreate('select');
+ _dateYear.className = 'year jsTooltip';
+ elAttr(_dateYear, 'title', Language.get('wcf.date.datePicker.year'));
+ elAttr(_dateYear, 'aria-label', Language.get('wcf.date.datePicker.year'));
+ _dateYear.addEventListener('change', this._changeYear.bind(this));
+ monthYearContainer.appendChild(_dateYear);
+
+ _dateMonthNext = elCreate('a');
+ _dateMonthNext.className = 'next jsTooltip';
+ _dateMonthNext.href = '#';
+ elAttr(_dateMonthNext, 'role', 'button');
+ elAttr(_dateMonthNext, 'tabindex', '0');
+ elAttr(_dateMonthNext, 'title', Language.get('wcf.date.datePicker.nextMonth'));
+ elAttr(_dateMonthNext, 'aria-label', Language.get('wcf.date.datePicker.nextMonth'));
+ _dateMonthNext.innerHTML = '<span class="icon icon16 fa-arrow-right"></span>';
+ _dateMonthNext.addEventListener(WCF_CLICK_EVENT, this.nextMonth.bind(this));
+ header.appendChild(_dateMonthNext);
+
+ _dateGrid = elCreate('ul');
+ _datePicker.appendChild(_dateGrid);
+
+ var item = elCreate('li');
+ item.className = 'weekdays';
+ _dateGrid.appendChild(item);
+
+ var span, weekdays = Language.get('__daysShort');
+ for (i = 0; i < 7; i++) {
+ var day = i + _firstDayOfWeek;
+ if (day > 6) day -= 7;
+
+ span = elCreate('span');
+ span.textContent = weekdays[day];
+ item.appendChild(span);
+ }
+
+ // create date grid
+ var callbackClick = this._click.bind(this), cell, row;
+ for (i = 0; i < 6; i++) {
+ row = elCreate('li');
+ _dateGrid.appendChild(row);
+
+ for (var j = 0; j < 7; j++) {
+ cell = elCreate('a');
+ cell.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ _dateCells.push(cell);
+
+ row.appendChild(cell);
+ }
+ }
+
+ _dateTime = elCreate('footer');
+ _datePicker.appendChild(_dateTime);
+
+ _dateHour = elCreate('select');
+ _dateHour.className = 'hour';
+ elAttr(_dateHour, 'title', Language.get('wcf.date.datePicker.hour'));
+ elAttr(_dateHour, 'aria-label', Language.get('wcf.date.datePicker.hour'));
+ _dateHour.addEventListener('change', this._formatValue.bind(this));
+
+ var tmp = '';
+ var date = new Date(2000, 0, 1);
+ var timeFormat = Language.get('wcf.date.timeFormat').replace(/:/, '').replace(/[isu]/g, '');
+ for (i = 0; i < 24; i++) {
+ date.setHours(i);
+ tmp += '<option value="' + i + '">' + DateUtil.format(date, timeFormat) + "</option>";
+ }
+ _dateHour.innerHTML = tmp;
+
+ _dateTime.appendChild(_dateHour);
+
+ _dateTime.appendChild(document.createTextNode('\u00A0:\u00A0'));
+
+ _dateMinute = elCreate('select');
+ _dateMinute.className = 'minute';
+ elAttr(_dateMinute, 'title', Language.get('wcf.date.datePicker.minute'));
+ elAttr(_dateMinute, 'aria-label', Language.get('wcf.date.datePicker.minute'));
+ _dateMinute.addEventListener('change', this._formatValue.bind(this));
+
+ tmp = '';
+ for (i = 0; i < 60; i++) {
+ tmp += '<option value="' + i + '">' + (i < 10 ? '0' + i.toString() : i) + '</option>';
+ }
+ _dateMinute.innerHTML = tmp;
+
+ _dateTime.appendChild(_dateMinute);
+
+ document.body.appendChild(_datePicker);
+ },
+
+ /**
+ * Shows the previous month.
+ */
+ previousMonth: function(event) {
+ event.preventDefault();
+
+ if (_dateMonth.value === '0') {
+ _dateMonth.value = 11;
+ _dateYear.value = ~~_dateYear.value - 1;
+ }
+ else {
+ _dateMonth.value = ~~_dateMonth.value - 1;
+ }
+
+ this._renderGrid(undefined, _dateMonth.value, _dateYear.value);
+ },
+
+ /**
+ * Shows the next month.
+ */
+ nextMonth: function(event) {
+ event.preventDefault();
+
+ if (_dateMonth.value === '11') {
+ _dateMonth.value = 0;
+ _dateYear.value = ~~_dateYear.value + 1;
+ }
+ else {
+ _dateMonth.value = ~~_dateMonth.value + 1;
+ }
+
+ this._renderGrid(undefined, _dateMonth.value, _dateYear.value);
+ },
+
+ /**
+ * Handles changes to the month select element.
+ *
+ * @param {object} event event object
+ */
+ _changeMonth: function(event) {
+ this._renderGrid(undefined, event.currentTarget.value);
+ },
+
+ /**
+ * Handles changes to the year select element.
+ *
+ * @param {object} event event object
+ */
+ _changeYear: function(event) {
+ this._renderGrid(undefined, undefined, event.currentTarget.value);
+ },
+
+ /**
+ * Handles clicks on an individual day.
+ *
+ * @param {object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ if (event.currentTarget.classList.contains('otherMonth')) {
+ return;
+ }
+
+ elData(_input, 'empty', false);
+
+ this._renderGrid(event.currentTarget.textContent);
+
+ var data = _data.get(_input);
+ if (!data.isDateTime) {
+ this._close();
+ }
+ },
+
+ /**
+ * Returns the current Date object or null.
+ *
+ * @param {(Element|string)} element input element or id
+ * @return {?Date} Date object or null
+ */
+ getDate: function(element) {
+ element = this._getElement(element);
+
+ if (element.hasAttribute('data-value')) {
+ return new Date(+elData(element, 'value'));
+ }
+
+ return null;
+ },
+
+ /**
+ * Sets the date of given element.
+ *
+ * @param {(HTMLInputElement|string)} element input element or id
+ * @param {Date} date Date object
+ */
+ setDate: function(element, date) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ elData(element, 'value', date.getTime());
+
+ var format = '', value;
+ if (data.isDateTime) {
+ if (data.isTimeOnly) {
+ value = DateUtil.formatTime(date);
+ format = 'H:i';
+ }
+ else if (data.ignoreTimezone) {
+ value = DateUtil.formatDateTime(date);
+ format = 'Y-m-dTH:i:s';
+ }
+ else {
+ value = DateUtil.formatDateTime(date);
+ format = 'c';
+ }
+ }
+ else {
+ value = DateUtil.formatDate(date);
+ format = 'Y-m-d';
+ }
+
+ element.value = value;
+ data.shadow.value = DateUtil.format(date, format);
+
+ // show clear button
+ if (!data.disableClear) {
+ data.clearButton.style.removeProperty('visibility');
+ }
+ },
+
+ /**
+ * Returns the current value.
+ *
+ * @param {(Element|string)} element input element or id
+ * @return {string} current date value
+ */
+ getValue: function (element) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ if (data) {
+ return data.shadow.value;
+ }
+
+ return '';
+ },
+
+ /**
+ * Clears the date value of given element.
+ *
+ * @param {(HTMLInputElement|string)} element input element or id
+ */
+ clear: function(element) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ element.removeAttribute('data-value');
+ element.value = '';
+
+ if (!data.disableClear) data.clearButton.style.setProperty('visibility', 'hidden', '');
+ data.isEmpty = true;
+ data.shadow.value = '';
+ },
+
+ /**
+ * Reverts the date picker into a normal input field.
+ *
+ * @param {(HTMLInputElement|string)} element input element or id
+ */
+ destroy: function(element) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ var container = element.parentNode;
+ container.parentNode.insertBefore(element, container);
+ elRemove(container);
+
+ elAttr(element, 'type', 'date' + (data.isDateTime ? 'time' : ''));
+ element.name = data.shadow.name;
+ element.value = data.shadow.value;
+
+ element.removeAttribute('data-value');
+ element.removeEventListener(WCF_CLICK_EVENT, _callbackOpen);
+ elRemove(data.shadow);
+
+ element.classList.remove('inputDatePicker');
+ element.readOnly = false;
+ _data['delete'](element);
+ },
+
+ /**
+ * Sets the callback invoked on picker close.
+ *
+ * @param {(Element|string)} element input element or id
+ * @param {function} callback callback function
+ */
+ setCloseCallback: function(element, callback) {
+ element = this._getElement(element);
+ _data.get(element).onClose = callback;
+ },
+
+ /**
+ * Validates given element or id if it represents an active date picker.
+ *
+ * @param {(Element|string)} element input element or id
+ * @return {Element} input element
+ */
+ _getElement: function(element) {
+ if (typeof element === 'string') element = elById(element);
+
+ if (!(element instanceof Element) || !element.classList.contains('inputDatePicker') || !_data.has(element)) {
+ throw new Error("Expected a valid date picker input element or id.");
+ }
+
+ return element;
+ },
+
+ /**
+ * @param {Event} event
+ */
+ _maintainFocus: function(event) {
+ if (_datePicker !== null && _datePicker.classList.contains('active')) {
+ if (!_datePicker.contains(event.target)) {
+ if (_wasInsidePicker) {
+ _input.nextElementSibling.focus();
+ _wasInsidePicker = false;
+ }
+ else {
+ elBySel('.previous', _datePicker).focus();
+ }
+ }
+ else {
+ _wasInsidePicker = true;
+ }
+ }
+ }
+ };
+
+ // backward-compatibility for `$.ui.datepicker` shim
+ window.__wcf_bc_datePicker = DatePicker;
+
+ return DatePicker;
+});
+
+/**
+ * Provides page actions such as "jump to top" and clipboard actions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Action
+ */
+define('WoltLabSuite/Core/Ui/Page/Action',['Dictionary', 'Dom/Util'], function(Dictionary, DomUtil) {
+ "use strict";
+
+ var _buttons = new Dictionary();
+ var _container = null;
+ var _didInit = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Action
+ */
+ return {
+ /**
+ * Initializes the page action container.
+ */
+ setup: function() {
+ _didInit = true;
+
+ _container = elCreate('ul');
+ _container.className = 'pageAction';
+ document.body.appendChild(_container);
+ },
+
+ /**
+ * Adds a button to the page action list. You can optionally provide a button name to
+ * insert the button right before it. Unmatched button names or empty value will cause
+ * the button to be prepended to the list.
+ *
+ * @param {string} buttonName unique identifier
+ * @param {Element} button button element, must not be wrapped in a <li>
+ * @param {string=} insertBeforeButton insert button before element identified by provided button name
+ */
+ add: function(buttonName, button, insertBeforeButton) {
+ if (_didInit === false) this.setup();
+
+ var listItem = elCreate('li');
+ button.classList.add('button');
+ button.classList.add('buttonPrimary');
+ listItem.appendChild(button);
+ elAttr(listItem, 'aria-hidden', (buttonName === 'toTop' ? 'true' : 'false'));
+ elData(listItem, 'name', buttonName);
+
+ // force 'to top' button to be always at the most outer position
+ if (buttonName === 'toTop') {
+ listItem.className = 'toTop initiallyHidden';
+ _container.appendChild(listItem);
+ }
+ else {
+ var insertBefore = null;
+ if (insertBeforeButton) {
+ insertBefore = _buttons.get(insertBeforeButton);
+ if (insertBefore !== undefined) {
+ insertBefore = insertBefore.parentNode;
+ }
+ }
+
+ if (insertBefore === null && _container.childElementCount) {
+ insertBefore = _container.children[0];
+ }
+
+ if (insertBefore === null) {
+ DomUtil.prepend(listItem, _container);
+ }
+ else {
+ _container.insertBefore(listItem, insertBefore);
+ }
+ }
+
+ _buttons.set(buttonName, button);
+ this._renderContainer();
+ },
+
+ /**
+ * Returns true if there is a registered button with the provided name.
+ *
+ * @param {string} buttonName unique identifier
+ * @return {boolean} true if there is a registered button with this name
+ */
+ has: function (buttonName) {
+ return _buttons.has(buttonName);
+ },
+
+ /**
+ * Returns the stored button by name or undefined.
+ *
+ * @param {string} buttonName unique identifier
+ * @return {Element} button element or undefined
+ */
+ get: function(buttonName) {
+ return _buttons.get(buttonName);
+ },
+
+ /**
+ * Removes a button by its button name.
+ *
+ * @param {string} buttonName unique identifier
+ */
+ remove: function(buttonName) {
+ var button = _buttons.get(buttonName);
+ if (button !== undefined) {
+ var listItem = button.parentNode;
+ listItem.addEventListener('animationend', function () {
+ try {
+ _container.removeChild(listItem);
+ _buttons.delete(buttonName);
+ }
+ catch (e) {
+ // ignore errors if the element has already been removed
+ }
+ });
+
+ this.hide(buttonName);
+ }
+ },
+
+ /**
+ * Hides a button by its button name.
+ *
+ * @param {string} buttonName unique identifier
+ */
+ hide: function(buttonName) {
+ var button = _buttons.get(buttonName);
+ if (button) {
+ elAttr(button.parentNode, 'aria-hidden', 'true');
+ this._renderContainer();
+ }
+ },
+
+ /**
+ * Shows a button by its button name.
+ *
+ * @param {string} buttonName unique identifier
+ */
+ show: function(buttonName) {
+ var button = _buttons.get(buttonName);
+ if (button) {
+ if (button.parentNode.classList.contains('initiallyHidden')) {
+ button.parentNode.classList.remove('initiallyHidden');
+ }
+
+ elAttr(button.parentNode, 'aria-hidden', 'false');
+ this._renderContainer();
+ }
+ },
+
+ /**
+ * Toggles the container's visibility.
+ *
+ * @protected
+ */
+ _renderContainer: function() {
+ var hasVisibleItems = false;
+ if (_container.childElementCount) {
+ for (var i = 0, length = _container.childElementCount; i < length; i++) {
+ if (elAttr(_container.children[i], 'aria-hidden') === 'false') {
+ hasVisibleItems = true;
+ break;
+ }
+ }
+ }
+
+ _container.classList[(hasVisibleItems ? 'add' : 'remove')]('active');
+ }
+ };
+});
+
+/**
+ * Provides a link to scroll to top once the page is scrolled by at least 50% the height of the window.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/JumpToTop
+ */
+define('WoltLabSuite/Core/Ui/Page/JumpToTop',['Environment', 'Language', './Action'], function(Environment, Language, PageAction) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function JumpToTop() { this.init(); }
+ JumpToTop.prototype = {
+ /**
+ * Initializes the top link for desktop browsers only.
+ */
+ init: function() {
+ // top link is not available on smartphones and tablets (they have a built-in function to accomplish this)
+ if (Environment.platform() !== 'desktop') {
+ return;
+ }
+
+ this._callbackScrollEnd = this._afterScroll.bind(this);
+ this._timeoutScroll = null;
+
+ var button = elCreate('a');
+ button.className = 'jsTooltip';
+ button.href = '#';
+ elAttr(button, 'title', Language.get('wcf.global.scrollUp'));
+ elAttr(button, 'role', 'button');
+ button.innerHTML = '<span class="icon icon32 fa-angle-up"></span>';
+
+ button.addEventListener(WCF_CLICK_EVENT, this._jump.bind(this));
+
+ PageAction.add('toTop', button);
+
+ window.addEventListener('scroll', this._scroll.bind(this));
+
+ // invoke callback on page load
+ this._afterScroll();
+ },
+
+ /**
+ * Handles clicks on the top link.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _jump: function(event) {
+ event.preventDefault();
+
+ elById('top').scrollIntoView({ behavior: 'smooth' });
+ },
+
+ /**
+ * Callback executed whenever the window is being scrolled.
+ *
+ * @protected
+ */
+ _scroll: function() {
+ if (this._timeoutScroll !== null) {
+ window.clearTimeout(this._timeoutScroll);
+ }
+
+ this._timeoutScroll = window.setTimeout(this._callbackScrollEnd, 100);
+ },
+
+ /**
+ * Delayed callback executed once the page has not been scrolled for a certain amount of time.
+ *
+ * @protected
+ */
+ _afterScroll: function() {
+ this._timeoutScroll = null;
+
+ PageAction[(window.pageYOffset >= 300) ? 'show' : 'hide']('toTop');
+ }
+ };
+
+ return JumpToTop;
+});
+
+/**
+ * Bootstraps WCF's JavaScript.
+ * It defines globals needed for backwards compatibility
+ * and runs modules that are needed on page load.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Bootstrap
+ */
+define(
+ 'WoltLabSuite/Core/Bootstrap',[
+ 'favico', 'enquire', 'perfect-scrollbar', 'WoltLabSuite/Core/Date/Time/Relative',
+ 'Ui/SimpleDropdown', 'WoltLabSuite/Core/Ui/Mobile', 'WoltLabSuite/Core/Ui/TabMenu', 'WoltLabSuite/Core/Ui/FlexibleMenu',
+ 'Ui/Dialog', 'WoltLabSuite/Core/Ui/Tooltip', 'WoltLabSuite/Core/Language', 'WoltLabSuite/Core/Environment',
+ 'WoltLabSuite/Core/Date/Picker', 'EventHandler', 'Core', 'WoltLabSuite/Core/Ui/Page/JumpToTop',
+ 'Devtools', 'Dom/ChangeListener'
+ ],
+ function(
+ favico, enquire, perfectScrollbar, DateTimeRelative,
+ UiSimpleDropdown, UiMobile, UiTabMenu, UiFlexibleMenu,
+ UiDialog, UiTooltip, Language, Environment,
+ DatePicker, EventHandler, Core, UiPageJumpToTop,
+ Devtools, DomChangeListener
+ )
+{
+ "use strict";
+
+ // perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js
+ window.Favico = favico;
+ window.enquire = enquire;
+ // non strict equals by intent
+ if (window.WCF == null) window.WCF = { };
+ if (window.WCF.Language == null) window.WCF.Language = { };
+ window.WCF.Language.get = Language.get;
+ window.WCF.Language.add = Language.add;
+ window.WCF.Language.addObject = Language.addObject;
+
+ // WCF.System.Event compatibility
+ window.__wcf_bc_eventHandler = EventHandler;
+
+ /**
+ * @exports WoltLabSuite/Core/Bootstrap
+ */
+ return {
+ /**
+ * Initializes the core UI modifications and unblocks jQuery's ready event.
+ *
+ * @param {Object=} options initialization options
+ */
+ setup: function(options) {
+ options = Core.extend({
+ enableMobileMenu: true
+ }, options);
+
+ //noinspection JSUnresolvedVariable
+ if (window.ENABLE_DEVELOPER_TOOLS) Devtools._internal_.enable();
+
+ Environment.setup();
+
+ DateTimeRelative.setup();
+ DatePicker.init();
+
+ UiSimpleDropdown.setup();
+ UiMobile.setup({
+ enableMobileMenu: options.enableMobileMenu
+ });
+ UiTabMenu.setup();
+ //UiFlexibleMenu.setup();
+ UiDialog.setup();
+ UiTooltip.setup();
+
+ // convert method=get into method=post
+ var forms = elBySelAll('form[method=get]');
+ for (var i = 0, length = forms.length; i < length; i++) {
+ forms[i].setAttribute('method', 'post');
+ }
+
+ if (Environment.browser() === 'microsoft') {
+ window.onbeforeunload = function() {
+ /* Prevent "Back navigation caching" (http://msdn.microsoft.com/en-us/library/ie/dn265017%28v=vs.85%29.aspx) */
+ };
+ }
+
+ var interval = 0;
+ interval = window.setInterval(function() {
+ if (typeof window.jQuery === 'function') {
+ window.clearInterval(interval);
+
+ // the 'jump to top' button triggers style recalculation/layout,
+ // putting it at the end of the jQuery queue avoids trashing the
+ // layout too early and thus delaying the page initialization
+ window.jQuery(function() {
+ new UiPageJumpToTop();
+ });
+
+ window.jQuery.holdReady(false);
+ }
+ }, 20);
+
+ this._initA11y();
+ DomChangeListener.add('WoltLabSuite/Core/Bootstrap', this._initA11y.bind(this));
+ },
+
+ _initA11y: function() {
+ elBySelAll('nav:not([aria-label]):not([aria-labelledby]):not([role])', undefined, function(element) {
+ elAttr(element, 'role', 'presentation');
+ });
+
+ elBySelAll('article:not([aria-label]):not([aria-labelledby]):not([role])', undefined, function(element) {
+ elAttr(element, 'role', 'presentation');
+ });
+ }
+ };
+});
+
+/**
+ * Dialog based style changer.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Style/Changer
+ */
+define('WoltLabSuite/Core/Controller/Style/Changer',['Ajax', 'Language', 'Ui/Dialog'], function(Ajax, Language, UiDialog) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Style/Changer
+ */
+ return {
+ /**
+ * Adds the style changer to the bottom navigation.
+ */
+ setup: function() {
+ var link = elBySel('.jsButtonStyleChanger');
+ if (link) {
+ link.addEventListener(WCF_CLICK_EVENT, this.showDialog.bind(this));
+ }
+ },
+
+ /**
+ * Loads and displays the style change dialog.
+ *
+ * @param {object} event event object
+ */
+ showDialog: function(event) {
+ event.preventDefault();
+
+ UiDialog.open(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'styleChanger',
+ options: {
+ disableContentPadding: true,
+ title: Language.get('wcf.style.changeStyle')
+ },
+ source: {
+ data: {
+ actionName: 'getStyleChooser',
+ className: 'wcf\\data\\style\\StyleAction'
+ },
+ after: (function(content) {
+ var styles = elBySelAll('.styleList > li', content);
+ for (var i = 0, length = styles.length; i < length; i++) {
+ var style = styles[i];
+
+ style.classList.add('pointer');
+ style.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }
+ }).bind(this)
+ }
+ };
+ },
+
+ /**
+ * Changes the style and reloads current page.
+ *
+ * @param {object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ Ajax.apiOnce({
+ data: {
+ actionName: 'changeStyle',
+ className: 'wcf\\data\\style\\StyleAction',
+ objectIDs: [ elData(event.currentTarget, 'style-id') ]
+ },
+ success: function() { window.location.reload(); }
+ });
+ }
+ };
+});
+
+/**
+ * Versatile popover manager.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Popover
+ */
+define('WoltLabSuite/Core/Controller/Popover',['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', 'Ui/Alignment'], function(Ajax, Dictionary, Environment, DomChangeListener, DomUtil, UiAlignment) {
+ "use strict";
+
+ var _activeId = null;
+ var _cache = new Dictionary();
+ var _elements = new Dictionary();
+ var _handlers = new Dictionary();
+ var _hoverId = null;
+ var _suspended = false;
+ var _timeoutEnter = null;
+ var _timeoutLeave = null;
+
+ var _popover = null;
+ var _popoverContent = null;
+
+ var _callbackClick = null;
+ var _callbackHide = null;
+ var _callbackMouseEnter = null;
+ var _callbackMouseLeave = null;
+
+ /** @const */ var STATE_NONE = 0;
+ /** @const */ var STATE_LOADING = 1;
+ /** @const */ var STATE_READY = 2;
+
+ /** @const */ var DELAY_HIDE = 500;
+ /** @const */ var DELAY_SHOW = 800;
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Popover
+ */
+ return {
+ /**
+ * Builds popover DOM elements and binds event listeners.
+ */
+ _setup: function() {
+ if (_popover !== null) {
+ return;
+ }
+
+ _popover = elCreate('div');
+ _popover.className = 'popover forceHide';
+
+ _popoverContent = elCreate('div');
+ _popoverContent.className = 'popoverContent';
+ _popover.appendChild(_popoverContent);
+
+ var pointer = elCreate('span');
+ pointer.className = 'elementPointer';
+ pointer.appendChild(elCreate('span'));
+ _popover.appendChild(pointer);
+
+ document.body.appendChild(_popover);
+
+ // static binding for callbacks (they don't change anyway and binding each time is expensive)
+ _callbackClick = this._hide.bind(this);
+ _callbackMouseEnter = this._mouseEnter.bind(this);
+ _callbackMouseLeave = this._mouseLeave.bind(this);
+
+ // event listener
+ _popover.addEventListener('mouseenter', this._popoverMouseEnter.bind(this));
+ _popover.addEventListener('mouseleave', _callbackMouseLeave);
+
+ _popover.addEventListener('animationend', this._clearContent.bind(this));
+
+ window.addEventListener('beforeunload', (function() {
+ _suspended = true;
+
+ if (_timeoutEnter !== null) {
+ window.clearTimeout(_timeoutEnter);
+ }
+
+ this._hide(true);
+ }).bind(this));
+
+ DomChangeListener.add('WoltLabSuite/Core/Controller/Popover', this._init.bind(this));
+ },
+
+ /**
+ * Initializes a popover handler.
+ *
+ * Usage:
+ *
+ * ControllerPopover.init({
+ * attributeName: 'data-object-id',
+ * className: 'fooLink',
+ * identifier: 'com.example.bar.foo',
+ * loadCallback: function(objectId, popover) {
+ * // request data for object id (e.g. via WoltLabSuite/Core/Ajax)
+ *
+ * // then call this to set the content
+ * popover.setContent('com.example.bar.foo', objectId, htmlTemplateString);
+ * }
+ * });
+ *
+ * @param {Object} options handler options
+ */
+ init: function(options) {
+ if (Environment.platform() !== 'desktop') {
+ return;
+ }
+
+ options.attributeName = options.attributeName || 'data-object-id';
+ options.legacy = (options.legacy === true);
+
+ this._setup();
+
+ if (_handlers.has(options.identifier)) {
+ return;
+ }
+
+ _handlers.set(options.identifier, {
+ attributeName: options.attributeName,
+ elements: options.legacy ? options.className : elByClass(options.className),
+ legacy: options.legacy,
+ loadCallback: options.loadCallback
+ });
+
+ this._init(options.identifier);
+ },
+
+ /**
+ * Initializes a popover handler.
+ *
+ * @param {string} identifier handler identifier
+ */
+ _init: function(identifier) {
+ if (typeof identifier === 'string' && identifier.length) {
+ this._initElements(_handlers.get(identifier), identifier);
+ }
+ else {
+ _handlers.forEach(this._initElements.bind(this));
+ }
+ },
+
+ /**
+ * Binds event listeners for popover-enabled elements.
+ *
+ * @param {Object} options handler options
+ * @param {string} identifier handler identifier
+ */
+ _initElements: function(options, identifier) {
+ var elements = options.legacy ? elBySelAll(options.elements) : options.elements;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ var element = elements[i];
+
+ var id = DomUtil.identify(element);
+ if (_cache.has(id)) {
+ return;
+ }
+ // skip if element is in a popover
+ if (element.closest('.popover') !== null) {
+ _cache.set(id, {
+ content: null,
+ state: STATE_NONE
+ });
+ return;
+ }
+
+ var objectId = (options.legacy) ? id : ~~element.getAttribute(options.attributeName);
+ if (objectId === 0) {
+ continue;
+ }
+
+ element.addEventListener('mouseenter', _callbackMouseEnter);
+ element.addEventListener('mouseleave', _callbackMouseLeave);
+
+ if (element.nodeName === 'A' && elAttr(element, 'href')) {
+ element.addEventListener(WCF_CLICK_EVENT, _callbackClick);
+ }
+
+ var cacheId = identifier + "-" + objectId;
+ elData(element, 'cache-id', cacheId);
+
+ _elements.set(id, {
+ element: element,
+ identifier: identifier,
+ objectId: objectId
+ });
+
+ if (!_cache.has(cacheId)) {
+ _cache.set(identifier + "-" + objectId, {
+ content: null,
+ state: STATE_NONE
+ });
+ }
+ }
+ },
+
+ /**
+ * Sets the content for given identifier and object id.
+ *
+ * @param {string} identifier handler identifier
+ * @param {int} objectId object id
+ * @param {string} content HTML string
+ */
+ setContent: function(identifier, objectId, content) {
+ var cacheId = identifier + "-" + objectId;
+ var data = _cache.get(cacheId);
+ if (data === undefined) {
+ throw new Error("Unable to find element for object id '" + objectId + "' (identifier: '" + identifier + "').");
+ }
+
+ var fragment = DomUtil.createFragmentFromHtml(content);
+ if (!fragment.childElementCount) fragment = DomUtil.createFragmentFromHtml('<p>' + content + '</p>');
+ data.content = fragment;
+ data.state = STATE_READY;
+
+ if (_activeId) {
+ var activeElement = _elements.get(_activeId).element;
+
+ if (elData(activeElement, 'cache-id') === cacheId) {
+ this._show();
+ }
+ }
+ },
+
+ /**
+ * Handles the mouse start hovering the popover-enabled element.
+ *
+ * @param {object} event event object
+ */
+ _mouseEnter: function(event) {
+ if (_suspended) {
+ return;
+ }
+
+ if (_timeoutEnter !== null) {
+ window.clearTimeout(_timeoutEnter);
+ _timeoutEnter = null;
+ }
+
+ var id = DomUtil.identify(event.currentTarget);
+ if (_activeId === id && _timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+
+ _hoverId = id;
+
+ _timeoutEnter = window.setTimeout((function() {
+ _timeoutEnter = null;
+
+ if (_hoverId === id) {
+ this._show();
+ }
+ }).bind(this), DELAY_SHOW);
+ },
+
+ /**
+ * Handles the mouse leaving the popover-enabled element or the popover itself.
+ */
+ _mouseLeave: function() {
+ _hoverId = null;
+
+ if (_timeoutLeave !== null) {
+ return;
+ }
+
+ if (_callbackHide === null) {
+ _callbackHide = this._hide.bind(this);
+ }
+
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ }
+
+ _timeoutLeave = window.setTimeout(_callbackHide, DELAY_HIDE);
+ },
+
+ /**
+ * Handles the mouse start hovering the popover element.
+ */
+ _popoverMouseEnter: function() {
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+ },
+
+ /**
+ * Shows the popover and loads content on-the-fly.
+ */
+ _show: function() {
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+
+ var forceHide = false;
+ if (_popover.classList.contains('active')) {
+ if (_activeId !== _hoverId) {
+ this._hide();
+
+ forceHide = true;
+ }
+ }
+ else if (_popoverContent.childElementCount) {
+ forceHide = true;
+ }
+
+ if (forceHide) {
+ _popover.classList.add('forceHide');
+
+ // force layout
+ //noinspection BadExpressionStatementJS
+ _popover.offsetTop;
+
+ this._clearContent();
+
+ _popover.classList.remove('forceHide');
+ }
+
+ _activeId = _hoverId;
+
+ var elementData = _elements.get(_activeId);
+ // check if source element is already gone
+ if (elementData === undefined) {
+ return;
+ }
+
+ var data = _cache.get(elData(elementData.element, 'cache-id'));
+
+ if (data.state === STATE_READY) {
+ _popoverContent.appendChild(data.content);
+
+ this._rebuild(_activeId);
+ }
+ else if (data.state === STATE_NONE) {
+ data.state = STATE_LOADING;
+
+ _handlers.get(elementData.identifier).loadCallback(elementData.objectId, this);
+ }
+ },
+
+ /**
+ * Hides the popover element.
+ */
+ _hide: function() {
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+
+ _popover.classList.remove('active');
+ },
+
+ /**
+ * Clears popover content by moving it back into the cache.
+ */
+ _clearContent: function() {
+ if (_activeId && _popoverContent.childElementCount && !_popover.classList.contains('active')) {
+ var activeElData = _cache.get(elData(_elements.get(_activeId).element, 'cache-id'));
+ while (_popoverContent.childNodes.length) {
+ activeElData.content.appendChild(_popoverContent.childNodes[0]);
+ }
+ }
+ },
+
+ /**
+ * Rebuilds the popover.
+ */
+ _rebuild: function() {
+ if (_popover.classList.contains('active')) {
+ return;
+ }
+
+ _popover.classList.remove('forceHide');
+ _popover.classList.add('active');
+
+ UiAlignment.set(_popover, _elements.get(_activeId).element, {
+ pointer: true,
+ vertical: 'top'
+ });
+ },
+
+ _ajaxSetup: function() {
+ return {
+ silent: true
+ };
+ },
+
+ /**
+ * Sends an AJAX requests to the server, simple wrapper to reuse the request object.
+ *
+ * @param {Object} data request data
+ * @param {function} success success callback
+ * @param {function=} failure error callback
+ */
+ ajaxApi: function(data, success, failure) {
+ if (typeof success !== 'function') {
+ throw new TypeError("Expected a valid callback for parameter 'success'.");
+ }
+
+ Ajax.api(this, data, success, failure);
+ }
+ };
+});
+
+/**
+ * Provides global helper methods to interact with ignored content.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Ignore
+ */
+define('WoltLabSuite/Core/Ui/User/Ignore',['List', 'Dom/ChangeListener'], function(List, DomChangeListener) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _rebuild: function() {},
+ _removeClass: function() {}
+ };
+ return Fake;
+ }
+
+ var _availableMessages = elByClass('ignoredUserMessage');
+ var _callback = null;
+ var _knownMessages = new List();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/User/Ignore
+ */
+ return {
+ /**
+ * Initializes the click handler for each ignored message and listens for
+ * newly inserted messages.
+ */
+ init: function () {
+ _callback = this._removeClass.bind(this);
+
+ this._rebuild();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/User/Ignore', this._rebuild.bind(this));
+ },
+
+ /**
+ * Adds ignored messages to the collection.
+ *
+ * @protected
+ */
+ _rebuild: function() {
+ var message;
+ for (var i = 0, length = _availableMessages.length; i < length; i++) {
+ message = _availableMessages[i];
+
+ if (!_knownMessages.has(message)) {
+ message.addEventListener(WCF_CLICK_EVENT, _callback);
+
+ _knownMessages.add(message);
+ }
+ }
+ },
+
+ /**
+ * Reveals a message on click/tap and disables the listener.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _removeClass: function(event) {
+ event.preventDefault();
+
+ var message = event.currentTarget;
+ message.classList.remove('ignoredUserMessage');
+ message.removeEventListener(WCF_CLICK_EVENT, _callback);
+ _knownMessages.delete(message);
+
+ // Firefox selects the entire message on click for no reason
+ window.getSelection().removeAllRanges();
+ }
+ };
+});
+
+/**
+ * Handles main menu overflow and a11y.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Header/Menu
+ */
+define('WoltLabSuite/Core/Ui/Page/Header/Menu',['Environment', 'Language', 'Ui/Screen'], function(Environment, Language, UiScreen) {
+ "use strict";
+
+ var _enabled = false;
+
+ // elements
+ var _buttonShowNext, _buttonShowPrevious, _firstElement, _menu;
+
+ // internal states
+ var _marginLeft = 0, _invisibleLeft = [], _invisibleRight = [];
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Header/Menu
+ */
+ return {
+ /**
+ * Initializes the main menu overflow handling.
+ */
+ init: function () {
+ _menu = elBySel('.mainMenu .boxMenu');
+ _firstElement = (_menu && _menu.childElementCount) ? _menu.children[0] : null;
+ if (_firstElement === null) {
+ throw new Error("Unable to find the menu.");
+ }
+
+ UiScreen.on('screen-lg', {
+ enable: this._enable.bind(this),
+ disable: this._disable.bind(this),
+ setup: this._setup.bind(this)
+ });
+ },
+
+ /**
+ * Enables the overflow handler.
+ *
+ * @protected
+ */
+ _enable: function () {
+ _enabled = true;
+
+ // Safari waits three seconds for a font to be loaded which causes the header menu items
+ // to be extremely wide while waiting for the font to be loaded. The extremely wide menu
+ // items in turn can cause the overflow controls to be shown even if the width of the header
+ // menu, after the font has been loaded successfully, does not require them. This width
+ // issue results in the next button being shown for a short time. To circumvent this issue,
+ // we wait a second before showing the obverflow controls in Safari.
+ // see https://webkit.org/blog/6643/improved-font-loading/
+ if (Environment.browser() === 'safari') {
+ window.setTimeout(this._rebuildVisibility.bind(this), 1000);
+ }
+ else {
+ this._rebuildVisibility();
+
+ // IE11 sometimes suffers from a timing issue
+ window.setTimeout(this._rebuildVisibility.bind(this), 1000);
+ }
+ },
+
+ /**
+ * Disables the overflow handler.
+ *
+ * @protected
+ */
+ _disable: function () {
+ _enabled = false;
+ },
+
+ /**
+ * Displays the next three menu items.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _showNext: function(event) {
+ event.preventDefault();
+
+ if (_invisibleRight.length) {
+ var showItem = _invisibleRight.slice(0, 3).pop();
+ this._setMarginLeft(_menu.clientWidth - (showItem.offsetLeft + showItem.clientWidth));
+
+ if (_menu.lastElementChild === showItem) {
+ _buttonShowNext.classList.remove('active');
+ }
+
+ _buttonShowPrevious.classList.add('active');
+ }
+ },
+
+ /**
+ * Displays the previous three menu items.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _showPrevious: function (event) {
+ event.preventDefault();
+
+ if (_invisibleLeft.length) {
+ var showItem = _invisibleLeft.slice(-3)[0];
+ this._setMarginLeft(showItem.offsetLeft * -1);
+
+ if (_menu.firstElementChild === showItem) {
+ _buttonShowPrevious.classList.remove('active');
+ }
+
+ _buttonShowNext.classList.add('active');
+ }
+ },
+
+ /**
+ * Sets the first item's margin-left value that is
+ * used to move the menu contents around.
+ *
+ * @param {int} offset changes to the margin-left value in pixel
+ * @protected
+ */
+ _setMarginLeft: function (offset) {
+ _marginLeft = Math.min(_marginLeft + offset, 0);
+
+ _firstElement.style.setProperty('margin-left', _marginLeft + 'px', '');
+ },
+
+ /**
+ * Toggles button overlays and rebuilds the list
+ * of invisible items from left to right.
+ *
+ * @protected
+ */
+ _rebuildVisibility: function () {
+ if (!_enabled) return;
+
+ _invisibleLeft = [];
+ _invisibleRight = [];
+
+ var menuWidth = _menu.clientWidth;
+ if (_menu.scrollWidth > menuWidth || _marginLeft < 0) {
+ var child;
+ for (var i = 0, length = _menu.childElementCount; i < length; i++) {
+ child = _menu.children[i];
+
+ var offsetLeft = child.offsetLeft;
+ if (offsetLeft < 0) {
+ _invisibleLeft.push(child);
+ }
+ else if (offsetLeft + child.clientWidth > menuWidth) {
+ _invisibleRight.push(child);
+ }
+ }
+ }
+
+ _buttonShowPrevious.classList[(_invisibleLeft.length ? 'add' : 'remove')]('active');
+ _buttonShowNext.classList[(_invisibleRight.length ? 'add' : 'remove')]('active');
+ },
+
+ /**
+ * Builds the UI and binds the event listeners.
+ *
+ * @protected
+ */
+ _setup: function () {
+ this._setupOverflow();
+ this._setupA11y();
+ },
+
+ /**
+ * Setups overflow handling.
+ *
+ * @protected
+ */
+ _setupOverflow: function () {
+ _buttonShowNext = elCreate('a');
+ _buttonShowNext.className = 'mainMenuShowNext';
+ _buttonShowNext.href = '#';
+ _buttonShowNext.innerHTML = '<span class="icon icon32 fa-angle-right"></span>';
+ _buttonShowNext.addEventListener(WCF_CLICK_EVENT, this._showNext.bind(this));
+
+ _menu.parentNode.appendChild(_buttonShowNext);
+
+ _buttonShowPrevious = elCreate('a');
+ _buttonShowPrevious.className = 'mainMenuShowPrevious';
+ _buttonShowPrevious.href = '#';
+ _buttonShowPrevious.innerHTML = '<span class="icon icon32 fa-angle-left"></span>';
+ _buttonShowPrevious.addEventListener(WCF_CLICK_EVENT, this._showPrevious.bind(this));
+
+ _menu.parentNode.insertBefore(_buttonShowPrevious, _menu.parentNode.firstChild);
+
+ var rebuildVisibility = this._rebuildVisibility.bind(this);
+ _firstElement.addEventListener('transitionend', rebuildVisibility);
+
+ window.addEventListener('resize', function () {
+ _firstElement.style.setProperty('margin-left', '0px', '');
+ _marginLeft = 0;
+
+ rebuildVisibility();
+ });
+
+ this._enable();
+ },
+
+ /**
+ * Setups a11y improvements.
+ *
+ * @protected
+ */
+ _setupA11y: function() {
+ elBySelAll('.boxMenuHasChildren', _menu, (function(element) {
+ var showMenu = false;
+ var link = elBySel('.boxMenuLink', element);
+ if (link) {
+ elAttr(link, 'aria-haspopup', true);
+ elAttr(link, 'aria-expanded', showMenu);
+ }
+
+ var showMenuButton = elCreate('button');
+ showMenuButton.className = 'visuallyHidden';
+ showMenuButton.tabindex = 0;
+ elAttr(showMenuButton, 'role', 'button');
+ elAttr(showMenuButton, 'aria-label', Language.get('wcf.global.button.showMenu'));
+ element.insertBefore(showMenuButton, link.nextSibling);
+
+ showMenuButton.addEventListener(WCF_CLICK_EVENT, function() {
+ showMenu = !showMenu;
+ elAttr(link, 'aria-expanded', showMenu);
+ elAttr(showMenuButton, 'aria-label', (showMenu ? Language.get('wcf.global.button.hideMenu') : Language.get('wcf.global.button.showMenu')));
+ });
+ }).bind(this));
+ }
+ };
+});
+
+/**
+ * Bootstraps WCF's JavaScript with additions for the frontend usage.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/BootstrapFrontend
+ */
+define(
+ 'WoltLabSuite/Core/BootstrapFrontend',[
+ 'WoltLabSuite/Core/BackgroundQueue', 'WoltLabSuite/Core/Bootstrap', 'WoltLabSuite/Core/Controller/Style/Changer',
+ 'WoltLabSuite/Core/Controller/Popover', 'WoltLabSuite/Core/Ui/User/Ignore', 'WoltLabSuite/Core/Ui/Page/Header/Menu'
+ ],
+ function(
+ BackgroundQueue, Bootstrap, ControllerStyleChanger,
+ ControllerPopover, UiUserIgnore, UiPageHeaderMenu
+ )
+{
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/BootstrapFrontend
+ */
+ return {
+ /**
+ * Bootstraps general modules and frontend exclusive ones.
+ *
+ * @param {object<string, *>} options bootstrap options
+ */
+ setup: function(options) {
+ // fix the background queue URL to always run against the current domain (avoiding CORS)
+ options.backgroundQueue.url = WSC_API_URL + options.backgroundQueue.url.substr(WCF_PATH.length);
+
+ Bootstrap.setup();
+
+ UiPageHeaderMenu.init();
+
+ if (options.styleChanger) {
+ ControllerStyleChanger.setup();
+ }
+
+ if (options.enableUserPopover) {
+ this._initUserPopover();
+ }
+
+ BackgroundQueue.setUrl(options.backgroundQueue.url);
+ if (Math.random() < 0.1 || options.backgroundQueue.force) {
+ // invoke the queue roughly every 10th request or on demand
+ BackgroundQueue.invoke();
+ }
+
+ if (COMPILER_TARGET_DEFAULT) {
+ UiUserIgnore.init();
+ }
+ },
+
+ /**
+ * Initializes user profile popover.
+ */
+ _initUserPopover: function() {
+ ControllerPopover.init({
+ attributeName: 'data-user-id',
+ className: 'userLink',
+ identifier: 'com.woltlab.wcf.user',
+ loadCallback: function(objectId, popover) {
+ var callback = function(data) {
+ popover.setContent('com.woltlab.wcf.user', objectId, data.returnValues.template);
+ };
+
+ popover.ajaxApi({
+ actionName: 'getUserProfile',
+ className: 'wcf\\data\\user\\UserProfileAction',
+ objectIDs: [ objectId ]
+ }, callback, callback);
+ }
+ });
+ }
+ };
+});
+
+/**
+ * Wrapper around the web browser's various clipboard APIs.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Clipboard
+ */
+define('WoltLabSuite/Core/Clipboard',[], function() {
+ "use strict";
+
+ return {
+ copyTextToClipboard: function (text) {
+ if (navigator.clipboard) {
+ return navigator.clipboard.writeText(text);
+ }
+ else if (window.getSelection) {
+ var textarea = elCreate('textarea');
+ textarea.contentEditable = true;
+ textarea.readOnly = false;
+ textarea.style.cssText = 'position: absolute; left: -9999px; top: -9999px; width: 0; height: 0;';
+ document.body.appendChild(textarea);
+ try {
+ // see: https://stackoverflow.com/a/34046084/782822
+ textarea.value = text;
+ var range = document.createRange();
+ range.selectNodeContents(textarea);
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ textarea.setSelectionRange(0, 999999);
+ if (!document.execCommand('copy')) {
+ return Promise.reject(new Error("execCommand('copy') failed"));
+ }
+ return Promise.resolve();
+ }
+ finally {
+ elRemove(textarea);
+ }
+ }
+
+ return Promise.reject(new Error('Neither navigator.clipboard, nor window.getSelection is supported.'));
+ },
+
+ copyElementTextToClipboard: function (element) {
+ return this.copyTextToClipboard(element.textContent);
+ }
+ };
+});
+
+/**
+ * Helper functions to convert between different color formats.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/ColorUtil
+ */
+define('WoltLabSuite/Core/ColorUtil',[], function () {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/ColorUtil
+ */
+ var ColorUtil = {
+ /**
+ * Converts a HSV color into RGB.
+ *
+ * @see https://secure.wikimedia.org/wikipedia/de/wiki/HSV-Farbraum#Transformation_von_RGB_und_HSV
+ *
+ * @param {int} h
+ * @param {int} s
+ * @param {int} v
+ * @return {Object}
+ */
+ hsvToRgb: function(h, s, v) {
+ var rgb = { r: 0, g: 0, b: 0 };
+ var h2, f, p, q, t;
+
+ h2 = Math.floor(h / 60);
+ f = h / 60 - h2;
+
+ s /= 100;
+ v /= 100;
+
+ p = v * (1 - s);
+ q = v * (1 - s * f);
+ t = v * (1 - s * (1 - f));
+
+ if (s == 0) {
+ rgb.r = rgb.g = rgb.b = v;
+ }
+ else {
+ switch (h2) {
+ case 1:
+ rgb.r = q;
+ rgb.g = v;
+ rgb.b = p;
+ break;
+
+ case 2:
+ rgb.r = p;
+ rgb.g = v;
+ rgb.b = t;
+ break;
+
+ case 3:
+ rgb.r = p;
+ rgb.g = q;
+ rgb.b = v;
+ break;
+
+ case 4:
+ rgb.r = t;
+ rgb.g = p;
+ rgb.b = v;
+ break;
+
+ case 5:
+ rgb.r = v;
+ rgb.g = p;
+ rgb.b = q;
+ break;
+
+ case 0:
+ case 6:
+ rgb.r = v;
+ rgb.g = t;
+ rgb.b = p;
+ break;
+ }
+ }
+
+ return {
+ r: Math.round(rgb.r * 255),
+ g: Math.round(rgb.g * 255),
+ b: Math.round(rgb.b * 255)
+ };
+ },
+
+ /**
+ * Converts a RGB color into HSV.
+ *
+ * @see https://secure.wikimedia.org/wikipedia/de/wiki/HSV-Farbraum#Transformation_von_RGB_und_HSV
+ *
+ * @param {int} r
+ * @param {int} g
+ * @param {int} b
+ * @return {Object}
+ */
+ rgbToHsv: function(r, g, b) {
+ var h, s, v;
+ var max, min, diff;
+
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ max = Math.max(Math.max(r, g), b);
+ min = Math.min(Math.min(r, g), b);
+ diff = max - min;
+
+ h = 0;
+ if (max !== min) {
+ switch (max) {
+ case r:
+ h = 60 * ((g - b) / diff);
+ break;
+
+ case g:
+ h = 60 * (2 + (b - r) / diff);
+ break;
+
+ case b:
+ h = 60 * (4 + (r - g) / diff);
+ break;
+ }
+
+ if (h < 0) {
+ h += 360;
+ }
+ }
+
+ if (max === 0) {
+ s = 0;
+ }
+ else {
+ s = diff / max;
+ }
+
+ v = max;
+
+ return {
+ h: Math.round(h),
+ s: Math.round(s * 100),
+ v: Math.round(v * 100)
+ };
+ },
+
+ /**
+ * Converts HEX into RGB.
+ *
+ * @param {string} hex
+ * @return {Object}
+ */
+ hexToRgb: function(hex) {
+ if (/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex)) {
+ // only convert #abc and #abcdef
+ var parts = hex.split('');
+
+ // drop the hashtag
+ if (parts[0] === '#') {
+ parts.shift();
+ }
+
+ // parse shorthand #xyz
+ if (parts.length === 3) {
+ return {
+ r: parseInt(parts[0] + '' + parts[0], 16),
+ g: parseInt(parts[1] + '' + parts[1], 16),
+ b: parseInt(parts[2] + '' + parts[2], 16)
+ };
+ }
+ else {
+ return {
+ r: parseInt(parts[0] + '' + parts[1], 16),
+ g: parseInt(parts[2] + '' + parts[3], 16),
+ b: parseInt(parts[4] + '' + parts[5], 16)
+ };
+ }
+ }
+
+ return Number.NaN;
+ },
+
+ /**
+ * Converts a RGB into HEX.
+ *
+ * @see http://www.linuxtopia.org/online_books/javascript_guides/javascript_faq/rgbtohex.htm
+ *
+ * @param {int} r
+ * @param {int} g
+ * @param {int} b
+ * @return {string}
+ */
+ rgbToHex: function(r, g, b) {
+ var charList = "0123456789ABCDEF";
+
+ if (g === undefined) {
+ if (r.toString().match(/^rgba?\((\d+), ?(\d+), ?(\d+)(?:, ?[0-9.]+)?\)$/)) {
+ r = RegExp.$1;
+ g = RegExp.$2;
+ b = RegExp.$3;
+ }
+ }
+
+ return (charList.charAt((r - r % 16) / 16) + '' + charList.charAt(r % 16)) + '' + (charList.charAt((g - g % 16) / 16) + '' + charList.charAt(g % 16)) + '' + (charList.charAt((b - b % 16) / 16) + '' + charList.charAt(b % 16));
+ }
+ };
+
+ // WCF.ColorPicker compatibility (color format conversion)
+ window.__wcf_bc_colorUtil = ColorUtil;
+
+ return ColorUtil;
+});
+
+/**
+ * Provides helper functions for file handling.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/FileUtil
+ */
+define('WoltLabSuite/Core/FileUtil',['Dictionary', 'StringUtil'], function(Dictionary, StringUtil) {
+ "use strict";
+
+ var _fileExtensionIconMapping = Dictionary.fromObject({
+ // archive
+ zip: 'archive',
+ rar: 'archive',
+ tar: 'archive',
+ gz: 'archive',
+
+ // audio
+ mp3: 'audio',
+ ogg: 'audio',
+ wav: 'audio',
+
+ // code
+ php: 'code',
+ html: 'code',
+ htm: 'code',
+ tpl: 'code',
+ js: 'code',
+
+ // excel
+ xls: 'excel',
+ ods: 'excel',
+ xlsx: 'excel',
+
+ // image
+ gif: 'image',
+ jpg: 'image',
+ jpeg: 'image',
+ png: 'image',
+ bmp: 'image',
+ webp: 'image',
+
+ // video
+ avi: 'video',
+ wmv: 'video',
+ mov: 'video',
+ mp4: 'video',
+ mpg: 'video',
+ mpeg: 'video',
+ flv: 'video',
+
+ // pdf
+ pdf: 'pdf',
+
+ // powerpoint
+ ppt: 'powerpoint',
+ pptx: 'powerpoint',
+
+ // text
+ txt: 'text',
+
+ // word
+ doc: 'word',
+ docx: 'word',
+ odt: 'word'
+ });
+
+ var _mimeTypeExtensionMapping = Dictionary.fromObject({
+ // archive
+ 'application/zip': 'zip',
+ 'application/x-zip-compressed': 'zip',
+ 'application/rar': 'rar',
+ 'application/vnd.rar': 'rar',
+ 'application/x-rar-compressed': 'rar',
+ 'application/x-tar': 'tar',
+ 'application/x-gzip': 'gz',
+ 'application/gzip': 'gz',
+
+ // audio
+ 'audio/mpeg': 'mp3',
+ 'audio/mp3': 'mp3',
+ 'audio/ogg': 'ogg',
+ 'audio/x-wav': 'wav',
+
+ // code
+ 'application/x-php': 'php',
+ 'text/html': 'html',
+ 'application/javascript': 'js',
+
+ // excel
+ 'application/vnd.ms-excel': 'xls',
+ 'application/vnd.oasis.opendocument.spreadsheet': 'ods',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
+
+ // image
+ 'image/gif': 'gif',
+ 'image/jpeg': 'jpg',
+ 'image/png': 'png',
+ 'image/x-ms-bmp': 'bmp',
+ 'image/bmp': 'bmp',
+ 'image/webp': 'webp',
+
+ // video
+ 'video/x-msvideo': 'avi',
+ 'video/x-ms-wmv': 'wmv',
+ 'video/quicktime': 'mov',
+ 'video/mp4': 'mp4',
+ 'video/mpeg': 'mpg',
+ 'video/x-flv': 'flv',
+
+ // pdf
+ 'application/pdf': 'pdf',
+
+ // powerpoint
+ 'application/vnd.ms-powerpoint': 'ppt',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
+
+ // text
+ 'text/plain': 'txt',
+
+ // word
+ 'application/msword': 'doc',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
+ 'application/vnd.oasis.opendocument.text': 'odt'
+ });
+
+ return {
+ /**
+ * Formats the given filesize.
+ *
+ * @param {integer} byte number of bytes
+ * @param {integer} precision number of decimals
+ * @return {string} formatted filesize
+ */
+ formatFilesize: function(byte, precision) {
+ if (precision === undefined) {
+ precision = 2;
+ }
+
+ var symbol = 'Byte';
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'kB';
+ }
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'MB';
+ }
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'GB';
+ }
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'TB';
+ }
+
+ return StringUtil.formatNumeric(byte, -precision) + ' ' + symbol;
+ },
+
+ /**
+ * Returns the icon name for given filename.
+ *
+ * Note: For any file icon name like `fa-file-word`, only `word`
+ * will be returned by this method.
+ *
+ * @parsm {string} filename name of file for which icon name will be returned
+ * @return {string} FontAwesome icon name
+ */
+ getIconNameByFilename: function(filename) {
+ var lastDotPosition = filename.lastIndexOf('.');
+ if (lastDotPosition !== false) {
+ var extension = filename.substr(lastDotPosition + 1);
+
+ if (_fileExtensionIconMapping.has(extension)) {
+ return _fileExtensionIconMapping.get(extension);
+ }
+ }
+
+ return '';
+ },
+
+ /**
+ * Returns a known file extension including a leading dot or an empty string.
+ *
+ * @param mimetype the mimetype to get the common file extension for
+ * @returns {string} the file dot prefixed extension or an empty string
+ */
+ getExtensionByMimeType: function (mimetype) {
+ if (_mimeTypeExtensionMapping.has(mimetype)) {
+ return '.' + _mimeTypeExtensionMapping.get(mimetype);
+ }
+
+ return '';
+ },
+
+ /**
+ * Constructs a File object from a Blob
+ *
+ * @param blob the blob to convert
+ * @param filename the filename
+ * @returns {File} the File object
+ */
+ blobToFile: function (blob, filename) {
+ var ext = this.getExtensionByMimeType(blob.type);
+ var File = window.File;
+
+ try {
+ // IE11 does not support the file constructor
+ new File([], 'ie11-check');
+ }
+ catch (error) {
+ // Create a good enough File object based on the Blob prototype
+ File = function File(chunks, filename, options) {
+ var self = Blob.call(this, chunks, options);
+
+ self.name = filename;
+ self.lastModifiedDate = new Date();
+
+ return self;
+ };
+
+ File.prototype = Object.create(window.File.prototype);
+ }
+
+ return new File([blob], filename + ext, {type: blob.type});
+ },
+ };
+});
+
+/**
+ * Manages user permissions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Permission
+ */
+define('WoltLabSuite/Core/Permission',['Dictionary'], function(Dictionary) {
+ "use strict";
+
+ var _permissions = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Permission
+ */
+ return {
+ /**
+ * Adds a single permission to the store.
+ *
+ * @param {string} permission permission name
+ * @param {boolean} value permission value
+ */
+ add: function(permission, value) {
+ if (typeof value !== "boolean") {
+ throw new TypeError("Permission value has to be boolean.");
+ }
+
+ _permissions.set(permission, value);
+ },
+
+ /**
+ * Adds all the permissions in the given object to the store.
+ *
+ * @param {Object.<string, boolean>} object permission list
+ */
+ addObject: function(object) {
+ for (var key in object) {
+ if (objOwns(object, key)) {
+ this.add(key, object[key]);
+ }
+ }
+ },
+
+ /**
+ * Returns the value of a permission.
+ *
+ * If the permission is unknown, false is returned.
+ *
+ * @param {string} permission permission name
+ * @return {boolean} permission value
+ */
+ get: function(permission) {
+ if (_permissions.has(permission)) {
+ return _permissions.get(permission);
+ }
+
+ return false;
+ }
+ };
+});
+
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism */
+var _self = (typeof window !== 'undefined')
+ ? window // if in browser
+ : (
+ (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
+ ? self // if in worker
+ : {} // if in node js
+ );
+
+/**
+ * Prism: Lightweight, robust, elegant syntax highlighting
+ * MIT license http://www.opensource.org/licenses/mit-license.php/
+ * @author Lea Verou http://lea.verou.me
+ */
+
+var Prism = (function(){
+
+// Private helper vars
+var lang = /\blang(?:uage)?-([\w-]+)\b/i;
+var uniqueId = 0;
+
+var _ = _self.Prism = {
+ manual: _self.Prism && _self.Prism.manual,
+ disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
+ util: {
+ encode: function (tokens) {
+ if (tokens instanceof Token) {
+ return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
+ } else if (_.util.type(tokens) === 'Array') {
+ return tokens.map(_.util.encode);
+ } else {
+ return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' ');
+ }
+ },
+
+ type: function (o) {
+ return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
+ },
+
+ objId: function (obj) {
+ if (!obj['__id']) {
+ Object.defineProperty(obj, '__id', { value: ++uniqueId });
+ }
+ return obj['__id'];
+ },
+
+ // Deep clone a language definition (e.g. to extend it)
+ clone: function (o, visited) {
+ var type = _.util.type(o);
+ visited = visited || {};
+
+ switch (type) {
+ case 'Object':
+ if (visited[_.util.objId(o)]) {
+ return visited[_.util.objId(o)];
+ }
+ var clone = {};
+ visited[_.util.objId(o)] = clone;
+
+ for (var key in o) {
+ if (o.hasOwnProperty(key)) {
+ clone[key] = _.util.clone(o[key], visited);
+ }
+ }
+
+ return clone;
+
+ case 'Array':
+ if (visited[_.util.objId(o)]) {
+ return visited[_.util.objId(o)];
+ }
+ var clone = [];
+ visited[_.util.objId(o)] = clone;
+
+ o.forEach(function (v, i) {
+ clone[i] = _.util.clone(v, visited);
+ });
+
+ return clone;
+ }
+
+ return o;
+ }
+ },
+
+ languages: {
+ extend: function (id, redef) {
+ var lang = _.util.clone(_.languages[id]);
+
+ for (var key in redef) {
+ lang[key] = redef[key];
+ }
+
+ return lang;
+ },
+
+ /**
+ * Insert a token before another token in a language literal
+ * As this needs to recreate the object (we cannot actually insert before keys in object literals),
+ * we cannot just provide an object, we need anobject and a key.
+ * @param inside The key (or language id) of the parent
+ * @param before The key to insert before. If not provided, the function appends instead.
+ * @param insert Object with the key/value pairs to insert
+ * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
+ */
+ insertBefore: function (inside, before, insert, root) {
+ root = root || _.languages;
+ var grammar = root[inside];
+
+ if (arguments.length == 2) {
+ insert = arguments[1];
+
+ for (var newToken in insert) {
+ if (insert.hasOwnProperty(newToken)) {
+ grammar[newToken] = insert[newToken];
+ }
+ }
+
+ return grammar;
+ }
+
+ var ret = {};
+
+ for (var token in grammar) {
+
+ if (grammar.hasOwnProperty(token)) {
+
+ if (token == before) {
+
+ for (var newToken in insert) {
+
+ if (insert.hasOwnProperty(newToken)) {
+ ret[newToken] = insert[newToken];
+ }
+ }
+ }
+
+ ret[token] = grammar[token];
+ }
+ }
+
+ var old = root[inside];
+ root[inside] = ret;
+
+ // Update references in other language definitions
+ _.languages.DFS(_.languages, function(key, value) {
+ if (value === old && key != inside) {
+ this[key] = ret;
+ }
+ });
+
+ return ret;
+ },
+
+ // Traverse a language definition with Depth First Search
+ DFS: function(o, callback, type, visited) {
+ visited = visited || {};
+ for (var i in o) {
+ if (o.hasOwnProperty(i)) {
+ callback.call(o, i, o[i], type || i);
+
+ if (_.util.type(o[i]) === 'Object' && !visited[_.util.objId(o[i])]) {
+ visited[_.util.objId(o[i])] = true;
+ _.languages.DFS(o[i], callback, null, visited);
+ }
+ else if (_.util.type(o[i]) === 'Array' && !visited[_.util.objId(o[i])]) {
+ visited[_.util.objId(o[i])] = true;
+ _.languages.DFS(o[i], callback, i, visited);
+ }
+ }
+ }
+ }
+ },
+ plugins: {},
+
+ highlightAll: function(async, callback) {
+ _.highlightAllUnder(document, async, callback);
+ },
+
+ highlightAllUnder: function(container, async, callback) {
+ var env = {
+ callback: callback,
+ selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
+ };
+
+ _.hooks.run("before-highlightall", env);
+
+ var elements = env.elements || container.querySelectorAll(env.selector);
+
+ for (var i=0, element; element = elements[i++];) {
+ _.highlightElement(element, async === true, env.callback);
+ }
+ },
+
+ highlightElement: function(element, async, callback) {
+ // Find language
+ var language, grammar, parent = element;
+
+ while (parent && !lang.test(parent.className)) {
+ parent = parent.parentNode;
+ }
+
+ if (parent) {
+ language = (parent.className.match(lang) || [,''])[1].toLowerCase();
+ grammar = _.languages[language];
+ }
+
+ // Set language on the element, if not present
+ element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
+
+ if (element.parentNode) {
+ // Set language on the parent, for styling
+ parent = element.parentNode;
+
+ if (/pre/i.test(parent.nodeName)) {
+ parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
+ }
+ }
+
+ var code = element.textContent;
+
+ var env = {
+ element: element,
+ language: language,
+ grammar: grammar,
+ code: code
+ };
+
+ _.hooks.run('before-sanity-check', env);
+
+ if (!env.code || !env.grammar) {
+ if (env.code) {
+ _.hooks.run('before-highlight', env);
+ env.element.textContent = env.code;
+ _.hooks.run('after-highlight', env);
+ }
+ _.hooks.run('complete', env);
+ return;
+ }
+
+ _.hooks.run('before-highlight', env);
+
+ if (async && _self.Worker) {
+ var worker = new Worker(_.filename);
+
+ worker.onmessage = function(evt) {
+ env.highlightedCode = evt.data;
+
+ _.hooks.run('before-insert', env);
+
+ env.element.innerHTML = env.highlightedCode;
+
+ callback && callback.call(env.element);
+ _.hooks.run('after-highlight', env);
+ _.hooks.run('complete', env);
+ };
+
+ worker.postMessage(JSON.stringify({
+ language: env.language,
+ code: env.code,
+ immediateClose: true
+ }));
+ }
+ else {
+ env.highlightedCode = _.highlight(env.code, env.grammar, env.language);
+
+ _.hooks.run('before-insert', env);
+
+ env.element.innerHTML = env.highlightedCode;
+
+ callback && callback.call(element);
+
+ _.hooks.run('after-highlight', env);
+ _.hooks.run('complete', env);
+ }
+ },
+
+ highlight: function (text, grammar, language) {
+ var env = {
+ code: text,
+ grammar: grammar,
+ language: language
+ };
+ _.hooks.run('before-tokenize', env);
+ env.tokens = _.tokenize(env.code, env.grammar);
+ _.hooks.run('after-tokenize', env);
+ return Token.stringify(_.util.encode(env.tokens), env.language);
+ },
+
+ matchGrammar: function (text, strarr, grammar, index, startPos, oneshot, target) {
+ var Token = _.Token;
+
+ for (var token in grammar) {
+ if(!grammar.hasOwnProperty(token) || !grammar[token]) {
+ continue;
+ }
+
+ if (token == target) {
+ return;
+ }
+
+ var patterns = grammar[token];
+ patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns];
+
+ for (var j = 0; j < patterns.length; ++j) {
+ var pattern = patterns[j],
+ inside = pattern.inside,
+ lookbehind = !!pattern.lookbehind,
+ greedy = !!pattern.greedy,
+ lookbehindLength = 0,
+ alias = pattern.alias;
+
+ if (greedy && !pattern.pattern.global) {
+ // Without the global flag, lastIndex won't work
+ var flags = pattern.pattern.toString().match(/[imuy]*$/)[0];
+ pattern.pattern = RegExp(pattern.pattern.source, flags + "g");
+ }
+
+ pattern = pattern.pattern || pattern;
+
+ // Don’t cache length as it changes during the loop
+ for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) {
+
+ var str = strarr[i];
+
+ if (strarr.length > text.length) {
+ // Something went terribly wrong, ABORT, ABORT!
+ return;
+ }
+
+ if (str instanceof Token) {
+ continue;
+ }
+
+ if (greedy && i != strarr.length - 1) {
+ pattern.lastIndex = pos;
+ var match = pattern.exec(text);
+ if (!match) {
+ break;
+ }
+
+ var from = match.index + (lookbehind ? match[1].length : 0),
+ to = match.index + match[0].length,
+ k = i,
+ p = pos;
+
+ for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) {
+ p += strarr[k].length;
+ // Move the index i to the element in strarr that is closest to from
+ if (from >= p) {
+ ++i;
+ pos = p;
+ }
+ }
+
+ // If strarr[i] is a Token, then the match starts inside another Token, which is invalid
+ if (strarr[i] instanceof Token) {
+ continue;
+ }
+
+ // Number of tokens to delete and replace with the new match
+ delNum = k - i;
+ str = text.slice(pos, p);
+ match.index -= pos;
+ } else {
+ pattern.lastIndex = 0;
+
+ var match = pattern.exec(str),
+ delNum = 1;
+ }
+
+ if (!match) {
+ if (oneshot) {
+ break;
+ }
+
+ continue;
+ }
+
+ if(lookbehind) {
+ lookbehindLength = match[1] ? match[1].length : 0;
+ }
+
+ var from = match.index + lookbehindLength,
+ match = match[0].slice(lookbehindLength),
+ to = from + match.length,
+ before = str.slice(0, from),
+ after = str.slice(to);
+
+ var args = [i, delNum];
+
+ if (before) {
+ ++i;
+ pos += before.length;
+ args.push(before);
+ }
+
+ var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);
+
+ args.push(wrapped);
+
+ if (after) {
+ args.push(after);
+ }
+
+ Array.prototype.splice.apply(strarr, args);
+
+ if (delNum != 1)
+ _.matchGrammar(text, strarr, grammar, i, pos, true, token);
+
+ if (oneshot)
+ break;
+ }
+ }
+ }
+ },
+
+ tokenize: function(text, grammar, language) {
+ var strarr = [text];
+
+ var rest = grammar.rest;
+
+ if (rest) {
+ for (var token in rest) {
+ grammar[token] = rest[token];
+ }
+
+ delete grammar.rest;
+ }
+
+ _.matchGrammar(text, strarr, grammar, 0, 0, false);
+
+ return strarr;
+ },
+
+ hooks: {
+ all: {},
+
+ add: function (name, callback) {
+ var hooks = _.hooks.all;
+
+ hooks[name] = hooks[name] || [];
+
+ hooks[name].push(callback);
+ },
+
+ run: function (name, env) {
+ var callbacks = _.hooks.all[name];
+
+ if (!callbacks || !callbacks.length) {
+ return;
+ }
+
+ for (var i=0, callback; callback = callbacks[i++];) {
+ callback(env);
+ }
+ }
+ }
+};
+
+var Token = _.Token = function(type, content, alias, matchedStr, greedy) {
+ this.type = type;
+ this.content = content;
+ this.alias = alias;
+ // Copy of the full string this token was created from
+ this.length = (matchedStr || "").length|0;
+ this.greedy = !!greedy;
+};
+
+Token.stringify = function(o, language, parent) {
+ if (typeof o == 'string') {
+ return o;
+ }
+
+ if (_.util.type(o) === 'Array') {
+ return o.map(function(element) {
+ return Token.stringify(element, language, o);
+ }).join('');
+ }
+
+ var env = {
+ type: o.type,
+ content: Token.stringify(o.content, language, parent),
+ tag: 'span',
+ classes: ['token', o.type],
+ attributes: {},
+ language: language,
+ parent: parent
+ };
+
+ if (o.alias) {
+ var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
+ Array.prototype.push.apply(env.classes, aliases);
+ }
+
+ _.hooks.run('wrap', env);
+
+ var attributes = Object.keys(env.attributes).map(function(name) {
+ return name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"';
+ }).join(' ');
+
+ return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '</' + env.tag + '>';
+
+};
+
+if (!_self.document) {
+ if (!_self.addEventListener) {
+ // in Node.js
+ return _self.Prism;
+ }
+
+ if (!_.disableWorkerMessageHandler) {
+ // In worker
+ _self.addEventListener('message', function (evt) {
+ var message = JSON.parse(evt.data),
+ lang = message.language,
+ code = message.code,
+ immediateClose = message.immediateClose;
+
+ _self.postMessage(_.highlight(code, _.languages[lang], lang));
+ if (immediateClose) {
+ _self.close();
+ }
+ }, false);
+ }
+
+ return _self.Prism;
+}
+
+//Get current script and highlight
+var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop();
+
+if (script) {
+ _.filename = script.src;
+
+ if (!_.manual && !script.hasAttribute('data-manual')) {
+ if(document.readyState !== "loading") {
+ if (window.requestAnimationFrame) {
+ window.requestAnimationFrame(_.highlightAll);
+ } else {
+ window.setTimeout(_.highlightAll, 16);
+ }
+ }
+ else {
+ document.addEventListener('DOMContentLoaded', _.highlightAll);
+ }
+ }
+}
+
+return _self.Prism;
+
+})();
+
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Prism;
+}
+
+// hack for components to work correctly in node.js
+if (typeof global !== 'undefined') {
+ global.Prism = Prism;
+}
+;
+
+define("prism/prism", function(){});
+
+/**
+ * Augments the Prism syntax highlighter with additional functions.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Prism
+ */
+
+window.Prism = window.Prism || {}
+window.Prism.manual = true
+
+define('WoltLabSuite/Core/Prism',['prism/prism'], function () {
+ Prism.wscSplitIntoLines = function (container) {
+ var frag = document.createDocumentFragment();
+ var lineNo = 1;
+ var it, node, line;
+
+ function newLine() {
+ var line = elCreate('span');
+ elData(line, 'number', lineNo++);
+ frag.appendChild(line);
+
+ return line;
+ }
+
+ // IE11 expects a fourth, non-standard, parameter (entityReferenceExpansion) and a valid function as third
+ it = document.createNodeIterator(container, NodeFilter.SHOW_TEXT, function () {
+ return NodeFilter.FILTER_ACCEPT;
+ }, false);
+
+ line = newLine(lineNo);
+ while (node = it.nextNode()) {
+ node.data.split(/\r?\n/).forEach(function (codeLine, index) {
+ var current, parent;
+
+ // We are behind a newline, insert \n and create new container.
+ if (index >= 1) {
+ line.appendChild(document.createTextNode("\n"));
+ line = newLine(lineNo);
+ }
+
+ current = document.createTextNode(codeLine);
+
+ // Copy hierarchy (to preserve CSS classes).
+ parent = node.parentNode
+ while (parent !== container) {
+ var clone = parent.cloneNode(false);
+ clone.appendChild(current);
+ current = clone;
+ parent = parent.parentNode;
+ }
+
+ line.appendChild(current);
+ });
+ }
+
+ return frag;
+ };
+
+ return Prism;
+});
+
+/**
+ * Uploads file via AJAX.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Upload
+ */
+define('WoltLabSuite/Core/Upload',['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util', 'Dom/Traverse'], function(AjaxRequest, Core, DomChangeListener, Language, DomUtil, DomTraverse) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _createButton: function() {},
+ _createFileElement: function() {},
+ _createFileElements: function() {},
+ _failure: function() {},
+ _getParameters: function() {},
+ _insertButton: function() {},
+ _progress: function() {},
+ _removeButton: function() {},
+ _success: function() {},
+ _upload: function() {},
+ _uploadFiles: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function Upload(buttonContainerId, targetId, options) {
+ options = options || {};
+
+ if (options.className === undefined) {
+ throw new Error("Missing class name.");
+ }
+
+ // set default options
+ this._options = Core.extend({
+ // name of the PHP action
+ action: 'upload',
+ // is true if multiple files can be uploaded at once
+ multiple: false,
+ // name if the upload field
+ name: '__files[]',
+ // is true if every file from a multi-file selection is uploaded in its own request
+ singleFileRequests: false,
+ // url for uploading file
+ url: 'index.php?ajax-upload/&t=' + SECURITY_TOKEN
+ }, options);
+
+ this._options.url = Core.convertLegacyUrl(this._options.url);
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
+ }
+
+ this._buttonContainer = elById(buttonContainerId);
+ if (this._buttonContainer === null) {
+ throw new Error("Element id '" + buttonContainerId + "' is unknown.");
+ }
+
+ this._target = elById(targetId);
+ if (targetId === null) {
+ throw new Error("Element id '" + targetId + "' is unknown.");
+ }
+ if (options.multiple && this._target.nodeName !== 'UL' && this._target.nodeName !== 'OL' && this._target.nodeName !== 'TBODY') {
+ throw new Error("Target element has to be list or table body if uploading multiple files is supported.");
+ }
+
+ this._fileElements = [];
+ this._internalFileId = 0;
+
+ // upload ids that belong to an upload of multiple files at once
+ this._multiFileUploadIds = [];
+
+ this._createButton();
+ }
+ Upload.prototype = {
+ /**
+ * Creates the upload button.
+ */
+ _createButton: function() {
+ this._fileUpload = elCreate('input');
+ elAttr(this._fileUpload, 'type', 'file');
+ elAttr(this._fileUpload, 'name', this._options.name);
+ if (this._options.multiple) {
+ elAttr(this._fileUpload, 'multiple', 'true');
+ }
+ this._fileUpload.addEventListener('change', this._upload.bind(this));
+
+ this._button = elCreate('p');
+ this._button.className = 'button uploadButton';
+ elAttr(this._button, 'role', 'button');
+ elAttr(this._button, 'tabindex', '0');
+
+ var span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.upload');
+ this._button.appendChild(span);
+
+ DomUtil.prepend(this._fileUpload, this._button);
+
+ this._insertButton();
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Creates the document element for an uploaded file.
+ *
+ * @param {File} file uploaded file
+ * @return {HTMLElement}
+ */
+ _createFileElement: function(file) {
+ var progress = elCreate('progress');
+ elAttr(progress, 'max', 100);
+
+ if (this._target.nodeName === 'OL' || this._target.nodeName === 'UL') {
+ var li = elCreate('li');
+ li.innerText = file.name;
+ li.appendChild(progress);
+
+ this._target.appendChild(li);
+
+ return li;
+ }
+ else if (this._target.nodeName === 'TBODY') {
+ return this._createFileTableRow(file);
+ }
+ else {
+ var p = elCreate('p');
+ p.appendChild(progress);
+
+ this._target.appendChild(p);
+
+ return p;
+ }
+ },
+
+ /**
+ * Creates the document elements for uploaded files.
+ *
+ * @param {(FileList|Array.<File>)} files uploaded files
+ */
+ _createFileElements: function(files) {
+ if (files.length) {
+ var uploadId = this._fileElements.length;
+ this._fileElements[uploadId] = [];
+
+ for (var i = 0, length = files.length; i < length; i++) {
+ var file = files[i];
+ var fileElement = this._createFileElement(file);
+
+ if (!fileElement.classList.contains('uploadFailed')) {
+ elData(fileElement, 'filename', file.name);
+ elData(fileElement, 'internal-file-id', this._internalFileId++);
+ this._fileElements[uploadId][i] = fileElement;
+ }
+ }
+
+ DomChangeListener.trigger();
+
+ return uploadId;
+ }
+
+ return null;
+ },
+
+ _createFileTableRow: function(file) {
+ throw new Error("Has to be implemented in subclass.");
+ },
+
+ /**
+ * Handles a failed file upload.
+ *
+ * @param {int} uploadId identifier of a file upload
+ * @param {object<string, *>} data response data
+ * @param {string} responseText response
+ * @param {XMLHttpRequest} xhr request object
+ * @param {object<string, *>} requestOptions options used to send AJAX request
+ * @return {boolean} true if the error message should be shown
+ */
+ _failure: function(uploadId, data, responseText, xhr, requestOptions) {
+ // does nothing
+ return true;
+ },
+
+ /**
+ * Return additional parameters for upload requests.
+ *
+ * @return {object<string, *>} additional parameters
+ */
+ _getParameters: function() {
+ return {};
+ },
+
+ /**
+ * Return additional form data for upload requests.
+ *
+ * @return {object<string, *>} additional form data
+ * @since 5.2
+ */
+ _getFormData: function() {
+ return {};
+ },
+
+ /**
+ * Inserts the created button to upload files into the button container.
+ */
+ _insertButton: function() {
+ DomUtil.prepend(this._button, this._buttonContainer);
+ },
+
+ /**
+ * Updates the progress of an upload.
+ *
+ * @param {int} uploadId internal upload identifier
+ * @param {XMLHttpRequestProgressEvent} event progress event object
+ */
+ _progress: function(uploadId, event) {
+ var percentComplete = Math.round(event.loaded / event.total * 100);
+
+ for (var i in this._fileElements[uploadId]) {
+ var progress = elByTag('PROGRESS', this._fileElements[uploadId][i]);
+ if (progress.length === 1) {
+ elAttr(progress[0], 'value', percentComplete);
+ }
+ }
+ },
+
+ /**
+ * Removes the button to upload files.
+ */
+ _removeButton: function() {
+ elRemove(this._button);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Handles a successful file upload.
+ *
+ * @param {int} uploadId identifier of a file upload
+ * @param {object<string, *>} data response data
+ * @param {string} responseText response
+ * @param {XMLHttpRequest} xhr request object
+ * @param {object<string, *>} requestOptions options used to send AJAX request
+ */
+ _success: function(uploadId, data, responseText, xhr, requestOptions) {
+ // does nothing
+ },
+
+ /**
+ * File input change callback to upload files.
+ *
+ * @param {Event} event input change event object
+ * @param {File} file uploaded file
+ * @param {Blob} blob file blob
+ * @return {(int|Array.<int>|null)} identifier(s) for the uploaded files
+ */
+ _upload: function(event, file, blob) {
+ // remove failed upload elements first
+ var failedUploads = DomTraverse.childrenByClass(this._target, 'uploadFailed');
+ for (var i = 0, length = failedUploads.length; i < length; i++) {
+ elRemove(failedUploads[i]);
+ }
+
+ var uploadId = null;
+
+ var files = [];
+ if (file) {
+ files.push(file);
+ }
+ else if (blob) {
+ var fileExtension = '';
+ switch (blob.type) {
+ case 'image/jpeg':
+ fileExtension = '.jpg';
+ break;
+
+ case 'image/gif':
+ fileExtension = '.gif';
+ break;
+
+ case 'image/png':
+ fileExtension = '.png';
+ break;
+ }
+
+ files.push({
+ name: 'pasted-from-clipboard' + fileExtension
+ });
+ }
+ else {
+ files = this._fileUpload.files;
+ }
+
+ if (files.length && this.validateUpload(files)) {
+ if (this._options.singleFileRequests) {
+ uploadId = [];
+ for (var i = 0, length = files.length; i < length; i++) {
+ var localUploadId = this._uploadFiles([ files[i] ], blob);
+
+ if (files.length !== 1) {
+ this._multiFileUploadIds.push(localUploadId)
+ }
+ uploadId.push(localUploadId);
+ }
+ }
+ else {
+ uploadId = this._uploadFiles(files, blob);
+ }
+ }
+
+ // re-create upload button to effectively reset the 'files'
+ // property of the input element
+ this._removeButton();
+ this._createButton();
+
+ return uploadId;
+ },
+
+ /**
+ * Validates the upload before uploading them.
+ *
+ * @param {(FileList|Array.<File>)} files uploaded files
+ * @return {boolean}
+ * @since 5.2
+ */
+ validateUpload: function(files) {
+ return true;
+ },
+
+ /**
+ * Sends the request to upload files.
+ *
+ * @param {(FileList|Array.<File>)} files uploaded files
+ * @param {Blob} blob file blob
+ * @return {(int|null)} identifier for the uploaded files
+ */
+ _uploadFiles: function(files, blob) {
+ var uploadId = this._createFileElements(files);
+
+ // no more files left, abort
+ if (!this._fileElements[uploadId].length) {
+ return null;
+ }
+
+ var formData = new FormData();
+ for (var i = 0, length = files.length; i < length; i++) {
+ if (this._fileElements[uploadId][i]) {
+ var internalFileId = elData(this._fileElements[uploadId][i], 'internal-file-id');
+
+ if (blob) {
+ formData.append('__files[' + internalFileId + ']', blob, files[i].name);
+ }
+ else {
+ formData.append('__files[' + internalFileId + ']', files[i]);
+ }
+ }
+ }
+
+ formData.append('actionName', this._options.action);
+ formData.append('className', this._options.className);
+ if (this._options.action === 'upload') {
+ formData.append('interfaceName', 'wcf\\data\\IUploadAction');
+ }
+
+ // recursively append additional parameters to form data
+ var appendFormData = function(parameters, prefix) {
+ prefix = prefix || '';
+
+ for (var name in parameters) {
+ if (typeof parameters[name] === 'object') {
+ var newPrefix = prefix.length === 0 ? name : prefix + '[' + name + ']';
+ appendFormData(parameters[name], newPrefix);
+ }
+ else {
+ var dataName = prefix.length === 0 ? name : prefix + '[' + name + ']';
+ formData.append(dataName, parameters[name]);
+ }
+ }
+ };
+
+ appendFormData(this._getParameters(), 'parameters');
+ appendFormData(this._getFormData());
+
+ var request = new AjaxRequest({
+ data: formData,
+ contentType: false,
+ failure: this._failure.bind(this, uploadId),
+ silent: true,
+ success: this._success.bind(this, uploadId),
+ uploadProgress: this._progress.bind(this, uploadId),
+ url: this._options.url,
+ withCredentials: true
+ });
+ request.sendRequest();
+
+ return uploadId;
+ },
+
+ /**
+ * Returns true if there are any pending uploads handled by this
+ * upload manager.
+ *
+ * @return {boolean}
+ * @since 5.2
+ */
+ hasPendingUploads: function() {
+ for (var uploadId in this._fileElements) {
+ for (var i in this._fileElements[uploadId]) {
+ var progress = elByTag('PROGRESS', this._fileElements[uploadId][i]);
+ if (progress.length === 1) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Uploads the given file blob.
+ *
+ * @param {Blob} blob file blob
+ * @return {int} identifier for the uploaded file
+ */
+ uploadBlob: function(blob) {
+ return this._upload(null, null, blob);
+ },
+
+ /**
+ * Uploads the given file.
+ *
+ * @param {File} file uploaded file
+ * @return {int} identifier(s) for the uploaded file
+ */
+ uploadFile: function(file) {
+ return this._upload(null, file);
+ }
+ };
+
+ return Upload;
+});
+
+/**
+ * Provides data of the active user.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/User
+ */
+define('WoltLabSuite/Core/User',[], function() {
+ "use strict";
+
+ var _didInit = false;
+ var _link;
+
+ /**
+ * @exports WoltLabSuite/Core/User
+ */
+ return {
+ /**
+ * Returns the link to the active user's profile or an empty string
+ * if the active user is a guest.
+ *
+ * @return {string}
+ */
+ getLink: function() {
+ return _link;
+ },
+
+ /**
+ * Initializes the user object.
+ *
+ * @param {int} userId id of the user, `0` for guests
+ * @param {string} username name of the user, empty for guests
+ * @param {string} userLink link to the user's profile, empty for guests
+ */
+ init: function(userId, username, userLink) {
+ if (_didInit) {
+ throw new Error('User has already been initialized.');
+ }
+
+ // define non-writeable properties for userId and username
+ Object.defineProperty(this, 'userId', {
+ value: userId,
+ writable: false
+ });
+ Object.defineProperty(this, 'username', {
+ value: username,
+ writable: false
+ });
+
+ _link = userLink;
+
+ _didInit = true;
+ }
+ };
+});
+
+/**
+ * Provides a utility class to issue JSONP requests.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax/Jsonp
+ */
+define('WoltLabSuite/Core/Ajax/Jsonp',['Core'], function(Core) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Ajax/Jsonp
+ */
+ return {
+ /**
+ * Issues a JSONP request.
+ *
+ * @param {string} url source URL, must not contain callback parameter
+ * @param {function} success success callback
+ * @param {function=} failure timeout callback
+ * @param {object<string, *>=} options request options
+ */
+ send: function(url, success, failure, options) {
+ url = (typeof url === 'string') ? url.trim() : '';
+ if (url.length === 0) {
+ throw new Error("Expected a non-empty string for parameter 'url'.");
+ }
+
+ if (typeof success !== 'function') {
+ throw new TypeError("Expected a valid callback function for parameter 'success'.");
+ }
+
+ options = Core.extend({
+ parameterName: 'callback',
+ timeout: 10
+ }, options || {});
+
+ var callbackName = 'wcf_jsonp_' + Core.getUuid().replace(/-/g, '').substr(0, 8);
+ var script;
+
+ var timeout = window.setTimeout(function() {
+ if (typeof failure === 'function') {
+ failure();
+ }
+
+ window[callbackName] = undefined;
+ elRemove(script);
+ }, (~~options.timeout || 10) * 1000);
+
+ window[callbackName] = function() {
+ window.clearTimeout(timeout);
+
+ success.apply(null, arguments);
+
+ window[callbackName] = undefined;
+ elRemove(script);
+ };
+
+ url += (url.indexOf('?') === -1) ? '?' : '&';
+ url += options.parameterName + '=' + callbackName;
+
+ script = elCreate('script');
+ script.async = true;
+ elAttr(script, 'src', url);
+
+ document.head.appendChild(script);
+ }
+ };
+});
+
+/**
+ * Simple notification overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Notification
+ */
+define('WoltLabSuite/Core/Ui/Notification',['Language'], function(Language) {
+ "use strict";
+
+ var _busy = false;
+ var _callback = null;
+ var _message = null;
+ var _notificationElement = null;
+ var _timeout = null;
+
+ var _callbackHide = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Notification
+ */
+ var UiNotification = {
+ /**
+ * Shows a notification.
+ *
+ * @param {string} message message
+ * @param {function=} callback callback function to be executed once notification is being hidden
+ * @param {string=} cssClassName alternate CSS class name, defaults to 'success'
+ */
+ show: function(message, callback, cssClassName) {
+ if (_busy) {
+ return;
+ }
+
+ this._init();
+
+ _callback = (typeof callback === 'function') ? callback : null;
+ _message.className = cssClassName || 'success';
+ _message.textContent = Language.get(message || 'wcf.global.success');
+
+ _busy = true;
+
+ _notificationElement.classList.add('active');
+
+ _timeout = setTimeout(_callbackHide, 2000);
+ },
+
+ /**
+ * Initializes the UI elements.
+ */
+ _init: function() {
+ if (_notificationElement === null) {
+ _callbackHide = this._hide.bind(this);
+
+ _notificationElement = elCreate('div');
+ _notificationElement.id = 'systemNotification';
+
+ _message = elCreate('p');
+ _message.addEventListener(WCF_CLICK_EVENT, _callbackHide);
+ _notificationElement.appendChild(_message);
+
+ document.body.appendChild(_notificationElement);
+ }
+ },
+
+ /**
+ * Hides the notification and invokes the callback if provided.
+ */
+ _hide: function() {
+ clearTimeout(_timeout);
+
+ _notificationElement.classList.remove('active');
+
+ if (_callback !== null) {
+ _callback();
+ }
+
+ _busy = false;
+ }
+ };
+
+ return UiNotification;
+});
+
+define('prism/prism-meta',[],function(){return /*START*/{"markup":{"title":"Markup","file":"markup"},"html":{"title":"HTML","file":"markup"},"xml":{"title":"XML","file":"markup"},"svg":{"title":"SVG","file":"markup"},"mathml":{"title":"MathML","file":"markup"},"css":{"title":"CSS","file":"css"},"clike":{"title":"C-like","file":"clike"},"javascript":{"title":"JavaScript","file":"javascript"},"abap":{"title":"ABAP","file":"abap"},"actionscript":{"title":"ActionScript","file":"actionscript"},"ada":{"title":"Ada","file":"ada"},"apacheconf":{"title":"Apache Configuration","file":"apacheconf"},"apl":{"title":"APL","file":"apl"},"applescript":{"title":"AppleScript","file":"applescript"},"arduino":{"title":"Arduino","file":"arduino"},"arff":{"title":"ARFF","file":"arff"},"asciidoc":{"title":"AsciiDoc","file":"asciidoc"},"asm6502":{"title":"6502 Assembly","file":"asm6502"},"aspnet":{"title":"ASP.NET (C#)","file":"aspnet"},"autohotkey":{"title":"AutoHotkey","file":"autohotkey"},"autoit":{"title":"AutoIt","file":"autoit"},"bash":{"title":"Bash","file":"bash"},"basic":{"title":"BASIC","file":"basic"},"batch":{"title":"Batch","file":"batch"},"bison":{"title":"Bison","file":"bison"},"brainfuck":{"title":"Brainfuck","file":"brainfuck"},"bro":{"title":"Bro","file":"bro"},"c":{"title":"C","file":"c"},"csharp":{"title":"C#","file":"csharp"},"cpp":{"title":"C++","file":"cpp"},"coffeescript":{"title":"CoffeeScript","file":"coffeescript"},"clojure":{"title":"Clojure","file":"clojure"},"crystal":{"title":"Crystal","file":"crystal"},"csp":{"title":"Content-Security-Policy","file":"csp"},"css-extras":{"title":"CSS Extras","file":"css-extras"},"d":{"title":"D","file":"d"},"dart":{"title":"Dart","file":"dart"},"diff":{"title":"Diff","file":"diff"},"django":{"title":"Django/Jinja2","file":"django"},"docker":{"title":"Docker","file":"docker"},"eiffel":{"title":"Eiffel","file":"eiffel"},"elixir":{"title":"Elixir","file":"elixir"},"elm":{"title":"Elm","file":"elm"},"erb":{"title":"ERB","file":"erb"},"erlang":{"title":"Erlang","file":"erlang"},"fsharp":{"title":"F#","file":"fsharp"},"flow":{"title":"Flow","file":"flow"},"fortran":{"title":"Fortran","file":"fortran"},"gedcom":{"title":"GEDCOM","file":"gedcom"},"gherkin":{"title":"Gherkin","file":"gherkin"},"git":{"title":"Git","file":"git"},"glsl":{"title":"GLSL","file":"glsl"},"gml":{"title":"GameMaker Language","file":"gml"},"go":{"title":"Go","file":"go"},"graphql":{"title":"GraphQL","file":"graphql"},"groovy":{"title":"Groovy","file":"groovy"},"haml":{"title":"Haml","file":"haml"},"handlebars":{"title":"Handlebars","file":"handlebars"},"haskell":{"title":"Haskell","file":"haskell"},"haxe":{"title":"Haxe","file":"haxe"},"http":{"title":"HTTP","file":"http"},"hpkp":{"title":"HTTP Public-Key-Pins","file":"hpkp"},"hsts":{"title":"HTTP Strict-Transport-Security","file":"hsts"},"ichigojam":{"title":"IchigoJam","file":"ichigojam"},"icon":{"title":"Icon","file":"icon"},"inform7":{"title":"Inform 7","file":"inform7"},"ini":{"title":"Ini","file":"ini"},"io":{"title":"Io","file":"io"},"j":{"title":"J","file":"j"},"java":{"title":"Java","file":"java"},"jolie":{"title":"Jolie","file":"jolie"},"json":{"title":"JSON","file":"json"},"julia":{"title":"Julia","file":"julia"},"keyman":{"title":"Keyman","file":"keyman"},"kotlin":{"title":"Kotlin","file":"kotlin"},"latex":{"title":"LaTeX","file":"latex"},"less":{"title":"Less","file":"less"},"liquid":{"title":"Liquid","file":"liquid"},"lisp":{"title":"Lisp","file":"lisp"},"livescript":{"title":"LiveScript","file":"livescript"},"lolcode":{"title":"LOLCODE","file":"lolcode"},"lua":{"title":"Lua","file":"lua"},"makefile":{"title":"Makefile","file":"makefile"},"markdown":{"title":"Markdown","file":"markdown"},"markup-templating":{"title":"Markup templating","file":"markup-templating"},"matlab":{"title":"MATLAB","file":"matlab"},"mel":{"title":"MEL","file":"mel"},"mizar":{"title":"Mizar","file":"mizar"},"monkey":{"title":"Monkey","file":"monkey"},"n4js":{"title":"N4JS","file":"n4js"},"nasm":{"title":"NASM","file":"nasm"},"nginx":{"title":"nginx","file":"nginx"},"nim":{"title":"Nim","file":"nim"},"nix":{"title":"Nix","file":"nix"},"nsis":{"title":"NSIS","file":"nsis"},"objectivec":{"title":"Objective-C","file":"objectivec"},"ocaml":{"title":"OCaml","file":"ocaml"},"opencl":{"title":"OpenCL","file":"opencl"},"oz":{"title":"Oz","file":"oz"},"parigp":{"title":"PARI/GP","file":"parigp"},"parser":{"title":"Parser","file":"parser"},"pascal":{"title":"Pascal","file":"pascal"},"perl":{"title":"Perl","file":"perl"},"php":{"title":"PHP","file":"php"},"php-extras":{"title":"PHP Extras","file":"php-extras"},"plsql":{"title":"PL/SQL","file":"plsql"},"powershell":{"title":"PowerShell","file":"powershell"},"processing":{"title":"Processing","file":"processing"},"prolog":{"title":"Prolog","file":"prolog"},"properties":{"title":".properties","file":"properties"},"protobuf":{"title":"Protocol Buffers","file":"protobuf"},"pug":{"title":"Pug","file":"pug"},"puppet":{"title":"Puppet","file":"puppet"},"pure":{"title":"Pure","file":"pure"},"python":{"title":"Python","file":"python"},"q":{"title":"Q (kdb+ database)","file":"q"},"qore":{"title":"Qore","file":"qore"},"r":{"title":"R","file":"r"},"jsx":{"title":"React JSX","file":"jsx"},"tsx":{"title":"React TSX","file":"tsx"},"renpy":{"title":"Ren'py","file":"renpy"},"reason":{"title":"Reason","file":"reason"},"rest":{"title":"reST (reStructuredText)","file":"rest"},"rip":{"title":"Rip","file":"rip"},"roboconf":{"title":"Roboconf","file":"roboconf"},"ruby":{"title":"Ruby","file":"ruby"},"rust":{"title":"Rust","file":"rust"},"sas":{"title":"SAS","file":"sas"},"sass":{"title":"Sass (Sass)","file":"sass"},"scss":{"title":"Sass (Scss)","file":"scss"},"scala":{"title":"Scala","file":"scala"},"scheme":{"title":"Scheme","file":"scheme"},"smalltalk":{"title":"Smalltalk","file":"smalltalk"},"smarty":{"title":"Smarty","file":"smarty"},"sql":{"title":"SQL","file":"sql"},"soy":{"title":"Soy (Closure Template)","file":"soy"},"stylus":{"title":"Stylus","file":"stylus"},"swift":{"title":"Swift","file":"swift"},"tap":{"title":"TAP","file":"tap"},"tcl":{"title":"Tcl","file":"tcl"},"textile":{"title":"Textile","file":"textile"},"tt2":{"title":"Template Toolkit 2","file":"tt2"},"twig":{"title":"Twig","file":"twig"},"typescript":{"title":"TypeScript","file":"typescript"},"vbnet":{"title":"VB.Net","file":"vbnet"},"velocity":{"title":"Velocity","file":"velocity"},"verilog":{"title":"Verilog","file":"verilog"},"vhdl":{"title":"VHDL","file":"vhdl"},"vim":{"title":"vim","file":"vim"},"visual-basic":{"title":"Visual Basic","file":"visual-basic"},"wasm":{"title":"WebAssembly","file":"wasm"},"wiki":{"title":"Wiki markup","file":"wiki"},"xeora":{"title":"Xeora","file":"xeora"},"xojo":{"title":"Xojo (REALbasic)","file":"xojo"},"xquery":{"title":"XQuery","file":"xquery"},"yaml":{"title":"YAML","file":"yaml"}}/*END*/;});
+/**
+ * Highlights code in the Code bbcode.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Bbcode/Code
+ */
+define('WoltLabSuite/Core/Bbcode/Code',[
+ 'Language', 'WoltLabSuite/Core/Ui/Notification', 'WoltLabSuite/Core/Clipboard', 'WoltLabSuite/Core/Prism', 'prism/prism-meta'
+ ],
+ function(
+ Language, UiNotification, Clipboard, Prism, PrismMeta
+ )
+{
+ "use strict";
+
+ /** @const */ var CHUNK_SIZE = 50;
+
+ // Define idleify() for piecewiese highlighting to not block the UI thread.
+ var idleify = function (callback) {
+ return function () {
+ var args = arguments;
+ return new Promise(function (resolve, reject) {
+ var body = function () {
+ try {
+ resolve(callback.apply(null, args));
+ }
+ catch (e) {
+ reject(e);
+ }
+ };
+
+ if (window.requestIdleCallback) {
+ window.requestIdleCallback(body, { timeout: 5000 });
+ }
+ else {
+ setTimeout(body, 0);
+ }
+ });
+ };
+ };
+
+ /**
+ * @constructor
+ */
+ function Code(container) {
+ var matches;
+
+ this.container = container;
+ this.codeContainer = elBySel('.codeBoxCode > code', this.container);
+ this.language = null;
+ for (var i = 0; i < this.codeContainer.classList.length; i++) {
+ if ((matches = this.codeContainer.classList[i].match(/language-(.*)/))) {
+ this.language = matches[1];
+ }
+ }
+ }
+ Code.processAll = function () {
+ elBySelAll('.codeBox:not([data-processed])', document, function (codeBox) {
+ elData(codeBox, 'processed', '1');
+
+ var handle = new Code(codeBox);
+ if (handle.language) handle.highlight();
+ handle.createCopyButton();
+ })
+ };
+ Code.prototype = {
+ createCopyButton: function () {
+ var header = elBySel('.codeBoxHeader', this.container);
+ var button = elCreate('span');
+ button.className = 'icon icon24 fa-files-o pointer jsTooltip';
+ button.setAttribute('title', Language.get('wcf.message.bbcode.code.copy'));
+ button.addEventListener('click', function () {
+ Clipboard.copyElementTextToClipboard(this.codeContainer).then(function () {
+ UiNotification.show(Language.get('wcf.message.bbcode.code.copy.success'));
+ });
+ }.bind(this));
+
+ header.appendChild(button);
+ },
+ highlight: function () {
+ if (!this.language) {
+ return Promise.reject(new Error('No language detected'));
+ }
+ if (!PrismMeta[this.language]) {
+ return Promise.reject(new Error('Unknown language ' + this.language));
+ }
+
+ this.container.classList.add('highlighting');
+
+ return require(['prism/components/prism-' + PrismMeta[this.language].file])
+ .then(idleify(function () {
+ var grammar = Prism.languages[this.language];
+ if (!grammar) {
+ throw new Error('Invalid language ' + language + ' given.');
+ }
+
+ var container = elCreate('div');
+ container.innerHTML = Prism.highlight(this.codeContainer.textContent, grammar, this.language);
+ return container;
+ }.bind(this)))
+ .then(idleify(function (container) {
+ var highlighted = Prism.wscSplitIntoLines(container);
+ var highlightedLines = elBySelAll('[data-number]', highlighted);
+ var originalLines = elBySelAll('.codeBoxLine > span', this.codeContainer);
+
+ if (highlightedLines.length !== originalLines.length) {
+ throw new Error('Unreachable');
+ }
+
+ var promises = [];
+ for (var chunkStart = 0, max = highlightedLines.length; chunkStart < max; chunkStart += CHUNK_SIZE) {
+ promises.push(idleify(function (chunkStart) {
+ var chunkEnd = Math.min(chunkStart + CHUNK_SIZE, max);
+
+ for (var offset = chunkStart; offset < chunkEnd; offset++) {
+ originalLines[offset].parentNode.replaceChild(highlightedLines[offset], originalLines[offset]);
+ }
+ })(chunkStart));
+ }
+ return Promise.all(promises);
+ }.bind(this)))
+ .then(function () {
+ this.container.classList.remove('highlighting');
+ this.container.classList.add('highlighted');
+ }.bind(this))
+ }
+ }
+
+ return Code;
+});
+
+/**
+ * Generic handler for collapsible bbcode boxes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Bbcode/Collapsible
+ */
+define('WoltLabSuite/Core/Bbcode/Collapsible',[], function() {
+ "use strict";
+
+ var _containers = elByClass('jsCollapsibleBbcode');
+
+ /**
+ * @exports WoltLabSuite/Core/Bbcode/Collapsible
+ */
+ return {
+ observe: function() {
+ var container, toggleButton;
+ while (_containers.length) {
+ container = _containers[0];
+
+ // find the matching toggle button
+ toggleButton = null;
+ elBySelAll('.toggleButton:not(.jsToggleButtonEnabled)', container, function (button) {
+ //noinspection JSReferencingMutableVariableFromClosure
+ if (button.closest('.jsCollapsibleBbcode') === container) {
+ toggleButton = button;
+ }
+ });
+
+ if (toggleButton) {
+ (function (container, toggleButton) {
+ var toggle = function (event) {
+ if (container.classList.toggle('collapsed')) {
+ toggleButton.textContent = elData(toggleButton, 'title-expand');
+
+ if (event instanceof Event) {
+ // negative top value means the upper boundary is not within the viewport
+ var top = container.getBoundingClientRect().top;
+ if (top < 0) {
+ var y = window.pageYOffset + (top - 100);
+ if (y < 0) y = 0;
+ window.scrollTo(window.pageXOffset, y);
+ }
+ }
+ }
+ else {
+ toggleButton.textContent = elData(toggleButton, 'title-collapse');
+ }
+ };
+
+ toggleButton.classList.add('jsToggleButtonEnabled');
+ toggleButton.addEventListener(WCF_CLICK_EVENT, toggle);
+
+ // expand boxes that are initially scrolled
+ if (container.scrollTop !== 0) {
+ toggle();
+ }
+ container.addEventListener('scroll', function () {
+ if (container.classList.contains('collapsed')) {
+ toggle();
+ }
+ });
+ })(container, toggleButton);
+ }
+
+ container.classList.remove('jsCollapsibleBbcode');
+ }
+ }
+ };
+});
+
+/**
+ * Provides data of the active user.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Captcha
+ */
+define('WoltLabSuite/Core/Controller/Captcha',['Dictionary'], function(Dictionary) {
+ "use strict";
+
+ var _captchas = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Captcha
+ */
+ return {
+ /**
+ * Registers a captcha with the given identifier and callback used to get captcha data.
+ *
+ * @param {string} captchaId captcha identifier
+ * @param {function} callback callback to get captcha data
+ */
+ add: function(captchaId, callback) {
+ if (_captchas.has(captchaId)) {
+ throw new Error("Captcha with id '" + captchaId + "' is already registered.");
+ }
+
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback for parameter 'callback'.");
+ }
+
+ _captchas.set(captchaId, callback);
+ },
+
+ /**
+ * Deletes the captcha with the given identifier.
+ *
+ * @param {string} captchaId identifier of the captcha to be deleted
+ */
+ 'delete': function(captchaId) {
+ if (!_captchas.has(captchaId)) {
+ throw new Error("Unknown captcha with id '" + captchaId + "'.");
+ }
+
+ _captchas.delete(captchaId);
+ },
+
+ /**
+ * Returns true if a captcha with the given identifier exists.
+ *
+ * @param {string} captchaId captcha identifier
+ * @return {boolean}
+ */
+ has: function(captchaId) {
+ return _captchas.has(captchaId);
+ },
+
+ /**
+ * Returns the data of the captcha with the given identifier.
+ *
+ * @param {string} captchaId captcha identifier
+ * @return {Object} captcha data
+ */
+ getData: function(captchaId) {
+ if (!_captchas.has(captchaId)) {
+ throw new Error("Unknown captcha with id '" + captchaId + "'.");
+ }
+
+ return _captchas.get(captchaId)();
+ }
+ };
+});
+
+/**
+ * Clipboard API Handler.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Clipboard
+ */
+define(
+ 'WoltLabSuite/Core/Controller/Clipboard',[
+ 'Ajax', 'Core', 'Dictionary', 'EventHandler',
+ 'Language', 'List', 'ObjectMap', 'Dom/ChangeListener',
+ 'Dom/Traverse', 'Dom/Util', 'Ui/Confirmation', 'Ui/SimpleDropdown',
+ 'WoltLabSuite/Core/Ui/Page/Action', 'Ui/Screen'
+ ],
+ function(
+ Ajax, Core, Dictionary, EventHandler,
+ Language, List, ObjectMap, DomChangeListener,
+ DomTraverse, DomUtil, UiConfirmation, UiSimpleDropdown,
+ UiPageAction, UiScreen
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ return {
+ setup: function() {},
+ reload: function() {},
+ _initContainers: function() {},
+ _loadMarkedItems: function() {},
+ _markAll: function() {},
+ _mark: function() {},
+ _saveState: function() {},
+ _executeAction: function() {},
+ _executeProxyAction: function() {},
+ _unmarkAll: function() {},
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _rebuildMarkings: function() {},
+ hideEditor: function() {},
+ showEditor: function() {},
+ unmark: function() {}
+ };
+ }
+
+ var _containers = new Dictionary();
+ var _editors = new Dictionary();
+ var _editorDropdowns = new Dictionary();
+ var _elements = elByClass('jsClipboardContainer');
+ var _itemData = new ObjectMap();
+ var _knownCheckboxes = new List();
+ var _options = {};
+ var _reloadPageOnSuccess = new Dictionary();
+
+ var _callbackCheckbox = null;
+ var _callbackItem = null;
+ var _callbackUnmarkAll = null;
+
+ var _specialCheckboxSelector = '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]';
+
+ /**
+ * Clipboard API
+ *
+ * @exports WoltLabSuite/Core/Controller/Clipboard
+ */
+ return {
+ /**
+ * Initializes the clipboard API handler.
+ *
+ * @param {Object} options initialization options
+ */
+ setup: function(options) {
+ if (!options.pageClassName) {
+ throw new Error("Expected a non-empty string for parameter 'pageClassName'.");
+ }
+
+ if (_callbackCheckbox === null) {
+ _callbackCheckbox = this._mark.bind(this);
+ _callbackItem = this._executeAction.bind(this);
+ _callbackUnmarkAll = this._unmarkAll.bind(this);
+
+ _options = Core.extend({
+ hasMarkedItems: false,
+ pageClassNames: [options.pageClassName],
+ pageObjectId: 0
+ }, options);
+
+ delete _options.pageClassName;
+ }
+ else {
+ if (options.pageObjectId) {
+ throw new Error("Cannot load secondary clipboard with page object id set.");
+ }
+
+ _options.pageClassNames.push(options.pageClassName);
+ }
+
+ if (!Element.prototype.matches) {
+ Element.prototype.matches = Element.prototype.msMatchesSelector;
+ }
+
+ this._initContainers();
+
+ if (_options.hasMarkedItems && _elements.length) {
+ this._loadMarkedItems();
+ }
+
+ DomChangeListener.add('WoltLabSuite/Core/Controller/Clipboard', this._initContainers.bind(this));
+ },
+
+ /**
+ * Reloads the clipboard data.
+ */
+ reload: function() {
+ if (_containers.size) {
+ this._loadMarkedItems();
+ }
+ },
+
+ /**
+ * Initializes clipboard containers.
+ */
+ _initContainers: function() {
+ for (var i = 0, length = _elements.length; i < length; i++) {
+ var container = _elements[i];
+ var containerId = DomUtil.identify(container);
+ var containerData = _containers.get(containerId);
+
+ if (containerData === undefined) {
+ var markAll = elBySel('.jsClipboardMarkAll', container);
+
+ if (markAll !== null) {
+ if (markAll.matches(_specialCheckboxSelector)) {
+ var label = markAll.closest('label');
+ elAttr(label, 'role', 'checkbox');
+ elAttr(label, 'tabindex', '0');
+ elAttr(label, 'aria-checked', false);
+ elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.markAll'));
+
+ label.addEventListener('keyup', function (event) {
+ if (event.keyCode === 13 || event.keyCode === 32) {
+ checkbox.click();
+ }
+ });
+ }
+
+ elData(markAll, 'container-id', containerId);
+ markAll.addEventListener(WCF_CLICK_EVENT, this._markAll.bind(this));
+ }
+
+ containerData = {
+ checkboxes: elByClass('jsClipboardItem', container),
+ element: container,
+ markAll: markAll,
+ markedObjectIds: new List()
+ };
+ _containers.set(containerId, containerData);
+ }
+
+ for (var j = 0, innerLength = containerData.checkboxes.length; j < innerLength; j++) {
+ var checkbox = containerData.checkboxes[j];
+
+ if (!_knownCheckboxes.has(checkbox)) {
+ elData(checkbox, 'container-id', containerId);
+
+ (function(checkbox) {
+ if (checkbox.matches(_specialCheckboxSelector)) {
+ var label = checkbox.closest('label');
+ elAttr(label, 'role', 'checkbox');
+ elAttr(label, 'tabindex', '0');
+ elAttr(label, 'aria-checked', false);
+ elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.mark'));
+
+ label.addEventListener('keyup', function (event) {
+ if (event.keyCode === 13 || event.keyCode === 32) {
+ checkbox.click();
+ }
+ });
+ }
+
+ var link = checkbox.closest('a');
+ if (link === null) {
+ checkbox.addEventListener(WCF_CLICK_EVENT, _callbackCheckbox);
+ }
+ else {
+ // Firefox will always trigger the link if the checkbox is
+ // inside of one. Since 2000. Thanks Firefox.
+ checkbox.addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+
+ window.setTimeout(function () {
+ checkbox.checked = !checkbox.checked;
+
+ _callbackCheckbox(null, checkbox);
+ }, 10);
+ });
+ }
+ })(checkbox);
+
+ _knownCheckboxes.add(checkbox);
+ }
+ }
+ }
+ },
+
+ /**
+ * Loads marked items from clipboard.
+ */
+ _loadMarkedItems: function() {
+ Ajax.api(this, {
+ actionName: 'getMarkedItems',
+ parameters: {
+ pageClassNames: _options.pageClassNames,
+ pageObjectID: _options.pageObjectId
+ }
+ });
+ },
+
+ /**
+ * Marks or unmarks all visible items at once.
+ *
+ * @param {object} event event object
+ */
+ _markAll: function(event) {
+ var checkbox = event.currentTarget;
+ var isMarked = (checkbox.nodeName !== 'INPUT' || checkbox.checked);
+
+ if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+ elAttr(checkbox.parentNode, 'aria-checked', isMarked);
+ }
+
+ var objectIds = [];
+
+ var containerId = elData(checkbox, 'container-id');
+ var data = _containers.get(containerId);
+ var type = elData(data.element, 'type');
+
+ for (var i = 0, length = data.checkboxes.length; i < length; i++) {
+ var item = data.checkboxes[i];
+ var objectId = ~~elData(item, 'object-id');
+
+ if (isMarked) {
+ if (!item.checked) {
+ item.checked = true;
+
+ data.markedObjectIds.add(objectId);
+ objectIds.push(objectId);
+ }
+ }
+ else {
+ if (item.checked) {
+ item.checked = false;
+
+ data.markedObjectIds['delete'](objectId);
+ objectIds.push(objectId);
+ }
+ }
+
+ if (elAttr(item.parentNode, 'role') === 'checkbox') {
+ elAttr(item.parentNode, 'aria-checked', isMarked);
+ }
+
+ var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
+ if (clipboardObject !== null) {
+ clipboardObject.classList[(isMarked ? 'addClass' : 'removeClass')]('jsMarked');
+ }
+ }
+
+ this._saveState(type, objectIds, isMarked);
+ },
+
+ /**
+ * Marks or unmarks an individual item.
+ *
+ * @param {object} event event object
+ * @param {Element=} checkbox checkbox element
+ */
+ _mark: function(event, checkbox) {
+ checkbox = (event instanceof Event) ? event.currentTarget : checkbox;
+ var objectId = ~~elData(checkbox, 'object-id');
+ var isMarked = checkbox.checked;
+ var containerId = elData(checkbox, 'container-id');
+ var data = _containers.get(containerId);
+ var type = elData(data.element, 'type');
+
+ var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
+ data.markedObjectIds[(isMarked ? 'add' : 'delete')](objectId);
+ clipboardObject.classList[(isMarked) ? 'add' : 'remove']('jsMarked');
+
+ if (data.markAll !== null) {
+ var markedAll = true;
+ for (var i = 0, length = data.checkboxes.length; i < length; i++) {
+ if (!data.checkboxes[i].checked) {
+ markedAll = false;
+
+ break;
+ }
+ }
+
+ data.markAll.checked = markedAll;
+
+ if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
+ elAttr(data.markAll.parentNode, 'aria-checked', isMarked);
+ }
+ }
+
+ if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+ elAttr(checkbox.parentNode, 'aria-checked', checkbox.checked);
+ }
+
+ this._saveState(type, [ objectId ], isMarked);
+ },
+
+ /**
+ * Saves the state for given item object ids.
+ *
+ * @param {string} type object type
+ * @param {int[]} objectIds item object ids
+ * @param {boolean} isMarked true if marked
+ */
+ _saveState: function(type, objectIds, isMarked) {
+ Ajax.api(this, {
+ actionName: (isMarked ? 'mark' : 'unmark'),
+ parameters: {
+ pageClassNames: _options.pageClassNames,
+ pageObjectID: _options.pageObjectId,
+ objectIDs: objectIds,
+ objectType: type
+ }
+ });
+ },
+
+ /**
+ * Executes an editor action.
+ *
+ * @param {object} event event object
+ */
+ _executeAction: function(event) {
+ var listItem = event.currentTarget;
+ var data = _itemData.get(listItem);
+
+ if (data.url) {
+ window.location.href = data.url;
+ return;
+ }
+
+ var triggerEvent = function() {
+ var type = elData(listItem, 'type');
+
+ EventHandler.fire('com.woltlab.wcf.clipboard', type, {
+ data: data,
+ listItem: listItem,
+ responseData: null
+ });
+ };
+
+ //noinspection JSUnresolvedVariable
+ var confirmMessage = (typeof data.internalData.confirmMessage === 'string') ? data.internalData.confirmMessage : '';
+ var fireEvent = true;
+
+ if (typeof data.parameters === 'object' && data.parameters.actionName && data.parameters.className) {
+ if (data.parameters.actionName === 'unmarkAll' || Array.isArray(data.parameters.objectIDs)) {
+ if (confirmMessage.length) {
+ //noinspection JSUnresolvedVariable
+ var template = (typeof data.internalData.template === 'string') ? data.internalData.template : '';
+
+ UiConfirmation.show({
+ confirm: (function() {
+ var formData = {};
+
+ if (template.length) {
+ var items = elBySelAll('input, select, textarea', UiConfirmation.getContentElement());
+ for (var i = 0, length = items.length; i < length; i++) {
+ var item = items[i];
+ var name = elAttr(item, 'name');
+
+ switch (item.nodeName) {
+ case 'INPUT':
+ if ((item.type !== "checkbox" && item.type !== "radio") || item.checked) {
+ formData[name] = elAttr(item, 'value');
+ }
+ break;
+
+ case 'SELECT':
+ formData[name] = item.value;
+ break;
+
+ case 'TEXTAREA':
+ formData[name] = item.value.trim();
+ break;
+ }
+ }
+ }
+
+ //noinspection JSUnresolvedFunction
+ this._executeProxyAction(listItem, data, formData);
+ }).bind(this),
+ message: confirmMessage,
+ template: template
+ });
+ }
+ else {
+ this._executeProxyAction(listItem, data);
+ }
+ }
+ }
+ else if (confirmMessage.length) {
+ fireEvent = false;
+
+ UiConfirmation.show({
+ confirm: triggerEvent,
+ message: confirmMessage
+ });
+ }
+
+ if (fireEvent) {
+ triggerEvent();
+ }
+ },
+
+ /**
+ * Forwards clipboard actions to an individual handler.
+ *
+ * @param {Element} listItem dropdown item element
+ * @param {Object} data action data
+ * @param {Object?} formData form data
+ */
+ _executeProxyAction: function(listItem, data, formData) {
+ formData = formData || {};
+
+ var objectIds = (data.parameters.actionName !== 'unmarkAll') ? data.parameters.objectIDs : [];
+ var parameters = { data: formData };
+
+ //noinspection JSUnresolvedVariable
+ if (typeof data.internalData.parameters === 'object') {
+ //noinspection JSUnresolvedVariable
+ for (var key in data.internalData.parameters) {
+ //noinspection JSUnresolvedVariable
+ if (data.internalData.parameters.hasOwnProperty(key)) {
+ //noinspection JSUnresolvedVariable
+ parameters[key] = data.internalData.parameters[key];
+ }
+ }
+ }
+
+ Ajax.api(this, {
+ actionName: data.parameters.actionName,
+ className: data.parameters.className,
+ objectIDs: objectIds,
+ parameters: parameters
+ }, (function(responseData) {
+ if (data.actionName !== 'unmarkAll') {
+ var type = elData(listItem, 'type');
+
+ EventHandler.fire('com.woltlab.wcf.clipboard', type, {
+ data: data,
+ listItem: listItem,
+ responseData: responseData
+ });
+
+ if (_reloadPageOnSuccess.has(type) && _reloadPageOnSuccess.get(type).indexOf(responseData.actionName) !== -1) {
+ window.location.reload();
+ return;
+ }
+ }
+
+ this._loadMarkedItems();
+ }).bind(this));
+ },
+
+ /**
+ * Unmarks all clipboard items for an object type.
+ *
+ * @param {object} event event object
+ */
+ _unmarkAll: function(event) {
+ var type = elData(event.currentTarget, 'type');
+
+ Ajax.api(this, {
+ actionName: 'unmarkAll',
+ parameters: {
+ objectType: type
+ }
+ });
+ },
+
+ /**
+ * Sets up ajax request object.
+ *
+ * @return {object} request options
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\clipboard\\item\\ClipboardItemAction'
+ }
+ };
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ if (data.actionName === 'unmarkAll') {
+ _containers.forEach((function(containerData) {
+ if (elData(containerData.element, 'type') === data.returnValues.objectType) {
+ var clipboardObjects = elByClass('jsMarked', containerData.element);
+ while (clipboardObjects.length) {
+ clipboardObjects[0].classList.remove('jsMarked');
+ }
+
+ if (containerData.markAll !== null) {
+ containerData.markAll.checked = false;
+
+ if (elAttr(containerData.markAll.parentNode, 'role') === 'checkbox') {
+ elAttr(containerData.markAll.parentNode, 'aria-checked', false);
+ }
+ }
+ for (var i = 0, length = containerData.checkboxes.length; i < length; i++) {
+ containerData.checkboxes[i].checked = false;
+
+ if (elAttr(containerData.checkboxes[i].parentNode, 'role') === 'checkbox') {
+ elAttr(containerData.checkboxes[i].parentNode, 'aria-checked', false);
+ }
+ }
+
+ UiPageAction.remove('wcfClipboard-' + data.returnValues.objectType);
+ }
+ }).bind(this));
+
+ return;
+ }
+
+ _itemData = new ObjectMap();
+ _reloadPageOnSuccess = new Dictionary();
+
+ // rebuild markings
+ _containers.forEach((function(containerData) {
+ var typeName = elData(containerData.element, 'type');
+
+ //noinspection JSUnresolvedVariable
+ var objectIds = (data.returnValues.markedItems && data.returnValues.markedItems.hasOwnProperty(typeName)) ? data.returnValues.markedItems[typeName] : [];
+ this._rebuildMarkings(containerData, objectIds);
+ }).bind(this));
+
+ var keepEditors = [], typeName;
+ if (data.returnValues && data.returnValues.items) {
+ for (typeName in data.returnValues.items) {
+ if (data.returnValues.items.hasOwnProperty(typeName)) {
+ keepEditors.push(typeName);
+ }
+ }
+ }
+
+ // clear editors
+ _editors.forEach(function(editor, typeName) {
+ if (keepEditors.indexOf(typeName) === -1) {
+ UiPageAction.remove('wcfClipboard-' + typeName);
+
+ _editorDropdowns.get(typeName).innerHTML = '';
+ }
+ });
+
+ // no items
+ if (!data.returnValues || !data.returnValues.items) {
+ return;
+ }
+
+ // rebuild editors
+ var actionName, created, dropdown, editor, typeData;
+ var divider, item, itemData, itemIndex, label, unmarkAll;
+ for (typeName in data.returnValues.items) {
+ if (!data.returnValues.items.hasOwnProperty(typeName)) {
+ continue;
+ }
+
+ typeData = data.returnValues.items[typeName];
+ //noinspection JSUnresolvedVariable
+ _reloadPageOnSuccess.set(typeName, typeData.reloadPageOnSuccess);
+ created = false;
+
+ editor = _editors.get(typeName);
+ dropdown = _editorDropdowns.get(typeName);
+ if (editor === undefined) {
+ created = true;
+
+ editor = elCreate('a');
+ editor.className = 'dropdownToggle';
+ editor.textContent = typeData.label;
+
+ _editors.set(typeName, editor);
+
+ dropdown = elCreate('ol');
+ dropdown.className = 'dropdownMenu';
+
+ _editorDropdowns.set(typeName, dropdown);
+ }
+ else {
+ editor.textContent = typeData.label;
+ dropdown.innerHTML = '';
+ }
+
+ // create editor items
+ for (itemIndex in typeData.items) {
+ if (!typeData.items.hasOwnProperty(itemIndex)) {
+ continue;
+ }
+
+ itemData = typeData.items[itemIndex];
+
+ item = elCreate('li');
+ label = elCreate('span');
+ label.textContent = itemData.label;
+ item.appendChild(label);
+ dropdown.appendChild(item);
+
+ elData(item, 'type', typeName);
+ item.addEventListener(WCF_CLICK_EVENT, _callbackItem);
+
+ _itemData.set(item, itemData);
+ }
+
+ divider = elCreate('li');
+ divider.classList.add('dropdownDivider');
+ dropdown.appendChild(divider);
+
+ // add 'unmark all'
+ unmarkAll = elCreate('li');
+ elData(unmarkAll, 'type', typeName);
+ label = elCreate('span');
+ label.textContent = Language.get('wcf.clipboard.item.unmarkAll');
+ unmarkAll.appendChild(label);
+ unmarkAll.addEventListener(WCF_CLICK_EVENT, _callbackUnmarkAll);
+ dropdown.appendChild(unmarkAll);
+
+ if (keepEditors.indexOf(typeName) !== -1) {
+ actionName = 'wcfClipboard-' + typeName;
+
+ if (UiPageAction.has(actionName)) {
+ UiPageAction.show(actionName);
+ }
+ else {
+ UiPageAction.add(actionName, editor);
+ }
+ }
+
+ if (created) {
+ editor.parentNode.classList.add('dropdown');
+ editor.parentNode.appendChild(dropdown);
+ UiSimpleDropdown.init(editor);
+ }
+ }
+ },
+
+ /**
+ * Rebuilds the mark state for each item.
+ *
+ * @param {Object} data container data
+ * @param {int[]} objectIds item object ids
+ */
+ _rebuildMarkings: function(data, objectIds) {
+ var markAll = true;
+
+ for (var i = 0, length = data.checkboxes.length; i < length; i++) {
+ var checkbox = data.checkboxes[i];
+ var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
+
+ var isMarked = (objectIds.indexOf(~~elData(checkbox, 'object-id')) !== -1);
+ if (!isMarked) markAll = false;
+
+ checkbox.checked = isMarked;
+ clipboardObject.classList[(isMarked ? 'add' : 'remove')]('jsMarked');
+
+ if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+ elAttr(checkbox.parentNode, 'aria-checked', isMarked);
+ }
+ }
+
+ if (data.markAll !== null) {
+ data.markAll.checked = markAll;
+
+ if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
+ elAttr(data.markAll.parentNode, 'aria-checked', markAll);
+ }
+
+ var parent = data.markAll;
+ while (parent = parent.parentNode) {
+ if (parent instanceof Element && parent.classList.contains('columnMark')) {
+ parent = parent.parentNode;
+ break;
+ }
+ }
+
+ if (parent) {
+ parent.classList[(markAll ? 'add' : 'remove')]('jsMarked');
+ }
+ }
+ },
+
+ /**
+ * Hides the clipboard editor for the given object type.
+ *
+ * @param {string} objectType
+ */
+ hideEditor: function(objectType) {
+ UiPageAction.remove('wcfClipboard-' + objectType);
+
+ UiScreen.pageOverlayOpen();
+ },
+
+ /**
+ * Shows the clipboard editor.
+ */
+ showEditor: function() {
+ this._loadMarkedItems();
+
+ UiScreen.pageOverlayClose();
+ },
+
+ /**
+ * Unmarks the objects with given clipboard object type and ids.
+ *
+ * @param {string} objectType
+ * @param {int[]} objectIds
+ */
+ unmark: function(objectType, objectIds) {
+ this._saveState(objectType, objectIds, false);
+ }
+ };
+});
+
+/**
+ * Provides helper functions for Exif metadata handling.
+ *
+ * @author Maximilian Mader
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Image/ExifUtil
+ */
+define('WoltLabSuite/Core/Image/ExifUtil',[], function() {
+ "use strict";
+
+ var _tagNames = {
+ 'SOI': 0xD8, // Start of image
+ 'APP0': 0xE0, // JFIF tag
+ 'APP1': 0xE1, // EXIF / XMP
+ 'APP2': 0xE2, // General purpose tag
+ 'APP3': 0xE3, // General purpose tag
+ 'APP4': 0xE4, // General purpose tag
+ 'APP5': 0xE5, // General purpose tag
+ 'APP6': 0xE6, // General purpose tag
+ 'APP7': 0xE7, // General purpose tag
+ 'APP8': 0xE8, // General purpose tag
+ 'APP9': 0xE9, // General purpose tag
+ 'APP10': 0xEA, // General purpose tag
+ 'APP11': 0xEB, // General purpose tag
+ 'APP12': 0xEC, // General purpose tag
+ 'APP13': 0xED, // General purpose tag
+ 'APP14': 0xEE, // Often used to store copyright information
+ 'COM': 0xFE, // Comments
+ };
+
+ // Known sequence signatures
+ var _signatureEXIF = 'Exif';
+ var _signatureXMP = 'http://ns.adobe.com/xap/1.0/';
+
+ return {
+ /**
+ * Extracts the EXIF / XMP sections of a JPEG blob.
+ *
+ * @param blob {Blob} JPEG blob
+ * @returns {Promise<Uint8Array | TypeError>} Promise resolving with the EXIF / XMP sections
+ */
+ getExifBytesFromJpeg: function (blob) {
+ return new Promise(function (resolve, reject) {
+ if (!(blob instanceof Blob) && !(blob instanceof File)) {
+ return reject(new TypeError('The argument must be a Blob or a File'));
+ }
+
+ var reader = new FileReader();
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ reader.addEventListener('load', function() {
+ var buffer = reader.result;
+ var bytes = new Uint8Array(buffer);
+ var exif = new Uint8Array();
+
+ if (bytes[0] !== 0xFF && bytes[1] !== _tagNames.SOI) {
+ return reject(new Error('Not a JPEG'));
+ }
+
+ for (var i = 2; i < bytes.length;) {
+ // each sequence starts with 0xFF
+ if (bytes[i] !== 0xFF) break;
+
+ var length = 2 + ((bytes[i + 2] << 8) | bytes[i + 3]);
+
+ // Check if the next byte indicates an EXIF sequence
+ if (bytes[i + 1] === _tagNames.APP1) {
+ var signature = '';
+ for (var j = i + 4; bytes[j] !== 0 && j < bytes.length; j++) {
+ signature += String.fromCharCode(bytes[j]);
+ }
+
+ // Only copy Exif and XMP data
+ if (signature === _signatureEXIF || signature === _signatureXMP) {
+ // append the found EXIF sequence, usually only a single EXIF (APP1) sequence should be defined
+ var sequence = Array.prototype.slice.call(bytes, i, length + i); // IE11 does not have slice in the Uint8Array prototype
+ var concat = new Uint8Array(exif.length + sequence.length);
+ concat.set(exif);
+ concat.set(sequence, exif.length);
+ exif = concat;
+ }
+ }
+
+ i += length
+ }
+
+ // No EXIF data found
+ resolve(exif);
+ });
+
+ reader.readAsArrayBuffer(blob);
+ });
+ },
+
+ /**
+ * Removes all EXIF and XMP sections of a JPEG blob.
+ *
+ * @param blob {Blob} JPEG blob
+ * @returns {Promise<Blob | TypeError>} Promise resolving with the altered JPEG blob
+ */
+ removeExifData: function (blob) {
+ return new Promise(function (resolve, reject) {
+ if (!(blob instanceof Blob) && !(blob instanceof File)) {
+ return reject(new TypeError('The argument must be a Blob or a File'));
+ }
+
+ var reader = new FileReader();
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ reader.addEventListener('load', function () {
+ var buffer = reader.result;
+ var bytes = new Uint8Array(buffer);
+
+ if (bytes[0] !== 0xFF && bytes[1] !== _tagNames.SOI) {
+ return reject(new Error('Not a JPEG'));
+ }
+
+ for (var i = 2; i < bytes.length;) {
+ // each sequence starts with 0xFF
+ if (bytes[i] !== 0xFF) break;
+
+ var length = 2 + ((bytes[i + 2] << 8) | bytes[i + 3]);
+
+ // Check if the next byte indicates an EXIF sequence
+ if (bytes[i + 1] === _tagNames.APP1) {
+ var signature = '';
+ for (var j = i + 4; bytes[j] !== 0 && j < bytes.length; j++) {
+ signature += String.fromCharCode(bytes[j]);
+ }
+
+ // Only remove Exif and XMP data
+ if (signature === _signatureEXIF || signature === _signatureXMP) {
+ var start = Array.prototype.slice.call(bytes, 0, i);
+ var end = Array.prototype.slice.call(bytes, i + length);
+ bytes = new Uint8Array(start.length + end.length);
+ bytes.set(start, 0);
+ bytes.set(end, start.length);
+ }
+ }
+ else {
+ i += length;
+ }
+ }
+
+ resolve(new Blob([bytes], {type: blob.type}));
+ });
+
+ reader.readAsArrayBuffer(blob);
+ });
+ },
+
+ /**
+ * Overrides the APP1 (EXIF / XMP) sections of a JPEG blob with the given data.
+ *
+ * @param blob {Blob} JPEG blob
+ * @param exif {Uint8Array} APP1 sections
+ * @returns {Promise<Blob | never>} Promise resolving with the altered JPEG blob
+ */
+ setExifData: function (blob, exif) {
+ return this.removeExifData(blob).then(function (blob) {
+ return new Promise(function (resolve) {
+ var reader = new FileReader();
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ reader.addEventListener('load', function () {
+ var buffer = reader.result;
+ var bytes = new Uint8Array(buffer);
+ var offset = 2;
+
+ // check if the second tag is the JFIF tag
+ if (bytes[2] === 0xFF && bytes[3] === _tagNames.APP0) {
+ offset += 2 + ((bytes[4] << 8) | bytes[5]);
+ }
+
+ var start = Array.prototype.slice.call(bytes, 0, offset);
+ var end = Array.prototype.slice.call(bytes, offset);
+
+ bytes = new Uint8Array(start.length + exif.length + end.length);
+ bytes.set(start);
+ bytes.set(exif, offset);
+ bytes.set(end, offset + exif.length);
+
+ resolve(new Blob([bytes], {type: blob.type}));
+ });
+
+ reader.readAsArrayBuffer(blob);
+ });
+ });
+ }
+ };
+});
+
+/**
+ * Provides helper functions for Image metadata handling.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Image/ImageUtil
+ */
+define('WoltLabSuite/Core/Image/ImageUtil',[], function() {
+ "use strict";
+
+ return {
+ /**
+ * Returns whether the given canvas contains transparent pixels.
+ *
+ * @param image {Canvas} Canvas to check
+ * @returns {bool}
+ */
+ containsTransparentPixels: function (canvas) {
+ var imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
+
+ for (var i = 3, max = imageData.data.length; i < max; i += 4) {
+ if (imageData.data[i] !== 255) return true;
+ }
+
+ return false;
+ }
+ };
+});
+
+/* (The MIT License)
+
+Copyright (C) 2014-2017 by Vitaly Puzrin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+/* pica 5.0.0 nodeca/pica */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define('Pica',[],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pica = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+// Collection of math functions
+//
+// 1. Combine components together
+// 2. Has async init to load wasm modules
+//
+ 'use strict';
+
+ var inherits = require('inherits');
+
+ var Multimath = require('multimath');
+
+ var mm_unsharp_mask = require('multimath/lib/unsharp_mask');
+
+ var mm_resize = require('./mm_resize');
+
+ function MathLib(requested_features) {
+ var __requested_features = requested_features || [];
+
+ var features = {
+ js: __requested_features.indexOf('js') >= 0,
+ wasm: __requested_features.indexOf('wasm') >= 0
+ };
+ Multimath.call(this, features);
+ this.features = {
+ js: features.js,
+ wasm: features.wasm && this.has_wasm
+ };
+ this.use(mm_unsharp_mask);
+ this.use(mm_resize);
+ }
+
+ inherits(MathLib, Multimath);
+
+ MathLib.prototype.resizeAndUnsharp = function resizeAndUnsharp(options, cache) {
+ var result = this.resize(options, cache);
+
+ if (options.unsharpAmount) {
+ this.unsharp_mask(result, options.toWidth, options.toHeight, options.unsharpAmount, options.unsharpRadius, options.unsharpThreshold);
+ }
+
+ return result;
+ };
+
+ module.exports = MathLib;
+
+ },{"./mm_resize":4,"inherits":15,"multimath":16,"multimath/lib/unsharp_mask":19}],2:[function(require,module,exports){
+// Resize convolvers, pure JS implementation
+//
+ 'use strict'; // Precision of fixed FP values
+//var FIXED_FRAC_BITS = 14;
+
+ function clampTo8(i) {
+ return i < 0 ? 0 : i > 255 ? 255 : i;
+ } // Convolve image in horizontal directions and transpose output. In theory,
+// transpose allow:
+//
+// - use the same convolver for both passes (this fails due different
+// types of input array and temporary buffer)
+// - making vertical pass by horisonltal lines inprove CPU cache use.
+//
+// But in real life this doesn't work :)
+//
+
+
+ function convolveHorizontally(src, dest, srcW, srcH, destW, filters) {
+ var r, g, b, a;
+ var filterPtr, filterShift, filterSize;
+ var srcPtr, srcY, destX, filterVal;
+ var srcOffset = 0,
+ destOffset = 0; // For each row
+
+ for (srcY = 0; srcY < srcH; srcY++) {
+ filterPtr = 0; // Apply precomputed filters to each destination row point
+
+ for (destX = 0; destX < destW; destX++) {
+ // Get the filter that determines the current output pixel.
+ filterShift = filters[filterPtr++];
+ filterSize = filters[filterPtr++];
+ srcPtr = srcOffset + filterShift * 4 | 0;
+ r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a
+
+ for (; filterSize > 0; filterSize--) {
+ filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10)
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
+
+ a = a + filterVal * src[srcPtr + 3] | 0;
+ b = b + filterVal * src[srcPtr + 2] | 0;
+ g = g + filterVal * src[srcPtr + 1] | 0;
+ r = r + filterVal * src[srcPtr] | 0;
+ srcPtr = srcPtr + 4 | 0;
+ } // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
+ //
+ // (!) Add 1/2 of value before clamping to get proper rounding. In other
+ // case brightness loss will be noticeable if you resize image with white
+ // border and place it on white background.
+ //
+
+
+ dest[destOffset + 3] = clampTo8(a + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset] = clampTo8(r + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ destOffset = destOffset + srcH * 4 | 0;
+ }
+
+ destOffset = (srcY + 1) * 4 | 0;
+ srcOffset = (srcY + 1) * srcW * 4 | 0;
+ }
+ } // Technically, convolvers are the same. But input array and temporary
+// buffer can be of different type (especially, in old browsers). So,
+// keep code in separate functions to avoid deoptimizations & speed loss.
+
+
+ function convolveVertically(src, dest, srcW, srcH, destW, filters) {
+ var r, g, b, a;
+ var filterPtr, filterShift, filterSize;
+ var srcPtr, srcY, destX, filterVal;
+ var srcOffset = 0,
+ destOffset = 0; // For each row
+
+ for (srcY = 0; srcY < srcH; srcY++) {
+ filterPtr = 0; // Apply precomputed filters to each destination row point
+
+ for (destX = 0; destX < destW; destX++) {
+ // Get the filter that determines the current output pixel.
+ filterShift = filters[filterPtr++];
+ filterSize = filters[filterPtr++];
+ srcPtr = srcOffset + filterShift * 4 | 0;
+ r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a
+
+ for (; filterSize > 0; filterSize--) {
+ filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10)
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
+
+ a = a + filterVal * src[srcPtr + 3] | 0;
+ b = b + filterVal * src[srcPtr + 2] | 0;
+ g = g + filterVal * src[srcPtr + 1] | 0;
+ r = r + filterVal * src[srcPtr] | 0;
+ srcPtr = srcPtr + 4 | 0;
+ } // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
+ //
+ // (!) Add 1/2 of value before clamping to get proper rounding. In other
+ // case brightness loss will be noticeable if you resize image with white
+ // border and place it on white background.
+ //
+
+
+ dest[destOffset + 3] = clampTo8(a + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset] = clampTo8(r + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ destOffset = destOffset + srcH * 4 | 0;
+ }
+
+ destOffset = (srcY + 1) * 4 | 0;
+ srcOffset = (srcY + 1) * srcW * 4 | 0;
+ }
+ }
+
+ module.exports = {
+ convolveHorizontally: convolveHorizontally,
+ convolveVertically: convolveVertically
+ };
+
+ },{}],3:[function(require,module,exports){
+// This is autogenerated file from math.wasm, don't edit.
+//
+ 'use strict';
+ /* eslint-disable max-len */
+
+ module.exports = 'AGFzbQEAAAABFAJgBn9/f39/fwBgB39/f39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQQEAXAAAAcZAghjb252b2x2ZQAACmNvbnZvbHZlSFYAAQkBAArmAwLBAwEQfwJAIANFDQAgBEUNACAFQQRqIRVBACEMQQAhDQNAIA0hDkEAIRFBACEHA0AgB0ECaiESAn8gBSAHQQF0IgdqIgZBAmouAQAiEwRAQQAhCEEAIBNrIRQgFSAHaiEPIAAgDCAGLgEAakECdGohEEEAIQlBACEKQQAhCwNAIBAoAgAiB0EYdiAPLgEAIgZsIAtqIQsgB0H/AXEgBmwgCGohCCAHQRB2Qf8BcSAGbCAKaiEKIAdBCHZB/wFxIAZsIAlqIQkgD0ECaiEPIBBBBGohECAUQQFqIhQNAAsgEiATagwBC0EAIQtBACEKQQAhCUEAIQggEgshByABIA5BAnRqIApBgMAAakEOdSIGQf8BIAZB/wFIG0EQdEGAgPwHcUEAIAZBAEobIAtBgMAAakEOdSIGQf8BIAZB/wFIG0EYdEEAIAZBAEobciAJQYDAAGpBDnUiBkH/ASAGQf8BSBtBCHRBgP4DcUEAIAZBAEobciAIQYDAAGpBDnUiBkH/ASAGQf8BSBtB/wFxQQAgBkEAShtyNgIAIA4gA2ohDiARQQFqIhEgBEcNAAsgDCACaiEMIA1BAWoiDSADRw0ACwsLIQACQEEAIAIgAyAEIAUgABAAIAJBACAEIAUgBiABEAALCw==';
+
+ },{}],4:[function(require,module,exports){
+ 'use strict';
+
+ module.exports = {
+ name: 'resize',
+ fn: require('./resize'),
+ wasm_fn: require('./resize_wasm'),
+ wasm_src: require('./convolve_wasm_base64')
+ };
+
+ },{"./convolve_wasm_base64":3,"./resize":5,"./resize_wasm":8}],5:[function(require,module,exports){
+ 'use strict';
+
+ var createFilters = require('./resize_filter_gen');
+
+ var convolveHorizontally = require('./convolve').convolveHorizontally;
+
+ var convolveVertically = require('./convolve').convolveVertically;
+
+ function resetAlpha(dst, width, height) {
+ var ptr = 3,
+ len = width * height * 4 | 0;
+
+ while (ptr < len) {
+ dst[ptr] = 0xFF;
+ ptr = ptr + 4 | 0;
+ }
+ }
+
+ module.exports = function resize(options) {
+ var src = options.src;
+ var srcW = options.width;
+ var srcH = options.height;
+ var destW = options.toWidth;
+ var destH = options.toHeight;
+ var scaleX = options.scaleX || options.toWidth / options.width;
+ var scaleY = options.scaleY || options.toHeight / options.height;
+ var offsetX = options.offsetX || 0;
+ var offsetY = options.offsetY || 0;
+ var dest = options.dest || new Uint8Array(destW * destH * 4);
+ var quality = typeof options.quality === 'undefined' ? 3 : options.quality;
+ var alpha = options.alpha || false;
+ var filtersX = createFilters(quality, srcW, destW, scaleX, offsetX),
+ filtersY = createFilters(quality, srcH, destH, scaleY, offsetY);
+ var tmp = new Uint8Array(destW * srcH * 4); // To use single function we need src & tmp of the same type.
+ // But src can be CanvasPixelArray, and tmp - Uint8Array. So, keep
+ // vertical and horizontal passes separately to avoid deoptimization.
+
+ convolveHorizontally(src, tmp, srcW, srcH, destW, filtersX);
+ convolveVertically(tmp, dest, srcH, destW, destH, filtersY); // That's faster than doing checks in convolver.
+ // !!! Note, canvas data is not premultipled. We don't need other
+ // alpha corrections.
+
+ if (!alpha) resetAlpha(dest, destW, destH);
+ return dest;
+ };
+
+ },{"./convolve":2,"./resize_filter_gen":6}],6:[function(require,module,exports){
+// Calculate convolution filters for each destination point,
+// and pack data to Int16Array:
+//
+// [ shift, length, data..., shift2, length2, data..., ... ]
+//
+// - shift - offset in src image
+// - length - filter length (in src points)
+// - data - filter values sequence
+//
+ 'use strict';
+
+ var FILTER_INFO = require('./resize_filter_info'); // Precision of fixed FP values
+
+
+ var FIXED_FRAC_BITS = 14;
+
+ function toFixedPoint(num) {
+ return Math.round(num * ((1 << FIXED_FRAC_BITS) - 1));
+ }
+
+ module.exports = function resizeFilterGen(quality, srcSize, destSize, scale, offset) {
+ var filterFunction = FILTER_INFO[quality].filter;
+ var scaleInverted = 1.0 / scale;
+ var scaleClamped = Math.min(1.0, scale); // For upscale
+ // Filter window (averaging interval), scaled to src image
+
+ var srcWindow = FILTER_INFO[quality].win / scaleClamped;
+ var destPixel, srcPixel, srcFirst, srcLast, filterElementSize, floatFilter, fxpFilter, total, pxl, idx, floatVal, filterTotal, filterVal;
+ var leftNotEmpty, rightNotEmpty, filterShift, filterSize;
+ var maxFilterElementSize = Math.floor((srcWindow + 1) * 2);
+ var packedFilter = new Int16Array((maxFilterElementSize + 2) * destSize);
+ var packedFilterPtr = 0;
+ var slowCopy = !packedFilter.subarray || !packedFilter.set; // For each destination pixel calculate source range and built filter values
+
+ for (destPixel = 0; destPixel < destSize; destPixel++) {
+ // Scaling should be done relative to central pixel point
+ srcPixel = (destPixel + 0.5) * scaleInverted + offset;
+ srcFirst = Math.max(0, Math.floor(srcPixel - srcWindow));
+ srcLast = Math.min(srcSize - 1, Math.ceil(srcPixel + srcWindow));
+ filterElementSize = srcLast - srcFirst + 1;
+ floatFilter = new Float32Array(filterElementSize);
+ fxpFilter = new Int16Array(filterElementSize);
+ total = 0.0; // Fill filter values for calculated range
+
+ for (pxl = srcFirst, idx = 0; pxl <= srcLast; pxl++, idx++) {
+ floatVal = filterFunction((pxl + 0.5 - srcPixel) * scaleClamped);
+ total += floatVal;
+ floatFilter[idx] = floatVal;
+ } // Normalize filter, convert to fixed point and accumulate conversion error
+
+
+ filterTotal = 0;
+
+ for (idx = 0; idx < floatFilter.length; idx++) {
+ filterVal = floatFilter[idx] / total;
+ filterTotal += filterVal;
+ fxpFilter[idx] = toFixedPoint(filterVal);
+ } // Compensate normalization error, to minimize brightness drift
+
+
+ fxpFilter[destSize >> 1] += toFixedPoint(1.0 - filterTotal); //
+ // Now pack filter to useable form
+ //
+ // 1. Trim heading and tailing zero values, and compensate shitf/length
+ // 2. Put all to single array in this format:
+ //
+ // [ pos shift, data length, value1, value2, value3, ... ]
+ //
+
+ leftNotEmpty = 0;
+
+ while (leftNotEmpty < fxpFilter.length && fxpFilter[leftNotEmpty] === 0) {
+ leftNotEmpty++;
+ }
+
+ if (leftNotEmpty < fxpFilter.length) {
+ rightNotEmpty = fxpFilter.length - 1;
+
+ while (rightNotEmpty > 0 && fxpFilter[rightNotEmpty] === 0) {
+ rightNotEmpty--;
+ }
+
+ filterShift = srcFirst + leftNotEmpty;
+ filterSize = rightNotEmpty - leftNotEmpty + 1;
+ packedFilter[packedFilterPtr++] = filterShift; // shift
+
+ packedFilter[packedFilterPtr++] = filterSize; // size
+
+ if (!slowCopy) {
+ packedFilter.set(fxpFilter.subarray(leftNotEmpty, rightNotEmpty + 1), packedFilterPtr);
+ packedFilterPtr += filterSize;
+ } else {
+ // fallback for old IE < 11, without subarray/set methods
+ for (idx = leftNotEmpty; idx <= rightNotEmpty; idx++) {
+ packedFilter[packedFilterPtr++] = fxpFilter[idx];
+ }
+ }
+ } else {
+ // zero data, write header only
+ packedFilter[packedFilterPtr++] = 0; // shift
+
+ packedFilter[packedFilterPtr++] = 0; // size
+ }
+ }
+
+ return packedFilter;
+ };
+
+ },{"./resize_filter_info":7}],7:[function(require,module,exports){
+// Filter definitions to build tables for
+// resizing convolvers.
+//
+// Presets for quality 0..3. Filter functions + window size
+//
+ 'use strict';
+
+ module.exports = [{
+ // Nearest neibor (Box)
+ win: 0.5,
+ filter: function filter(x) {
+ return x >= -0.5 && x < 0.5 ? 1.0 : 0.0;
+ }
+ }, {
+ // Hamming
+ win: 1.0,
+ filter: function filter(x) {
+ if (x <= -1.0 || x >= 1.0) {
+ return 0.0;
+ }
+
+ if (x > -1.19209290E-07 && x < 1.19209290E-07) {
+ return 1.0;
+ }
+
+ var xpi = x * Math.PI;
+ return Math.sin(xpi) / xpi * (0.54 + 0.46 * Math.cos(xpi / 1.0));
+ }
+ }, {
+ // Lanczos, win = 2
+ win: 2.0,
+ filter: function filter(x) {
+ if (x <= -2.0 || x >= 2.0) {
+ return 0.0;
+ }
+
+ if (x > -1.19209290E-07 && x < 1.19209290E-07) {
+ return 1.0;
+ }
+
+ var xpi = x * Math.PI;
+ return Math.sin(xpi) / xpi * Math.sin(xpi / 2.0) / (xpi / 2.0);
+ }
+ }, {
+ // Lanczos, win = 3
+ win: 3.0,
+ filter: function filter(x) {
+ if (x <= -3.0 || x >= 3.0) {
+ return 0.0;
+ }
+
+ if (x > -1.19209290E-07 && x < 1.19209290E-07) {
+ return 1.0;
+ }
+
+ var xpi = x * Math.PI;
+ return Math.sin(xpi) / xpi * Math.sin(xpi / 3.0) / (xpi / 3.0);
+ }
+ }];
+
+ },{}],8:[function(require,module,exports){
+ 'use strict';
+
+ var createFilters = require('./resize_filter_gen');
+
+ function resetAlpha(dst, width, height) {
+ var ptr = 3,
+ len = width * height * 4 | 0;
+
+ while (ptr < len) {
+ dst[ptr] = 0xFF;
+ ptr = ptr + 4 | 0;
+ }
+ }
+
+ function asUint8Array(src) {
+ return new Uint8Array(src.buffer, 0, src.byteLength);
+ }
+
+ var IS_LE = true; // should not crash everything on module load in old browsers
+
+ try {
+ IS_LE = new Uint32Array(new Uint8Array([1, 0, 0, 0]).buffer)[0] === 1;
+ } catch (__) {}
+
+ function copyInt16asLE(src, target, target_offset) {
+ if (IS_LE) {
+ target.set(asUint8Array(src), target_offset);
+ return;
+ }
+
+ for (var ptr = target_offset, i = 0; i < src.length; i++) {
+ var data = src[i];
+ target[ptr++] = data & 0xFF;
+ target[ptr++] = data >> 8 & 0xFF;
+ }
+ }
+
+ module.exports = function resize_wasm(options) {
+ var src = options.src;
+ var srcW = options.width;
+ var srcH = options.height;
+ var destW = options.toWidth;
+ var destH = options.toHeight;
+ var scaleX = options.scaleX || options.toWidth / options.width;
+ var scaleY = options.scaleY || options.toHeight / options.height;
+ var offsetX = options.offsetX || 0.0;
+ var offsetY = options.offsetY || 0.0;
+ var dest = options.dest || new Uint8Array(destW * destH * 4);
+ var quality = typeof options.quality === 'undefined' ? 3 : options.quality;
+ var alpha = options.alpha || false;
+ var filtersX = createFilters(quality, srcW, destW, scaleX, offsetX),
+ filtersY = createFilters(quality, srcH, destH, scaleY, offsetY); // destination is 0 too.
+
+ var src_offset = 0; // buffer between convolve passes
+
+ var tmp_offset = this.__align(src_offset + Math.max(src.byteLength, dest.byteLength));
+
+ var filtersX_offset = this.__align(tmp_offset + srcH * destW * 4);
+
+ var filtersY_offset = this.__align(filtersX_offset + filtersX.byteLength);
+
+ var alloc_bytes = filtersY_offset + filtersY.byteLength;
+
+ var instance = this.__instance('resize', alloc_bytes); //
+ // Fill memory block with data to process
+ //
+
+
+ var mem = new Uint8Array(this.__memory.buffer);
+ var mem32 = new Uint32Array(this.__memory.buffer); // 32-bit copy is much faster in chrome
+
+ var src32 = new Uint32Array(src.buffer);
+ mem32.set(src32); // We should guarantee LE bytes order. Filters are not big, so
+ // speed difference is not significant vs direct .set()
+
+ copyInt16asLE(filtersX, mem, filtersX_offset);
+ copyInt16asLE(filtersY, mem, filtersY_offset); //
+ // Now call webassembly method
+ // emsdk does method names with '_'
+
+ var fn = instance.exports.convolveHV || instance.exports._convolveHV;
+ fn(filtersX_offset, filtersY_offset, tmp_offset, srcW, srcH, destW, destH); //
+ // Copy data back to typed array
+ //
+ // 32-bit copy is much faster in chrome
+
+ var dest32 = new Uint32Array(dest.buffer);
+ dest32.set(new Uint32Array(this.__memory.buffer, 0, destH * destW)); // That's faster than doing checks in convolver.
+ // !!! Note, canvas data is not premultipled. We don't need other
+ // alpha corrections.
+
+ if (!alpha) resetAlpha(dest, destW, destH);
+ return dest;
+ };
+
+ },{"./resize_filter_gen":6}],9:[function(require,module,exports){
+ 'use strict';
+
+ var GC_INTERVAL = 100;
+
+ function Pool(create, idle) {
+ this.create = create;
+ this.available = [];
+ this.acquired = {};
+ this.lastId = 1;
+ this.timeoutId = 0;
+ this.idle = idle || 2000;
+ }
+
+ Pool.prototype.acquire = function () {
+ var _this = this;
+
+ var resource;
+
+ if (this.available.length !== 0) {
+ resource = this.available.pop();
+ } else {
+ resource = this.create();
+ resource.id = this.lastId++;
+
+ resource.release = function () {
+ return _this.release(resource);
+ };
+ }
+
+ this.acquired[resource.id] = resource;
+ return resource;
+ };
+
+ Pool.prototype.release = function (resource) {
+ var _this2 = this;
+
+ delete this.acquired[resource.id];
+ resource.lastUsed = Date.now();
+ this.available.push(resource);
+
+ if (this.timeoutId === 0) {
+ this.timeoutId = setTimeout(function () {
+ return _this2.gc();
+ }, GC_INTERVAL);
+ }
+ };
+
+ Pool.prototype.gc = function () {
+ var _this3 = this;
+
+ var now = Date.now();
+ this.available = this.available.filter(function (resource) {
+ if (now - resource.lastUsed > _this3.idle) {
+ resource.destroy();
+ return false;
+ }
+
+ return true;
+ });
+
+ if (this.available.length !== 0) {
+ this.timeoutId = setTimeout(function () {
+ return _this3.gc();
+ }, GC_INTERVAL);
+ } else {
+ this.timeoutId = 0;
+ }
+ };
+
+ module.exports = Pool;
+
+ },{}],10:[function(require,module,exports){
+// Add intermediate resizing steps when scaling down by a very large factor.
+//
+// For example, when resizing 10000x10000 down to 10x10, it'll resize it to
+// 300x300 first.
+//
+// It's needed because tiler has issues when the entire tile is scaled down
+// to a few pixels (1024px source tile with border size 3 should result in
+// at least 3+3+2 = 8px target tile, so max scale factor is 128 here).
+//
+// Also, adding intermediate steps can speed up processing if we use lower
+// quality algorithms for first stages.
+//
+ 'use strict'; // min size = 0 results in infinite loop,
+// min size = 1 can consume large amount of memory
+
+ var MIN_INNER_TILE_SIZE = 2;
+
+ module.exports = function createStages(fromWidth, fromHeight, toWidth, toHeight, srcTileSize, destTileBorder) {
+ var scaleX = toWidth / fromWidth;
+ var scaleY = toHeight / fromHeight; // derived from createRegions equation:
+ // innerTileWidth = pixelFloor(srcTileSize * scaleX) - 2 * destTileBorder;
+
+ var minScale = (2 * destTileBorder + MIN_INNER_TILE_SIZE + 1) / srcTileSize; // refuse to scale image multiple times by less than twice each time,
+ // it could only happen because of invalid options
+
+ if (minScale > 0.5) return [[toWidth, toHeight]];
+ var stageCount = Math.ceil(Math.log(Math.min(scaleX, scaleY)) / Math.log(minScale)); // no additional resizes are necessary,
+ // stageCount can be zero or be negative when enlarging the image
+
+ if (stageCount <= 1) return [[toWidth, toHeight]];
+ var result = [];
+
+ for (var i = 0; i < stageCount; i++) {
+ var width = Math.round(Math.pow(Math.pow(fromWidth, stageCount - i - 1) * Math.pow(toWidth, i + 1), 1 / stageCount));
+ var height = Math.round(Math.pow(Math.pow(fromHeight, stageCount - i - 1) * Math.pow(toHeight, i + 1), 1 / stageCount));
+ result.push([width, height]);
+ }
+
+ return result;
+ };
+
+ },{}],11:[function(require,module,exports){
+// Split original image into multiple 1024x1024 chunks to reduce memory usage
+// (images have to be unpacked into typed arrays for resizing) and allow
+// parallel processing of multiple tiles at a time.
+//
+ 'use strict';
+ /*
+ * pixelFloor and pixelCeil are modified versions of Math.floor and Math.ceil
+ * functions which take into account floating point arithmetic errors.
+ * Those errors can cause undesired increments/decrements of sizes and offsets:
+ * Math.ceil(36 / (36 / 500)) = 501
+ * pixelCeil(36 / (36 / 500)) = 500
+ */
+
+ var PIXEL_EPSILON = 1e-5;
+
+ function pixelFloor(x) {
+ var nearest = Math.round(x);
+
+ if (Math.abs(x - nearest) < PIXEL_EPSILON) {
+ return nearest;
+ }
+
+ return Math.floor(x);
+ }
+
+ function pixelCeil(x) {
+ var nearest = Math.round(x);
+
+ if (Math.abs(x - nearest) < PIXEL_EPSILON) {
+ return nearest;
+ }
+
+ return Math.ceil(x);
+ }
+
+ module.exports = function createRegions(options) {
+ var scaleX = options.toWidth / options.width;
+ var scaleY = options.toHeight / options.height;
+ var innerTileWidth = pixelFloor(options.srcTileSize * scaleX) - 2 * options.destTileBorder;
+ var innerTileHeight = pixelFloor(options.srcTileSize * scaleY) - 2 * options.destTileBorder; // prevent infinite loop, this should never happen
+
+ if (innerTileWidth < 1 || innerTileHeight < 1) {
+ throw new Error('Internal error in pica: target tile width/height is too small.');
+ }
+
+ var x, y;
+ var innerX, innerY, toTileWidth, toTileHeight;
+ var tiles = [];
+ var tile; // we go top-to-down instead of left-to-right to make image displayed from top to
+ // doesn in the browser
+
+ for (innerY = 0; innerY < options.toHeight; innerY += innerTileHeight) {
+ for (innerX = 0; innerX < options.toWidth; innerX += innerTileWidth) {
+ x = innerX - options.destTileBorder;
+
+ if (x < 0) {
+ x = 0;
+ }
+
+ toTileWidth = innerX + innerTileWidth + options.destTileBorder - x;
+
+ if (x + toTileWidth >= options.toWidth) {
+ toTileWidth = options.toWidth - x;
+ }
+
+ y = innerY - options.destTileBorder;
+
+ if (y < 0) {
+ y = 0;
+ }
+
+ toTileHeight = innerY + innerTileHeight + options.destTileBorder - y;
+
+ if (y + toTileHeight >= options.toHeight) {
+ toTileHeight = options.toHeight - y;
+ }
+
+ tile = {
+ toX: x,
+ toY: y,
+ toWidth: toTileWidth,
+ toHeight: toTileHeight,
+ toInnerX: innerX,
+ toInnerY: innerY,
+ toInnerWidth: innerTileWidth,
+ toInnerHeight: innerTileHeight,
+ offsetX: x / scaleX - pixelFloor(x / scaleX),
+ offsetY: y / scaleY - pixelFloor(y / scaleY),
+ scaleX: scaleX,
+ scaleY: scaleY,
+ x: pixelFloor(x / scaleX),
+ y: pixelFloor(y / scaleY),
+ width: pixelCeil(toTileWidth / scaleX),
+ height: pixelCeil(toTileHeight / scaleY)
+ };
+ tiles.push(tile);
+ }
+ }
+
+ return tiles;
+ };
+
+ },{}],12:[function(require,module,exports){
+ 'use strict';
+
+ function objClass(obj) {
+ return Object.prototype.toString.call(obj);
+ }
+
+ module.exports.isCanvas = function isCanvas(element) {
+ //return (element.nodeName && element.nodeName.toLowerCase() === 'canvas') ||
+ var cname = objClass(element);
+ return cname === '[object HTMLCanvasElement]'
+ /* browser */
+ || cname === '[object Canvas]'
+ /* node-canvas */
+ ;
+ };
+
+ module.exports.isImage = function isImage(element) {
+ //return element.nodeName && element.nodeName.toLowerCase() === 'img';
+ return objClass(element) === '[object HTMLImageElement]';
+ };
+
+ module.exports.limiter = function limiter(concurrency) {
+ var active = 0,
+ queue = [];
+
+ function roll() {
+ if (active < concurrency && queue.length) {
+ active++;
+ queue.shift()();
+ }
+ }
+
+ return function limit(fn) {
+ return new Promise(function (resolve, reject) {
+ queue.push(function () {
+ fn().then(function (result) {
+ resolve(result);
+ active--;
+ roll();
+ }, function (err) {
+ reject(err);
+ active--;
+ roll();
+ });
+ });
+ roll();
+ });
+ };
+ };
+
+ module.exports.cib_quality_name = function cib_quality_name(num) {
+ switch (num) {
+ case 0:
+ return 'pixelated';
+
+ case 1:
+ return 'low';
+
+ case 2:
+ return 'medium';
+ }
+
+ return 'high';
+ };
+
+ module.exports.cib_support = function cib_support() {
+ return Promise.resolve().then(function () {
+ if (typeof createImageBitmap === 'undefined' || typeof document === 'undefined') {
+ return false;
+ }
+
+ var c = document.createElement('canvas');
+ c.width = 100;
+ c.height = 100;
+ return createImageBitmap(c, 0, 0, 100, 100, {
+ resizeWidth: 10,
+ resizeHeight: 10,
+ resizeQuality: 'high'
+ }).then(function (bitmap) {
+ var status = bitmap.width === 10; // Branch below is filtered on upper level. We do not call resize
+ // detection for basic ImageBitmap.
+ //
+ // https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap
+ // old Crome 51 has ImageBitmap without .close(). Then this code
+ // will throw and return 'false' as expected.
+ //
+
+ bitmap.close();
+ c = null;
+ return status;
+ });
+ }).catch(function () {
+ return false;
+ });
+ };
+
+ },{}],13:[function(require,module,exports){
+// Web Worker wrapper for image resize function
+ 'use strict';
+
+ module.exports = function () {
+ var MathLib = require('./mathlib');
+
+ var mathLib;
+ /* eslint-disable no-undef */
+
+ onmessage = function onmessage(ev) {
+ var opts = ev.data.opts;
+ if (!mathLib) mathLib = new MathLib(ev.data.features); // Use multimath's sync auto-init. Avoid Promise use in old browsers,
+ // because polyfills are not propagated to webworker.
+
+ var result = mathLib.resizeAndUnsharp(opts);
+ postMessage({
+ result: result
+ }, [result.buffer]);
+ };
+ };
+
+ },{"./mathlib":1}],14:[function(require,module,exports){
+// Calculate Gaussian blur of an image using IIR filter
+// The method is taken from Intel's white paper and code example attached to it:
+// https://software.intel.com/en-us/articles/iir-gaussian-blur-filter
+// -implementation-using-intel-advanced-vector-extensions
+
+ var a0, a1, a2, a3, b1, b2, left_corner, right_corner;
+
+ function gaussCoef(sigma) {
+ if (sigma < 0.5) {
+ sigma = 0.5;
+ }
+
+ var a = Math.exp(0.726 * 0.726) / sigma,
+ g1 = Math.exp(-a),
+ g2 = Math.exp(-2 * a),
+ k = (1 - g1) * (1 - g1) / (1 + 2 * a * g1 - g2);
+
+ a0 = k;
+ a1 = k * (a - 1) * g1;
+ a2 = k * (a + 1) * g1;
+ a3 = -k * g2;
+ b1 = 2 * g1;
+ b2 = -g2;
+ left_corner = (a0 + a1) / (1 - b1 - b2);
+ right_corner = (a2 + a3) / (1 - b1 - b2);
+
+ // Attempt to force type to FP32.
+ return new Float32Array([ a0, a1, a2, a3, b1, b2, left_corner, right_corner ]);
+ }
+
+ function convolveMono16(src, out, line, coeff, width, height) {
+ // takes src image and writes the blurred and transposed result into out
+
+ var prev_src, curr_src, curr_out, prev_out, prev_prev_out;
+ var src_index, out_index, line_index;
+ var i, j;
+ var coeff_a0, coeff_a1, coeff_b1, coeff_b2;
+
+ for (i = 0; i < height; i++) {
+ src_index = i * width;
+ out_index = i;
+ line_index = 0;
+
+ // left to right
+ prev_src = src[src_index];
+ prev_prev_out = prev_src * coeff[6];
+ prev_out = prev_prev_out;
+
+ coeff_a0 = coeff[0];
+ coeff_a1 = coeff[1];
+ coeff_b1 = coeff[4];
+ coeff_b2 = coeff[5];
+
+ for (j = 0; j < width; j++) {
+ curr_src = src[src_index];
+
+ curr_out = curr_src * coeff_a0 +
+ prev_src * coeff_a1 +
+ prev_out * coeff_b1 +
+ prev_prev_out * coeff_b2;
+
+ prev_prev_out = prev_out;
+ prev_out = curr_out;
+ prev_src = curr_src;
+
+ line[line_index] = prev_out;
+ line_index++;
+ src_index++;
+ }
+
+ src_index--;
+ line_index--;
+ out_index += height * (width - 1);
+
+ // right to left
+ prev_src = src[src_index];
+ prev_prev_out = prev_src * coeff[7];
+ prev_out = prev_prev_out;
+ curr_src = prev_src;
+
+ coeff_a0 = coeff[2];
+ coeff_a1 = coeff[3];
+
+ for (j = width - 1; j >= 0; j--) {
+ curr_out = curr_src * coeff_a0 +
+ prev_src * coeff_a1 +
+ prev_out * coeff_b1 +
+ prev_prev_out * coeff_b2;
+
+ prev_prev_out = prev_out;
+ prev_out = curr_out;
+
+ prev_src = curr_src;
+ curr_src = src[src_index];
+
+ out[out_index] = line[line_index] + prev_out;
+
+ src_index--;
+ line_index--;
+ out_index -= height;
+ }
+ }
+ }
+
+
+ function blurMono16(src, width, height, radius) {
+ // Quick exit on zero radius
+ if (!radius) { return; }
+
+ var out = new Uint16Array(src.length),
+ tmp_line = new Float32Array(Math.max(width, height));
+
+ var coeff = gaussCoef(radius);
+
+ convolveMono16(src, out, tmp_line, coeff, width, height, radius);
+ convolveMono16(out, src, tmp_line, coeff, height, width, radius);
+ }
+
+ module.exports = blurMono16;
+
+ },{}],15:[function(require,module,exports){
+ if (typeof Object.create === 'function') {
+ // implementation from standard node.js 'util' module
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ };
+ } else {
+ // old school shim for old browsers
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
+ }
+
+ },{}],16:[function(require,module,exports){
+ 'use strict';
+
+
+ var assign = require('object-assign');
+ var base64decode = require('./lib/base64decode');
+ var hasWebAssembly = require('./lib/wa_detect');
+
+
+ var DEFAULT_OPTIONS = {
+ js: true,
+ wasm: true
+ };
+
+
+ function MultiMath(options) {
+ if (!(this instanceof MultiMath)) return new MultiMath(options);
+
+ var opts = assign({}, DEFAULT_OPTIONS, options || {});
+
+ this.options = opts;
+
+ this.__cache = {};
+ this.has_wasm = hasWebAssembly();
+
+ this.__init_promise = null;
+ this.__modules = opts.modules || {};
+ this.__memory = null;
+ this.__wasm = {};
+
+ this.__isLE = ((new Uint32Array((new Uint8Array([ 1, 0, 0, 0 ])).buffer))[0] === 1);
+
+ if (!this.options.js && !this.options.wasm) {
+ throw new Error('mathlib: at least "js" or "wasm" should be enabled');
+ }
+ }
+
+
+ MultiMath.prototype.use = function (module) {
+ this.__modules[module.name] = module;
+
+ // Pin the best possible implementation
+ if (!this.has_wasm || !this.options.wasm || !module.wasm_fn) {
+ this[module.name] = module.fn;
+ } else {
+ this[module.name] = module.wasm_fn;
+ }
+
+ return this;
+ };
+
+
+ MultiMath.prototype.init = function () {
+ if (this.__init_promise) return this.__init_promise;
+
+ if (!this.options.js && this.options.wasm && !this.has_wasm) {
+ return Promise.reject(new Error('mathlib: only "wasm" was enabled, but it\'s not supported'));
+ }
+
+ var self = this;
+
+ this.__init_promise = Promise.all(Object.keys(self.__modules).map(function (name) {
+ var module = self.__modules[name];
+
+ if (!self.has_wasm || !self.options.wasm || !module.wasm_fn) return null;
+
+ // If already compiled - exit
+ if (self.__wasm[name]) return null;
+
+ // Compile wasm source
+ return WebAssembly.compile(self.__base64decode(module.wasm_src))
+ .then(function (m) { self.__wasm[name] = m; });
+ }))
+ .then(function () { return self; });
+
+ return this.__init_promise;
+ };
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Methods below are for internal use from plugins
+
+
+// Simple decode base64 to typed array. Useful to load embedded webassembly
+// code. You probably don't need to call this method directly.
+//
+ MultiMath.prototype.__base64decode = base64decode;
+
+
+// Increase current memory to include specified number of bytes. Do nothing if
+// size is already ok. You probably don't need to call this method directly,
+// because it will be invoked from `.__instance()`.
+//
+ MultiMath.prototype.__reallocate = function mem_grow_to(bytes) {
+ if (!this.__memory) {
+ this.__memory = new WebAssembly.Memory({
+ initial: Math.ceil(bytes / (64 * 1024))
+ });
+ return this.__memory;
+ }
+
+ var mem_size = this.__memory.buffer.byteLength;
+
+ if (mem_size < bytes) {
+ this.__memory.grow(Math.ceil((bytes - mem_size) / (64 * 1024)));
+ }
+
+ return this.__memory;
+ };
+
+
+// Returns instantinated webassembly item by name, with specified memory size
+// and environment.
+// - use cache if available
+// - do sync module init, if async init was not called earlier
+// - allocate memory if not enougth
+// - can export functions to webassembly via "env_extra",
+// for example, { exp: Math.exp }
+//
+ MultiMath.prototype.__instance = function instance(name, memsize, env_extra) {
+ if (memsize) this.__reallocate(memsize);
+
+ // If .init() was not called, do sync compile
+ if (!this.__wasm[name]) {
+ var module = this.__modules[name];
+ this.__wasm[name] = new WebAssembly.Module(this.__base64decode(module.wasm_src));
+ }
+
+ if (!this.__cache[name]) {
+ var env_base = {
+ memoryBase: 0,
+ memory: this.__memory,
+ tableBase: 0,
+ table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
+ };
+
+ this.__cache[name] = new WebAssembly.Instance(this.__wasm[name], {
+ env: assign(env_base, env_extra || {})
+ });
+ }
+
+ return this.__cache[name];
+ };
+
+
+// Helper to calculate memory aligh for pointers. Webassembly does not require
+// this, but you may wish to experiment. Default base = 8;
+//
+ MultiMath.prototype.__align = function align(number, base) {
+ base = base || 8;
+ var reminder = number % base;
+ return number + (reminder ? base - reminder : 0);
+ };
+
+
+ module.exports = MultiMath;
+
+ },{"./lib/base64decode":17,"./lib/wa_detect":23,"object-assign":24}],17:[function(require,module,exports){
+// base64 decode str -> Uint8Array, to load WA modules
+//
+ 'use strict';
+
+
+ var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+
+ module.exports = function base64decode(str) {
+ var input = str.replace(/[\r\n=]/g, ''), // remove CR/LF & padding to simplify scan
+ max = input.length;
+
+ var out = new Uint8Array((max * 3) >> 2);
+
+ // Collect by 6*4 bits (3 bytes)
+
+ var bits = 0;
+ var ptr = 0;
+
+ for (var idx = 0; idx < max; idx++) {
+ if ((idx % 4 === 0) && idx) {
+ out[ptr++] = (bits >> 16) & 0xFF;
+ out[ptr++] = (bits >> 8) & 0xFF;
+ out[ptr++] = bits & 0xFF;
+ }
+
+ bits = (bits << 6) | BASE64_MAP.indexOf(input.charAt(idx));
+ }
+
+ // Dump tail
+
+ var tailbits = (max % 4) * 6;
+
+ if (tailbits === 0) {
+ out[ptr++] = (bits >> 16) & 0xFF;
+ out[ptr++] = (bits >> 8) & 0xFF;
+ out[ptr++] = bits & 0xFF;
+ } else if (tailbits === 18) {
+ out[ptr++] = (bits >> 10) & 0xFF;
+ out[ptr++] = (bits >> 2) & 0xFF;
+ } else if (tailbits === 12) {
+ out[ptr++] = (bits >> 4) & 0xFF;
+ }
+
+ return out;
+ };
+
+ },{}],18:[function(require,module,exports){
+// Calculates 16-bit precision HSL lightness from 8-bit rgba buffer
+//
+ 'use strict';
+
+
+ module.exports = function hsl_l16_js(img, width, height) {
+ var size = width * height;
+ var out = new Uint16Array(size);
+ var r, g, b, min, max;
+ for (var i = 0; i < size; i++) {
+ r = img[4 * i];
+ g = img[4 * i + 1];
+ b = img[4 * i + 2];
+ max = (r >= g && r >= b) ? r : (g >= b && g >= r) ? g : b;
+ min = (r <= g && r <= b) ? r : (g <= b && g <= r) ? g : b;
+ out[i] = (max + min) * 257 >> 1;
+ }
+ return out;
+ };
+
+ },{}],19:[function(require,module,exports){
+ 'use strict';
+
+ module.exports = {
+ name: 'unsharp_mask',
+ fn: require('./unsharp_mask'),
+ wasm_fn: require('./unsharp_mask_wasm'),
+ wasm_src: require('./unsharp_mask_wasm_base64')
+ };
+
+ },{"./unsharp_mask":20,"./unsharp_mask_wasm":21,"./unsharp_mask_wasm_base64":22}],20:[function(require,module,exports){
+// Unsharp mask filter
+//
+// http://stackoverflow.com/a/23322820/1031804
+// USM(O) = O + (2 * (Amount / 100) * (O - GB))
+// GB - gaussian blur.
+//
+// Image is converted from RGB to HSL, unsharp mask is applied to the
+// lightness channel and then image is converted back to RGB.
+//
+ 'use strict';
+
+
+ var glur_mono16 = require('glur/mono16');
+ var hsl_l16 = require('./hsl_l16');
+
+
+ module.exports = function unsharp(img, width, height, amount, radius, threshold) {
+ var r, g, b;
+ var h, s, l;
+ var min, max;
+ var m1, m2, hShifted;
+ var diff, iTimes4;
+
+ if (amount === 0 || radius < 0.5) {
+ return;
+ }
+ if (radius > 2.0) {
+ radius = 2.0;
+ }
+
+ var lightness = hsl_l16(img, width, height);
+
+ var blured = new Uint16Array(lightness); // copy, because blur modify src
+
+ glur_mono16(blured, width, height, radius);
+
+ var amountFp = (amount / 100 * 0x1000 + 0.5)|0;
+ var thresholdFp = (threshold * 257)|0;
+
+ var size = width * height;
+
+ /* eslint-disable indent */
+ for (var i = 0; i < size; i++) {
+ diff = 2 * (lightness[i] - blured[i]);
+
+ if (Math.abs(diff) >= thresholdFp) {
+ iTimes4 = i * 4;
+ r = img[iTimes4];
+ g = img[iTimes4 + 1];
+ b = img[iTimes4 + 2];
+
+ // convert RGB to HSL
+ // take RGB, 8-bit unsigned integer per each channel
+ // save HSL, H and L are 16-bit unsigned integers, S is 12-bit unsigned integer
+ // math is taken from here: http://www.easyrgb.com/index.php?X=MATH&H=18
+ // and adopted to be integer (fixed point in fact) for sake of performance
+ max = (r >= g && r >= b) ? r : (g >= r && g >= b) ? g : b; // min and max are in [0..0xff]
+ min = (r <= g && r <= b) ? r : (g <= r && g <= b) ? g : b;
+ l = (max + min) * 257 >> 1; // l is in [0..0xffff] that is caused by multiplication by 257
+
+ if (min === max) {
+ h = s = 0;
+ } else {
+ s = (l <= 0x7fff) ?
+ (((max - min) * 0xfff) / (max + min))|0 :
+ (((max - min) * 0xfff) / (2 * 0xff - max - min))|0; // s is in [0..0xfff]
+ // h could be less 0, it will be fixed in backward conversion to RGB, |h| <= 0xffff / 6
+ h = (r === max) ? (((g - b) * 0xffff) / (6 * (max - min)))|0
+ : (g === max) ? 0x5555 + ((((b - r) * 0xffff) / (6 * (max - min)))|0) // 0x5555 == 0xffff / 3
+ : 0xaaaa + ((((r - g) * 0xffff) / (6 * (max - min)))|0); // 0xaaaa == 0xffff * 2 / 3
+ }
+
+ // add unsharp mask mask to the lightness channel
+ l += (amountFp * diff + 0x800) >> 12;
+ if (l > 0xffff) {
+ l = 0xffff;
+ } else if (l < 0) {
+ l = 0;
+ }
+
+ // convert HSL back to RGB
+ // for information about math look above
+ if (s === 0) {
+ r = g = b = l >> 8;
+ } else {
+ m2 = (l <= 0x7fff) ? (l * (0x1000 + s) + 0x800) >> 12 :
+ l + (((0xffff - l) * s + 0x800) >> 12);
+ m1 = 2 * l - m2 >> 8;
+ m2 >>= 8;
+ // save result to RGB channels
+ // R channel
+ hShifted = (h + 0x5555) & 0xffff; // 0x5555 == 0xffff / 3
+ r = (hShifted >= 0xaaaa) ? m1 // 0xaaaa == 0xffff * 2 / 3
+ : (hShifted >= 0x7fff) ? m1 + ((m2 - m1) * 6 * (0xaaaa - hShifted) + 0x8000 >> 16)
+ : (hShifted >= 0x2aaa) ? m2 // 0x2aaa == 0xffff / 6
+ : m1 + ((m2 - m1) * 6 * hShifted + 0x8000 >> 16);
+ // G channel
+ hShifted = h & 0xffff;
+ g = (hShifted >= 0xaaaa) ? m1 // 0xaaaa == 0xffff * 2 / 3
+ : (hShifted >= 0x7fff) ? m1 + ((m2 - m1) * 6 * (0xaaaa - hShifted) + 0x8000 >> 16)
+ : (hShifted >= 0x2aaa) ? m2 // 0x2aaa == 0xffff / 6
+ : m1 + ((m2 - m1) * 6 * hShifted + 0x8000 >> 16);
+ // B channel
+ hShifted = (h - 0x5555) & 0xffff;
+ b = (hShifted >= 0xaaaa) ? m1 // 0xaaaa == 0xffff * 2 / 3
+ : (hShifted >= 0x7fff) ? m1 + ((m2 - m1) * 6 * (0xaaaa - hShifted) + 0x8000 >> 16)
+ : (hShifted >= 0x2aaa) ? m2 // 0x2aaa == 0xffff / 6
+ : m1 + ((m2 - m1) * 6 * hShifted + 0x8000 >> 16);
+ }
+
+ img[iTimes4] = r;
+ img[iTimes4 + 1] = g;
+ img[iTimes4 + 2] = b;
+ }
+ }
+ };
+
+ },{"./hsl_l16":18,"glur/mono16":14}],21:[function(require,module,exports){
+ 'use strict';
+
+
+ module.exports = function unsharp(img, width, height, amount, radius, threshold) {
+ if (amount === 0 || radius < 0.5) {
+ return;
+ }
+
+ if (radius > 2.0) {
+ radius = 2.0;
+ }
+
+ var pixels = width * height;
+
+ var img_bytes_cnt = pixels * 4;
+ var hsl_bytes_cnt = pixels * 2;
+ var blur_bytes_cnt = pixels * 2;
+ var blur_line_byte_cnt = Math.max(width, height) * 4; // float32 array
+ var blur_coeffs_byte_cnt = 8 * 4; // float32 array
+
+ var img_offset = 0;
+ var hsl_offset = img_bytes_cnt;
+ var blur_offset = hsl_offset + hsl_bytes_cnt;
+ var blur_tmp_offset = blur_offset + blur_bytes_cnt;
+ var blur_line_offset = blur_tmp_offset + blur_bytes_cnt;
+ var blur_coeffs_offset = blur_line_offset + blur_line_byte_cnt;
+
+ var instance = this.__instance(
+ 'unsharp_mask',
+ img_bytes_cnt + hsl_bytes_cnt + blur_bytes_cnt * 2 + blur_line_byte_cnt + blur_coeffs_byte_cnt,
+ { exp: Math.exp }
+ );
+
+ // 32-bit copy is much faster in chrome
+ var img32 = new Uint32Array(img.buffer);
+ var mem32 = new Uint32Array(this.__memory.buffer);
+ mem32.set(img32);
+
+ // HSL
+ var fn = instance.exports.hsl_l16 || instance.exports._hsl_l16;
+ fn(img_offset, hsl_offset, width, height);
+
+ // BLUR
+ fn = instance.exports.blurMono16 || instance.exports._blurMono16;
+ fn(hsl_offset, blur_offset, blur_tmp_offset,
+ blur_line_offset, blur_coeffs_offset, width, height, radius);
+
+ // UNSHARP
+ fn = instance.exports.unsharp || instance.exports._unsharp;
+ fn(img_offset, img_offset, hsl_offset,
+ blur_offset, width, height, amount, threshold);
+
+ // 32-bit copy is much faster in chrome
+ img32.set(new Uint32Array(this.__memory.buffer, 0, pixels));
+ };
+
+ },{}],22:[function(require,module,exports){
+// This is autogenerated file from math.wasm, don't edit.
+//
+ 'use strict';
+
+ /* eslint-disable max-len */
+ module.exports = 'AGFzbQEAAAABMQZgAXwBfGACfX8AYAZ/f39/f38AYAh/f39/f39/fQBgBH9/f38AYAh/f39/f39/fwACGQIDZW52A2V4cAAAA2VudgZtZW1vcnkCAAEDBgUBAgMEBQQEAXAAAAdMBRZfX2J1aWxkX2dhdXNzaWFuX2NvZWZzAAEOX19nYXVzczE2X2xpbmUAAgpibHVyTW9ubzE2AAMHaHNsX2wxNgAEB3Vuc2hhcnAABQkBAAqJEAXZAQEGfAJAIAFE24a6Q4Ia+z8gALujIgOaEAAiBCAEoCIGtjgCECABIANEAAAAAAAAAMCiEAAiBbaMOAIUIAFEAAAAAAAA8D8gBKEiAiACoiAEIAMgA6CiRAAAAAAAAPA/oCAFoaMiArY4AgAgASAEIANEAAAAAAAA8L+gIAKioiIHtjgCBCABIAQgA0QAAAAAAADwP6AgAqKiIgO2OAIIIAEgBSACoiIEtow4AgwgASACIAegIAVEAAAAAAAA8D8gBqGgIgKjtjgCGCABIAMgBKEgAqO2OAIcCwu3AwMDfwR9CHwCQCADKgIUIQkgAyoCECEKIAMqAgwhCyADKgIIIQwCQCAEQX9qIgdBAEgiCA0AIAIgAC8BALgiDSADKgIYu6IiDiAJuyIQoiAOIAq7IhGiIA0gAyoCBLsiEqIgAyoCALsiEyANoqCgoCIPtjgCACACQQRqIQIgAEECaiEAIAdFDQAgBCEGA0AgAiAOIBCiIA8iDiARoiANIBKiIBMgAC8BALgiDaKgoKAiD7Y4AgAgAkEEaiECIABBAmohACAGQX9qIgZBAUoNAAsLAkAgCA0AIAEgByAFbEEBdGogAEF+ai8BACIIuCINIAu7IhGiIA0gDLsiEqKgIA0gAyoCHLuiIg4gCrsiE6KgIA4gCbsiFKKgIg8gAkF8aioCALugqzsBACAHRQ0AIAJBeGohAiAAQXxqIQBBACAFQQF0ayEHIAEgBSAEQQF0QXxqbGohBgNAIAghAyAALwEAIQggBiANIBGiIAO4Ig0gEqKgIA8iECAToqAgDiAUoqAiDyACKgIAu6CrOwEAIAYgB2ohBiAAQX5qIQAgAkF8aiECIBAhDiAEQX9qIgRBAUoNAAsLCwvfAgIDfwZ8AkAgB0MAAAAAWw0AIARE24a6Q4Ia+z8gB0MAAAA/l7ujIgyaEAAiDSANoCIPtjgCECAEIAxEAAAAAAAAAMCiEAAiDraMOAIUIAREAAAAAAAA8D8gDaEiCyALoiANIAwgDKCiRAAAAAAAAPA/oCAOoaMiC7Y4AgAgBCANIAxEAAAAAAAA8L+gIAuioiIQtjgCBCAEIA0gDEQAAAAAAADwP6AgC6KiIgy2OAIIIAQgDiALoiINtow4AgwgBCALIBCgIA5EAAAAAAAA8D8gD6GgIgujtjgCGCAEIAwgDaEgC6O2OAIcIAYEQCAFQQF0IQogBiEJIAIhCANAIAAgCCADIAQgBSAGEAIgACAKaiEAIAhBAmohCCAJQX9qIgkNAAsLIAVFDQAgBkEBdCEIIAUhAANAIAIgASADIAQgBiAFEAIgAiAIaiECIAFBAmohASAAQX9qIgANAAsLC7wBAQV/IAMgAmwiAwRAQQAgA2shBgNAIAAoAgAiBEEIdiIHQf8BcSECAn8gBEH/AXEiAyAEQRB2IgRB/wFxIgVPBEAgAyIIIAMgAk8NARoLIAQgBCAHIAIgA0kbIAIgBUkbQf8BcQshCAJAIAMgAk0EQCADIAVNDQELIAQgByAEIAMgAk8bIAIgBUsbQf8BcSEDCyAAQQRqIQAgASADIAhqQYECbEEBdjsBACABQQJqIQEgBkEBaiIGDQALCwvTBgEKfwJAIAazQwAAgEWUQwAAyEKVu0QAAAAAAADgP6CqIQ0gBSAEbCILBEAgB0GBAmwhDgNAQQAgAi8BACADLwEAayIGQQF0IgdrIAcgBkEASBsgDk8EQCAAQQJqLQAAIQUCfyAALQAAIgYgAEEBai0AACIESSIJRQRAIAYiCCAGIAVPDQEaCyAFIAUgBCAEIAVJGyAGIARLGwshCAJ/IAYgBE0EQCAGIgogBiAFTQ0BGgsgBSAFIAQgBCAFSxsgCRsLIgogCGoiD0GBAmwiEEEBdiERQQAhDAJ/QQAiCSAIIApGDQAaIAggCmsiCUH/H2wgD0H+AyAIayAKayAQQYCABEkbbSEMIAYgCEYEQCAEIAVrQf//A2wgCUEGbG0MAQsgBSAGayAGIARrIAQgCEYiBhtB//8DbCAJQQZsbUHVqgFBqtUCIAYbagshCSARIAcgDWxBgBBqQQx1aiIGQQAgBkEAShsiBkH//wMgBkH//wNIGyEGAkACfwJAIAxB//8DcSIFBEAgBkH//wFKDQEgBUGAIGogBmxBgBBqQQx2DAILIAZBCHYiBiEFIAYhBAwCCyAFIAZB//8Dc2xBgBBqQQx2IAZqCyIFQQh2IQcgBkEBdCAFa0EIdiIGIQQCQCAJQdWqAWpB//8DcSIFQanVAksNACAFQf//AU8EQEGq1QIgBWsgByAGa2xBBmxBgIACakEQdiAGaiEEDAELIAchBCAFQanVAEsNACAFIAcgBmtsQQZsQYCAAmpBEHYgBmohBAsCfyAGIgUgCUH//wNxIghBqdUCSw0AGkGq1QIgCGsgByAGa2xBBmxBgIACakEQdiAGaiAIQf//AU8NABogByIFIAhBqdUASw0AGiAIIAcgBmtsQQZsQYCAAmpBEHYgBmoLIQUgCUGr1QJqQf//A3EiCEGp1QJLDQAgCEH//wFPBEBBqtUCIAhrIAcgBmtsQQZsQYCAAmpBEHYgBmohBgwBCyAIQanVAEsEQCAHIQYMAQsgCCAHIAZrbEEGbEGAgAJqQRB2IAZqIQYLIAEgBDoAACABQQFqIAU6AAAgAUECaiAGOgAACyADQQJqIQMgAkECaiECIABBBGohACABQQRqIQEgC0F/aiILDQALCwsL';
+
+ },{}],23:[function(require,module,exports){
+// Detect WebAssembly support.
+// - Check global WebAssembly object
+// - Try to load simple module (can be disabled via CSP)
+//
+ 'use strict';
+
+
+ var wa;
+
+
+ module.exports = function hasWebAssembly() {
+ // use cache if called before;
+ if (typeof wa !== 'undefined') return wa;
+
+ wa = false;
+
+ if (typeof WebAssembly === 'undefined') return wa;
+
+ // If WebAssenbly is disabled, code can throw on compile
+ try {
+ // https://github.com/brion/min-wasm-fail/blob/master/min-wasm-fail.in.js
+ // Additional check that WA internals are correct
+
+ /* eslint-disable comma-spacing, max-len */
+ var bin = new Uint8Array([ 0,97,115,109,1,0,0,0,1,6,1,96,1,127,1,127,3,2,1,0,5,3,1,0,1,7,8,1,4,116,101,115,116,0,0,10,16,1,14,0,32,0,65,1,54,2,0,32,0,40,2,0,11 ]);
+ var module = new WebAssembly.Module(bin);
+ var instance = new WebAssembly.Instance(module, {});
+
+ // test storing to and loading from a non-zero location via a parameter.
+ // Safari on iOS 11.2.5 returns 0 unexpectedly at non-zero locations
+ if (instance.exports.test(4) !== 0) wa = true;
+
+ return wa;
+ } catch (__) {}
+
+ return wa;
+ };
+
+ },{}],24:[function(require,module,exports){
+ /*
+ object-assign
+ (c) Sindre Sorhus
+ @license MIT
+ */
+
+ 'use strict';
+ /* eslint-disable no-unused-vars */
+ var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+ var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+ function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+ }
+
+ function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+ }
+
+ module.exports = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+ };
+
+ },{}],25:[function(require,module,exports){
+ var bundleFn = arguments[3];
+ var sources = arguments[4];
+ var cache = arguments[5];
+
+ var stringify = JSON.stringify;
+
+ module.exports = function (fn, options) {
+ var wkey;
+ var cacheKeys = Object.keys(cache);
+
+ for (var i = 0, l = cacheKeys.length; i < l; i++) {
+ var key = cacheKeys[i];
+ var exp = cache[key].exports;
+ // Using babel as a transpiler to use esmodule, the export will always
+ // be an object with the default export as a property of it. To ensure
+ // the existing api and babel esmodule exports are both supported we
+ // check for both
+ if (exp === fn || exp && exp.default === fn) {
+ wkey = key;
+ break;
+ }
+ }
+
+ if (!wkey) {
+ wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
+ var wcache = {};
+ for (var i = 0, l = cacheKeys.length; i < l; i++) {
+ var key = cacheKeys[i];
+ wcache[key] = key;
+ }
+ sources[wkey] = [
+ 'function(require,module,exports){' + fn + '(self); }',
+ wcache
+ ];
+ }
+ var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
+
+ var scache = {}; scache[wkey] = wkey;
+ sources[skey] = [
+ 'function(require,module,exports){' +
+ // try to call default if defined to also support babel esmodule exports
+ 'var f = require(' + stringify(wkey) + ');' +
+ '(f.default ? f.default : f)(self);' +
+ '}',
+ scache
+ ];
+
+ var workerSources = {};
+ resolveSources(skey);
+
+ function resolveSources(key) {
+ workerSources[key] = true;
+
+ for (var depPath in sources[key][1]) {
+ var depKey = sources[key][1][depPath];
+ if (!workerSources[depKey]) {
+ resolveSources(depKey);
+ }
+ }
+ }
+
+ var src = '(' + bundleFn + ')({'
+ + Object.keys(workerSources).map(function (key) {
+ return stringify(key) + ':['
+ + sources[key][0]
+ + ',' + stringify(sources[key][1]) + ']'
+ ;
+ }).join(',')
+ + '},{},[' + stringify(skey) + '])'
+ ;
+
+ var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
+
+ var blob = new Blob([src], { type: 'text/javascript' });
+ if (options && options.bare) { return blob; }
+ var workerUrl = URL.createObjectURL(blob);
+ var worker = new Worker(workerUrl);
+ worker.objectURL = workerUrl;
+ return worker;
+ };
+
+ },{}],"/":[function(require,module,exports){
+ 'use strict';
+
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
+
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
+
+ function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
+
+ var assign = require('object-assign');
+
+ var webworkify = require('webworkify');
+
+ var MathLib = require('./lib/mathlib');
+
+ var Pool = require('./lib/pool');
+
+ var utils = require('./lib/utils');
+
+ var worker = require('./lib/worker');
+
+ var createStages = require('./lib/stepper');
+
+ var createRegions = require('./lib/tiler'); // Deduplicate pools & limiters with the same configs
+// when user creates multiple pica instances.
+
+
+ var singletones = {};
+ var NEED_SAFARI_FIX = false;
+
+ try {
+ if (typeof navigator !== 'undefined' && navigator.userAgent) {
+ NEED_SAFARI_FIX = navigator.userAgent.indexOf('Safari') >= 0;
+ }
+ } catch (e) {}
+
+ var concurrency = 1;
+
+ if (typeof navigator !== 'undefined') {
+ concurrency = Math.min(navigator.hardwareConcurrency || 1, 4);
+ }
+
+ var DEFAULT_PICA_OPTS = {
+ tile: 1024,
+ concurrency: concurrency,
+ features: ['js', 'wasm', 'ww'],
+ idle: 2000
+ };
+ var DEFAULT_RESIZE_OPTS = {
+ quality: 3,
+ alpha: false,
+ unsharpAmount: 0,
+ unsharpRadius: 0.0,
+ unsharpThreshold: 0
+ };
+ var CAN_NEW_IMAGE_DATA;
+ var CAN_CREATE_IMAGE_BITMAP;
+
+ function workerFabric() {
+ return {
+ value: webworkify(worker),
+ destroy: function destroy() {
+ this.value.terminate();
+
+ if (typeof window !== 'undefined') {
+ var url = window.URL || window.webkitURL || window.mozURL || window.msURL;
+
+ if (url && url.revokeObjectURL && this.value.objectURL) {
+ url.revokeObjectURL(this.value.objectURL);
+ }
+ }
+ }
+ };
+ } ////////////////////////////////////////////////////////////////////////////////
+// API methods
+
+
+ function Pica(options) {
+ if (!(this instanceof Pica)) return new Pica(options);
+ this.options = assign({}, DEFAULT_PICA_OPTS, options || {});
+ var limiter_key = "lk_".concat(this.options.concurrency); // Share limiters to avoid multiple parallel workers when user creates
+ // multiple pica instances.
+
+ this.__limit = singletones[limiter_key] || utils.limiter(this.options.concurrency);
+ if (!singletones[limiter_key]) singletones[limiter_key] = this.__limit; // List of supported features, according to options & browser/node.js
+
+ this.features = {
+ js: false,
+ // pure JS implementation, can be disabled for testing
+ wasm: false,
+ // webassembly implementation for heavy functions
+ cib: false,
+ // resize via createImageBitmap (only FF at this moment)
+ ww: false // webworkers
+
+ };
+ this.__workersPool = null; // Store requested features for webworkers
+
+ this.__requested_features = [];
+ this.__mathlib = null;
+ }
+
+ Pica.prototype.init = function () {
+ var _this = this;
+
+ if (this.__initPromise) return this.__initPromise; // Test if we can create ImageData without canvas and memory copy
+
+ if (CAN_NEW_IMAGE_DATA !== false && CAN_NEW_IMAGE_DATA !== true) {
+ CAN_NEW_IMAGE_DATA = false;
+
+ if (typeof ImageData !== 'undefined' && typeof Uint8ClampedArray !== 'undefined') {
+ try {
+ /* eslint-disable no-new */
+ new ImageData(new Uint8ClampedArray(400), 10, 10);
+ CAN_NEW_IMAGE_DATA = true;
+ } catch (__) {}
+ }
+ } // ImageBitmap can be effective in 2 places:
+ //
+ // 1. Threaded jpeg unpack (basic)
+ // 2. Built-in resize (blocked due problem in chrome, see issue #89)
+ //
+ // For basic use we also need ImageBitmap wo support .close() method,
+ // see https://developer.mozilla.org/ru/docs/Web/API/ImageBitmap
+
+
+ if (CAN_CREATE_IMAGE_BITMAP !== false && CAN_CREATE_IMAGE_BITMAP !== true) {
+ CAN_CREATE_IMAGE_BITMAP = false;
+
+ if (typeof ImageBitmap !== 'undefined') {
+ if (ImageBitmap.prototype && ImageBitmap.prototype.close) {
+ CAN_CREATE_IMAGE_BITMAP = true;
+ } else {
+ this.debug('ImageBitmap does not support .close(), disabled');
+ }
+ }
+ }
+
+ var features = this.options.features.slice();
+
+ if (features.indexOf('all') >= 0) {
+ features = ['cib', 'wasm', 'js', 'ww'];
+ }
+
+ this.__requested_features = features;
+ this.__mathlib = new MathLib(features); // Check WebWorker support if requested
+
+ if (features.indexOf('ww') >= 0) {
+ if (typeof window !== 'undefined' && 'Worker' in window) {
+ // IE <= 11 don't allow to create webworkers from string. We should check it.
+ // https://connect.microsoft.com/IE/feedback/details/801810/web-workers-from-blob-urls-in-ie-10-and-11
+ try {
+ var wkr = require('webworkify')(function () {});
+
+ wkr.terminate();
+ this.features.ww = true; // pool uniqueness depends on pool config + webworker config
+
+ var wpool_key = "wp_".concat(JSON.stringify(this.options));
+
+ if (singletones[wpool_key]) {
+ this.__workersPool = singletones[wpool_key];
+ } else {
+ this.__workersPool = new Pool(workerFabric, this.options.idle);
+ singletones[wpool_key] = this.__workersPool;
+ }
+ } catch (__) {}
+ }
+ }
+
+ var initMath = this.__mathlib.init().then(function (mathlib) {
+ // Copy detected features
+ assign(_this.features, mathlib.features);
+ });
+
+ var checkCibResize;
+
+ if (!CAN_CREATE_IMAGE_BITMAP) {
+ checkCibResize = Promise.resolve(false);
+ } else {
+ checkCibResize = utils.cib_support().then(function (status) {
+ if (_this.features.cib && features.indexOf('cib') < 0) {
+ _this.debug('createImageBitmap() resize supported, but disabled by config');
+
+ return;
+ }
+
+ if (features.indexOf('cib') >= 0) _this.features.cib = status;
+ });
+ } // Init math lib. That's async because can load some
+
+
+ this.__initPromise = Promise.all([initMath, checkCibResize]).then(function () {
+ return _this;
+ });
+ return this.__initPromise;
+ };
+
+ Pica.prototype.resize = function (from, to, options) {
+ var _this2 = this;
+
+ this.debug('Start resize...');
+ var opts = assign({}, DEFAULT_RESIZE_OPTS);
+
+ if (!isNaN(options)) {
+ opts = assign(opts, {
+ quality: options
+ });
+ } else if (options) {
+ opts = assign(opts, options);
+ }
+
+ opts.toWidth = to.width;
+ opts.toHeight = to.height;
+ opts.width = from.naturalWidth || from.width;
+ opts.height = from.naturalHeight || from.height; // Prevent stepper from infinite loop
+
+ if (to.width === 0 || to.height === 0) {
+ return Promise.reject(new Error("Invalid output size: ".concat(to.width, "x").concat(to.height)));
+ }
+
+ if (opts.unsharpRadius > 2) opts.unsharpRadius = 2;
+ var canceled = false;
+ var cancelToken = null;
+
+ if (opts.cancelToken) {
+ // Wrap cancelToken to avoid successive resolve & set flag
+ cancelToken = opts.cancelToken.then(function (data) {
+ canceled = true;
+ throw data;
+ }, function (err) {
+ canceled = true;
+ throw err;
+ });
+ }
+
+ var DEST_TILE_BORDER = 3; // Max possible filter window size
+
+ var destTileBorder = Math.ceil(Math.max(DEST_TILE_BORDER, 2.5 * opts.unsharpRadius | 0));
+ return this.init().then(function () {
+ if (canceled) return cancelToken; // if createImageBitmap supports resize, just do it and return
+
+ if (_this2.features.cib) {
+ var toCtx = to.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+
+ _this2.debug('Resize via createImageBitmap()');
+
+ return createImageBitmap(from, {
+ resizeWidth: opts.toWidth,
+ resizeHeight: opts.toHeight,
+ resizeQuality: utils.cib_quality_name(opts.quality)
+ }).then(function (imageBitmap) {
+ if (canceled) return cancelToken; // if no unsharp - draw directly to output canvas
+
+ if (!opts.unsharpAmount) {
+ toCtx.drawImage(imageBitmap, 0, 0);
+ imageBitmap.close();
+ toCtx = null;
+
+ _this2.debug('Finished!');
+
+ return to;
+ }
+
+ _this2.debug('Unsharp result');
+
+ var tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = opts.toWidth;
+ tmpCanvas.height = opts.toHeight;
+ var tmpCtx = tmpCanvas.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+ tmpCtx.drawImage(imageBitmap, 0, 0);
+ imageBitmap.close();
+ var iData = tmpCtx.getImageData(0, 0, opts.toWidth, opts.toHeight);
+
+ _this2.__mathlib.unsharp(iData.data, opts.toWidth, opts.toHeight, opts.unsharpAmount, opts.unsharpRadius, opts.unsharpThreshold);
+
+ toCtx.putImageData(iData, 0, 0);
+ iData = tmpCtx = tmpCanvas = toCtx = null;
+
+ _this2.debug('Finished!');
+
+ return to;
+ });
+ } //
+ // No easy way, let's resize manually via arrays
+ //
+ // Share cache between calls:
+ //
+ // - wasm instance
+ // - wasm memory object
+ //
+
+
+ var cache = {}; // Call resizer in webworker or locally, depending on config
+
+ var invokeResize = function invokeResize(opts) {
+ return Promise.resolve().then(function () {
+ if (!_this2.features.ww) return _this2.__mathlib.resizeAndUnsharp(opts, cache);
+ return new Promise(function (resolve, reject) {
+ var w = _this2.__workersPool.acquire();
+
+ if (cancelToken) cancelToken.catch(function (err) {
+ return reject(err);
+ });
+
+ w.value.onmessage = function (ev) {
+ w.release();
+ if (ev.data.err) reject(ev.data.err);else resolve(ev.data.result);
+ };
+
+ w.value.postMessage({
+ opts: opts,
+ features: _this2.__requested_features,
+ preload: {
+ wasm_nodule: _this2.__mathlib.__
+ }
+ }, [opts.src.buffer]);
+ });
+ });
+ };
+
+ var tileAndResize = function tileAndResize(from, to, opts) {
+ var srcCtx;
+ var srcImageBitmap;
+ var toCtx;
+
+ var processTile = function processTile(tile) {
+ return _this2.__limit(function () {
+ if (canceled) return cancelToken;
+ var srcImageData; // Extract tile RGBA buffer, depending on input type
+
+ if (utils.isCanvas(from)) {
+ _this2.debug('Get tile pixel data'); // If input is Canvas - extract region data directly
+
+
+ srcImageData = srcCtx.getImageData(tile.x, tile.y, tile.width, tile.height);
+ } else {
+ // If input is Image or decoded to ImageBitmap,
+ // draw region to temporary canvas and extract data from it
+ //
+ // Note! Attempt to reuse this canvas causes significant slowdown in chrome
+ //
+ _this2.debug('Draw tile imageBitmap/image to temporary canvas');
+
+ var tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = tile.width;
+ tmpCanvas.height = tile.height;
+ var tmpCtx = tmpCanvas.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+ tmpCtx.globalCompositeOperation = 'copy';
+ tmpCtx.drawImage(srcImageBitmap || from, tile.x, tile.y, tile.width, tile.height, 0, 0, tile.width, tile.height);
+
+ _this2.debug('Get tile pixel data');
+
+ srcImageData = tmpCtx.getImageData(0, 0, tile.width, tile.height);
+ tmpCtx = tmpCanvas = null;
+ }
+
+ var o = {
+ src: srcImageData.data,
+ width: tile.width,
+ height: tile.height,
+ toWidth: tile.toWidth,
+ toHeight: tile.toHeight,
+ scaleX: tile.scaleX,
+ scaleY: tile.scaleY,
+ offsetX: tile.offsetX,
+ offsetY: tile.offsetY,
+ quality: opts.quality,
+ alpha: opts.alpha,
+ unsharpAmount: opts.unsharpAmount,
+ unsharpRadius: opts.unsharpRadius,
+ unsharpThreshold: opts.unsharpThreshold
+ };
+
+ _this2.debug('Invoke resize math');
+
+ return Promise.resolve().then(function () {
+ return invokeResize(o);
+ }).then(function (result) {
+ if (canceled) return cancelToken;
+ srcImageData = null;
+ var toImageData;
+
+ _this2.debug('Convert raw rgba tile result to ImageData');
+
+ if (CAN_NEW_IMAGE_DATA) {
+ // this branch is for modern browsers
+ // If `new ImageData()` & Uint8ClampedArray suported
+ toImageData = new ImageData(new Uint8ClampedArray(result), tile.toWidth, tile.toHeight);
+ } else {
+ // fallback for `node-canvas` and old browsers
+ // (IE11 has ImageData but does not support `new ImageData()`)
+ toImageData = toCtx.createImageData(tile.toWidth, tile.toHeight);
+
+ if (toImageData.data.set) {
+ toImageData.data.set(result);
+ } else {
+ // IE9 don't have `.set()`
+ for (var i = toImageData.data.length - 1; i >= 0; i--) {
+ toImageData.data[i] = result[i];
+ }
+ }
+ }
+
+ _this2.debug('Draw tile');
+
+ if (NEED_SAFARI_FIX) {
+ // Safari draws thin white stripes between tiles without this fix
+ toCtx.putImageData(toImageData, tile.toX, tile.toY, tile.toInnerX - tile.toX, tile.toInnerY - tile.toY, tile.toInnerWidth + 1e-5, tile.toInnerHeight + 1e-5);
+ } else {
+ toCtx.putImageData(toImageData, tile.toX, tile.toY, tile.toInnerX - tile.toX, tile.toInnerY - tile.toY, tile.toInnerWidth, tile.toInnerHeight);
+ }
+
+ return null;
+ });
+ });
+ }; // Need to normalize data source first. It can be canvas or image.
+ // If image - try to decode in background if possible
+
+
+ return Promise.resolve().then(function () {
+ toCtx = to.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+
+ if (utils.isCanvas(from)) {
+ srcCtx = from.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+ return null;
+ }
+
+ if (utils.isImage(from)) {
+ // try do decode image in background for faster next operations
+ if (!CAN_CREATE_IMAGE_BITMAP) return null;
+
+ _this2.debug('Decode image via createImageBitmap');
+
+ return createImageBitmap(from).then(function (imageBitmap) {
+ srcImageBitmap = imageBitmap;
+ });
+ }
+
+ throw new Error('".from" should be image or canvas');
+ }).then(function () {
+ if (canceled) return cancelToken;
+
+ _this2.debug('Calculate tiles'); //
+ // Here we are with "normalized" source,
+ // follow to tiling
+ //
+
+
+ var regions = createRegions({
+ width: opts.width,
+ height: opts.height,
+ srcTileSize: _this2.options.tile,
+ toWidth: opts.toWidth,
+ toHeight: opts.toHeight,
+ destTileBorder: destTileBorder
+ });
+ var jobs = regions.map(function (tile) {
+ return processTile(tile);
+ });
+
+ function cleanup() {
+ if (srcImageBitmap) {
+ srcImageBitmap.close();
+ srcImageBitmap = null;
+ }
+ }
+
+ _this2.debug('Process tiles');
+
+ return Promise.all(jobs).then(function () {
+ _this2.debug('Finished!');
+
+ cleanup();
+ return to;
+ }, function (err) {
+ cleanup();
+ throw err;
+ });
+ });
+ };
+
+ var processStages = function processStages(stages, from, to, opts) {
+ if (canceled) return cancelToken;
+
+ var _stages$shift = stages.shift(),
+ _stages$shift2 = _slicedToArray(_stages$shift, 2),
+ toWidth = _stages$shift2[0],
+ toHeight = _stages$shift2[1];
+
+ var isLastStage = stages.length === 0;
+ opts = assign({}, opts, {
+ toWidth: toWidth,
+ toHeight: toHeight,
+ // only use user-defined quality for the last stage,
+ // use simpler (Hamming) filter for the first stages where
+ // scale factor is large enough (more than 2-3)
+ quality: isLastStage ? opts.quality : Math.min(1, opts.quality)
+ });
+ var tmpCanvas;
+
+ if (!isLastStage) {
+ // create temporary canvas
+ tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = toWidth;
+ tmpCanvas.height = toHeight;
+ }
+
+ return tileAndResize(from, isLastStage ? to : tmpCanvas, opts).then(function () {
+ if (isLastStage) return to;
+ opts.width = toWidth;
+ opts.height = toHeight;
+ return processStages(stages, tmpCanvas, to, opts);
+ });
+ };
+
+ var stages = createStages(opts.width, opts.height, opts.toWidth, opts.toHeight, _this2.options.tile, destTileBorder);
+ return processStages(stages, from, to, opts);
+ });
+ }; // RGBA buffer resize
+//
+
+
+ Pica.prototype.resizeBuffer = function (options) {
+ var _this3 = this;
+
+ var opts = assign({}, DEFAULT_RESIZE_OPTS, options);
+ return this.init().then(function () {
+ return _this3.__mathlib.resizeAndUnsharp(opts);
+ });
+ };
+
+ Pica.prototype.toBlob = function (canvas, mimeType, quality) {
+ mimeType = mimeType || 'image/png';
+ return new Promise(function (resolve) {
+ if (canvas.toBlob) {
+ canvas.toBlob(function (blob) {
+ return resolve(blob);
+ }, mimeType, quality);
+ return;
+ } // Fallback for old browsers
+
+
+ var asString = atob(canvas.toDataURL(mimeType, quality).split(',')[1]);
+ var len = asString.length;
+ var asBuffer = new Uint8Array(len);
+
+ for (var i = 0; i < len; i++) {
+ asBuffer[i] = asString.charCodeAt(i);
+ }
+
+ resolve(new Blob([asBuffer], {
+ type: mimeType
+ }));
+ });
+ };
+
+ Pica.prototype.debug = function () {};
+
+ module.exports = Pica;
+
+ },{"./lib/mathlib":1,"./lib/pool":9,"./lib/stepper":10,"./lib/tiler":11,"./lib/utils":12,"./lib/worker":13,"object-assign":24,"webworkify":25}]},{},[])("/")
+});
+
+/**
+ * This module allows resizing and conversion of HTMLImageElements to Blob and File objects
+ *
+ * @author Maximilian Mader
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Image/Resizer
+ */
+define('WoltLabSuite/Core/Image/Resizer',[
+ 'WoltLabSuite/Core/FileUtil',
+ 'WoltLabSuite/Core/Image/ExifUtil',
+ 'Pica'
+], function(FileUtil, ExifUtil, Pica) {
+ "use strict";
+
+ var pica = new Pica({features: ['js', 'wasm', 'ww']});
+
+ /**
+ * @constructor
+ */
+ function ImageResizer() { }
+ ImageResizer.prototype = {
+ maxWidth: 800,
+ maxHeight: 600,
+ quality: 0.8,
+ fileType: 'image/jpeg',
+
+ /**
+ * Sets the default maximum width for this instance
+ *
+ * @param {Number} value the new default maximum width
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setMaxWidth: function (value) {
+ if (value == null) value = ImageResizer.prototype.maxWidth;
+
+ this.maxWidth = value;
+ return this;
+ },
+
+ /**
+ * Sets the default maximum height for this instance
+ *
+ * @param {Number} value the new default maximum height
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setMaxHeight: function (value) {
+ if (value == null) value = ImageResizer.prototype.maxHeight;
+
+ this.maxHeight = value;
+ return this;
+ },
+
+ /**
+ * Sets the default quality for this instance
+ *
+ * @param {Number} value the new default quality
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setQuality: function (value) {
+ if (value == null) value = ImageResizer.prototype.quality;
+
+ this.quality = value;
+ return this;
+ },
+
+ /**
+ * Sets the default file type for this instance
+ *
+ * @param {Number} value the new default file type
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setFileType: function (value) {
+ if (value == null) value = ImageResizer.prototype.fileType;
+
+ this.fileType = value;
+ return this;
+ },
+
+ /**
+ * Converts the given object of exif data and image data into a File.
+ *
+ * @param {Object{exif: Uint8Array|undefined, image: Canvas} data object containing exif data and image data
+ * @param {String} fileName the name of the returned file
+ * @param {String} [fileType] the type of the returned image
+ * @param {Number} [quality] quality setting, currently only effective for "image/jpeg"
+ * @returns {Promise<File>} the File object
+ */
+ saveFile: function (data, fileName, fileType, quality) {
+ fileType = fileType || this.fileType;
+ quality = quality || this.quality;
+
+ var basename = fileName.match(/(.+)(\..+?)$/);
+
+ return pica.toBlob(data.image, fileType, quality)
+ .then(function (blob) {
+ if (fileType === 'image/jpeg' && typeof data.exif !== 'undefined') {
+ return ExifUtil.setExifData(blob, data.exif);
+ }
+
+ return blob;
+ })
+ .then(function (blob) {
+ return FileUtil.blobToFile(blob, basename[1] + '_autoscaled');
+ });
+ },
+
+ /**
+ * Loads the given file into an image object and parses Exif information.
+ *
+ * @param {File} file the file to load
+ * @returns {Promise} resulting image data
+ */
+ loadFile: function (file) {
+ var exif = undefined;
+ if (file.type === 'image/jpeg') {
+ // Extract EXIF data
+ exif = ExifUtil.getExifBytesFromJpeg(file);
+ }
+
+ var loader = new Promise(function (resolve, reject) {
+ var reader = new FileReader();
+ var image = new Image();
+
+ reader.addEventListener('load', function () {
+ image.src = reader.result;
+ });
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ image.addEventListener('error', reject);
+
+ image.addEventListener('load', function () {
+ resolve(image);
+ });
+
+ reader.readAsDataURL(file);
+ });
+
+ return Promise.all([ exif, loader ])
+ .then(function (result) {
+ return { exif: result[0], image: result[1] };
+ });
+ },
+
+ /**
+ * Downscales an image given as File object.
+ *
+ * @param {Image} image the image to resize
+ * @param {Number} [maxWidth] maximum width
+ * @param {Number} [maxHeight] maximum height
+ * @param {Number} [quality] quality in percent
+ * @param {boolean} [force] whether to force scaling even if unneeded (thus re-encoding with a possibly smaller file size)
+ * @param {Promise} cancelPromise a Promise used to cancel pica's operation when it resolves
+ * @returns {Promise<Blob | undefined>} a Promise resolving with the resized image as a {Canvas} or undefined if no resizing happened
+ */
+ resize: function (image, maxWidth, maxHeight, quality, force, cancelPromise) {
+ maxWidth = maxWidth || this.maxWidth;
+ maxHeight = maxHeight || this.maxHeight;
+ quality = quality || this.quality;
+ force = force || false;
+
+ var canvas = document.createElement('canvas');
+
+ // Prevent upscaling
+ var newWidth = Math.min(maxWidth, image.width);
+ var newHeight = Math.min(maxHeight, image.height);
+
+ if (image.width <= newWidth && image.height <= newHeight && !force) {
+ return Promise.resolve(undefined);
+ }
+
+ // Keep image ratio
+ var ratio = Math.min(newWidth / image.width, newHeight / image.height);
+ canvas.width = Math.floor(image.width * ratio);
+ canvas.height = Math.floor(image.height * ratio);
+
+ // Map to Pica's quality
+ var resizeQuality = 1;
+ if (quality >= 0.8) {
+ resizeQuality = 3;
+ }
+ else if (quality >= 0.4) {
+ resizeQuality = 2;
+ }
+
+ var options = {
+ quality: resizeQuality,
+ cancelToken: cancelPromise,
+ alpha: true
+ };
+
+ return pica.resize(image, canvas, options);
+ }
+ };
+
+ return ImageResizer;
+});
+
+/**
+ * Dropdown language chooser.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language/Chooser
+ */
+define('WoltLabSuite/Core/Language/Chooser',['Dictionary', 'Language', 'Dom/Traverse', 'Dom/Util', 'ObjectMap', 'Ui/SimpleDropdown'], function(Dictionary, Language, DomTraverse, DomUtil, ObjectMap, UiSimpleDropdown) {
+ "use strict";
+
+ var _choosers = new Dictionary();
+ var _didInit = false;
+ var _forms = new ObjectMap();
+
+ var _callbackSubmit = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Language/Chooser
+ */
+ return {
+ /**
+ * Initializes a language chooser.
+ *
+ * @param {string} containerId input element container id
+ * @param {string} chooserId input element id
+ * @param {int} languageId selected language id
+ * @param {object<int, object<string, string>>} languages data of available languages
+ * @param {function} callback function called after a language is selected
+ * @param {boolean} allowEmptyValue true if no language may be selected
+ */
+ init: function(containerId, chooserId, languageId, languages, callback, allowEmptyValue) {
+ if (_choosers.has(chooserId)) {
+ return;
+ }
+
+ var container = elById(containerId);
+ if (container === null) {
+ throw new Error("Expected a valid container id, cannot find '" + chooserId + "'.");
+ }
+
+ var element = elById(chooserId);
+ if (element === null) {
+ element = elCreate('input');
+ elAttr(element, 'type', 'hidden');
+ elAttr(element, 'id', chooserId);
+ elAttr(element, 'name', chooserId);
+ elAttr(element, 'value', languageId);
+
+ container.appendChild(element);
+ }
+
+ this._initElement(chooserId, element, languageId, languages, callback, allowEmptyValue);
+ },
+
+ /**
+ * Caches common event listener callbacks.
+ */
+ _setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _callbackSubmit = this._submit.bind(this);
+ },
+
+ /**
+ * Sets up DOM and event listeners for a language chooser.
+ *
+ * @param {string} chooserId chooser id
+ * @param {Element} element chooser element
+ * @param {int} languageId selected language id
+ * @param {object<int, object<string, string>>} languages data of available languages
+ * @param {function} callback callback function invoked on selection change
+ * @param {boolean} allowEmptyValue true if no language may be selected
+ */
+ _initElement: function(chooserId, element, languageId, languages, callback, allowEmptyValue) {
+ var container;
+
+ if (element.parentNode.nodeName === 'DD') {
+ container = elCreate('div');
+ container.className = 'dropdown';
+
+ // language chooser is the first child so that descriptions and error messages
+ // are always shown below the language chooser
+ DomUtil.prepend(container, element.parentNode);
+ }
+ else {
+ container = element.parentNode;
+ container.classList.add('dropdown');
+ }
+
+ elHide(element);
+
+ var dropdownToggle = elCreate('a');
+ dropdownToggle.className = 'dropdownToggle dropdownIndicator boxFlag box24 inputPrefix' + (element.parentNode.nodeName === 'DD' ? ' button' : '');
+ container.appendChild(dropdownToggle);
+
+ var dropdownMenu = elCreate('ul');
+ dropdownMenu.className = 'dropdownMenu';
+ container.appendChild(dropdownMenu);
+
+ var callbackClick = (function(event) {
+ var languageId = ~~elData(event.currentTarget, 'language-id');
+
+ var activeItem = DomTraverse.childByClass(dropdownMenu, 'active');
+ if (activeItem !== null) activeItem.classList.remove('active');
+
+ if (languageId) event.currentTarget.classList.add('active');
+
+ this._select(chooserId, languageId, event.currentTarget);
+ }).bind(this);
+
+ // add language dropdown items
+ var link, img, listItem, span;
+ for (var availableLanguageId in languages) {
+ if (languages.hasOwnProperty(availableLanguageId)) {
+ var language = languages[availableLanguageId];
+
+ listItem = elCreate('li');
+ listItem.className = 'boxFlag';
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ elData(listItem, 'language-id', availableLanguageId);
+ if (language.languageCode !== undefined) elData(listItem, 'language-code', language.languageCode);
+ dropdownMenu.appendChild(listItem);
+
+ link = elCreate('a');
+ link.className = 'box24';
+ listItem.appendChild(link);
+
+ img = elCreate('img');
+ elAttr(img, 'src', language.iconPath);
+ elAttr(img, 'alt', '');
+ img.className = 'iconFlag';
+ link.appendChild(img);
+
+ span = elCreate('span');
+ span.textContent = language.languageName;
+ link.appendChild(span);
+
+ if (availableLanguageId == languageId) {
+ dropdownToggle.innerHTML = listItem.firstChild.innerHTML;
+ }
+ }
+ }
+
+ // add dropdown item for "no selection"
+ if (allowEmptyValue) {
+ listItem = elCreate('li');
+ listItem.className = 'dropdownDivider';
+ dropdownMenu.appendChild(listItem);
+
+ listItem = elCreate('li');
+ elData(listItem, 'language-id', 0);
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ dropdownMenu.appendChild(listItem);
+
+ link = elCreate('a');
+ link.textContent = Language.get('wcf.global.language.noSelection');
+ listItem.appendChild(link);
+
+ if (languageId === 0) {
+ dropdownToggle.innerHTML = listItem.firstChild.innerHTML;
+ }
+
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ }
+ else if (languageId === 0) {
+ dropdownToggle.innerHTML = null;
+
+ var div = elCreate('div');
+ dropdownToggle.appendChild(div);
+
+ span = elCreate('span');
+ span.className = 'icon icon24 fa-question';
+ div.appendChild(span);
+
+ span = elCreate('span');
+ span.textContent = Language.get('wcf.global.language.noSelection');
+ div.appendChild(span);
+ }
+
+ UiSimpleDropdown.init(dropdownToggle);
+
+ _choosers.set(chooserId, {
+ callback: callback,
+ dropdownMenu: dropdownMenu,
+ dropdownToggle: dropdownToggle,
+ element: element
+ });
+
+ // bind to submit event
+ var form = DomTraverse.parentByTag(element, 'FORM');
+ if (form !== null) {
+ form.addEventListener('submit', _callbackSubmit);
+
+ var chooserIds = _forms.get(form);
+ if (chooserIds === undefined) {
+ chooserIds = [];
+ _forms.set(form, chooserIds);
+ }
+
+ chooserIds.push(chooserId);
+ }
+ },
+
+ /**
+ * Selects a language from the dropdown list.
+ *
+ * @param {string} chooserId input element id
+ * @param {int} languageId language id or `0` to disable i18n
+ * @param {Element=} listItem selected list item
+ */
+ _select: function(chooserId, languageId, listItem) {
+ var chooser = _choosers.get(chooserId);
+
+ if (listItem === undefined) {
+ var listItems = chooser.dropdownMenu.childNodes;
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var _listItem = listItems[i];
+ if (~~elData(_listItem, 'language-id') === languageId) {
+ listItem = _listItem;
+ break;
+ }
+ }
+
+ if (listItem === undefined) {
+ throw new Error("Cannot select unknown language id '" + languageId + "'");
+ }
+ }
+
+ chooser.element.value = languageId;
+
+ chooser.dropdownToggle.innerHTML = listItem.firstChild.innerHTML;
+
+ _choosers.set(chooserId, chooser);
+
+ // execute callback
+ if (typeof chooser.callback === 'function') {
+ chooser.callback(listItem);
+ }
+ },
+
+ /**
+ * Inserts hidden fields for the language chooser value on submit.
+ *
+ * @param {object} event event object
+ */
+ _submit: function(event) {
+ var elementIds = _forms.get(event.currentTarget);
+
+ var input;
+ for (var i = 0, length = elementIds.length; i < length; i++) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = elementIds[i];
+ input.value = this.getLanguageId(elementIds[i]);
+
+ event.currentTarget.appendChild(input);
+ }
+ },
+
+ /**
+ * Returns the chooser for an input field.
+ *
+ * @param {string} chooserId input element id
+ * @return {Dictionary} data of the chooser
+ */
+ getChooser: function(chooserId) {
+ var chooser = _choosers.get(chooserId);
+ if (chooser === undefined) {
+ throw new Error("Expected a valid language chooser input element, '" + chooserId + "' is not i18n input field.");
+ }
+
+ return chooser;
+ },
+
+ /**
+ * Returns the selected language for a certain chooser.
+ *
+ * @param {string} chooserId input element id
+ * @return {int} chosen language id
+ */
+ getLanguageId: function(chooserId) {
+ return ~~this.getChooser(chooserId).element.value;
+ },
+
+ /**
+ * Removes the chooser with given id.
+ *
+ * @param {string} chooserId input element id
+ */
+ removeChooser: function(chooserId) {
+ if (_choosers.has(chooserId)) {
+ _choosers.delete(chooserId);
+ }
+ },
+
+ /**
+ * Sets the language for a certain chooser.
+ *
+ * @param {string} chooserId input element id
+ * @param {int} languageId language id to be set
+ */
+ setLanguageId: function(chooserId, languageId) {
+ if (_choosers.get(chooserId) === undefined) {
+ throw new Error("Expected a valid input element, '" + chooserId + "' is not i18n input field.");
+ }
+
+ this._select(chooserId, languageId);
+ }
+ };
+});
+
+/**
+ * I18n interface for input and textarea fields.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language/Input
+ */
+define('WoltLabSuite/Core/Language/Input',['Core', 'Dictionary', 'Language', 'ObjectMap', 'StringUtil', 'Dom/Traverse', 'Dom/Util', 'Ui/SimpleDropdown'], function(Core, Dictionary, Language, ObjectMap, StringUtil, DomTraverse, DomUtil, UiSimpleDropdown) {
+ "use strict";
+
+ var _elements = new Dictionary();
+ var _didInit = false;
+ var _forms = new ObjectMap();
+ var _values = new Dictionary();
+
+ var _callbackDropdownToggle = null;
+ var _callbackSubmit = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Language/Input
+ */
+ return {
+ /**
+ * Initializes an input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Object} values preset values per language id
+ * @param {Object} availableLanguages language names per language id
+ * @param {boolean} forceSelection require i18n input
+ */
+ init: function(elementId, values, availableLanguages, forceSelection) {
+ if (_values.has(elementId)) {
+ return;
+ }
+
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, cannot find '" + elementId + "'.");
+ }
+
+ this._setup();
+
+ // unescape values
+ var unescapedValues = new Dictionary();
+ for (var key in values) {
+ if (values.hasOwnProperty(key)) {
+ unescapedValues.set(~~key, StringUtil.unescapeHTML(values[key]));
+ }
+ }
+
+ _values.set(elementId, unescapedValues);
+
+ this._initElement(elementId, element, unescapedValues, availableLanguages, forceSelection);
+ },
+
+ /**
+ * Registers a callback for an element.
+ *
+ * @param {string} elementId
+ * @param {string} eventName
+ * @param {function} callback
+ */
+ registerCallback: function (elementId, eventName, callback) {
+ if (!_values.has(elementId)) {
+ throw new Error("Unknown element id '" + elementId + "'.");
+ }
+
+ _elements.get(elementId).callbacks.set(eventName, callback);
+ },
+
+ /**
+ * Unregisters the element with the given id.
+ *
+ * @param {string} elementId
+ * @since 5.2
+ */
+ unregister: function(elementId) {
+ if (!_values.has(elementId)) {
+ throw new Error("Unknown element id '" + elementId + "'.");
+ }
+
+ _values.delete(elementId);
+ _elements.delete(elementId);
+ },
+
+ /**
+ * Caches common event listener callbacks.
+ */
+ _setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _callbackDropdownToggle = this._dropdownToggle.bind(this);
+ _callbackSubmit = this._submit.bind(this);
+ },
+
+ /**
+ * Sets up DOM and event listeners for an input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Element} element input or textarea element
+ * @param {Dictionary} values preset values per language id
+ * @param {Object} availableLanguages language names per language id
+ * @param {boolean} forceSelection require i18n input
+ */
+ _initElement: function(elementId, element, values, availableLanguages, forceSelection) {
+ var container = element.parentNode;
+ if (!container.classList.contains('inputAddon')) {
+ container = elCreate('div');
+ container.className = 'inputAddon' + (element.nodeName === 'TEXTAREA' ? ' inputAddonTextarea' : '');
+ //noinspection JSCheckFunctionSignatures
+ elData(container, 'input-id', elementId);
+
+ var hasFocus = document.activeElement === element;
+
+ // DOM manipulation causes focused element to lose focus
+ element.parentNode.insertBefore(container, element);
+ container.appendChild(element);
+
+ if (hasFocus) {
+ element.focus();
+ }
+ }
+
+ container.classList.add('dropdown');
+ var button = elCreate('span');
+ button.className = 'button dropdownToggle inputPrefix';
+
+ var span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.disabledI18n');
+
+ button.appendChild(span);
+ container.insertBefore(button, element);
+
+ var dropdownMenu = elCreate('ul');
+ dropdownMenu.className = 'dropdownMenu';
+ DomUtil.insertAfter(dropdownMenu, button);
+
+ var callbackClick = (function(event, isInit) {
+ var languageId = ~~elData(event.currentTarget, 'language-id');
+
+ var activeItem = DomTraverse.childByClass(dropdownMenu, 'active');
+ if (activeItem !== null) activeItem.classList.remove('active');
+
+ if (languageId) event.currentTarget.classList.add('active');
+
+ this._select(elementId, languageId, isInit || false);
+ }).bind(this);
+
+ // build language dropdown
+ var listItem;
+ for (var languageId in availableLanguages) {
+ if (availableLanguages.hasOwnProperty(languageId)) {
+ listItem = elCreate('li');
+ elData(listItem, 'language-id', languageId);
+
+ span = elCreate('span');
+ span.textContent = availableLanguages[languageId];
+
+ listItem.appendChild(span);
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ dropdownMenu.appendChild(listItem);
+ }
+ }
+
+ if (forceSelection !== true) {
+ listItem = elCreate('li');
+ listItem.className = 'dropdownDivider';
+ dropdownMenu.appendChild(listItem);
+
+ listItem = elCreate('li');
+ elData(listItem, 'language-id', 0);
+ span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.disabledI18n');
+ listItem.appendChild(span);
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ dropdownMenu.appendChild(listItem);
+ }
+
+ var activeItem = null;
+ if (forceSelection === true || values.size) {
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ if (~~elData(dropdownMenu.children[i], 'language-id') === LANGUAGE_ID) {
+ activeItem = dropdownMenu.children[i];
+ break;
+ }
+ }
+ }
+
+ UiSimpleDropdown.init(button);
+ UiSimpleDropdown.registerCallback(container.id, _callbackDropdownToggle);
+
+ _elements.set(elementId, {
+ buttonLabel: button.children[0],
+ callbacks: new Dictionary(),
+ element: element,
+ languageId: 0,
+ isEnabled: true,
+ forceSelection: forceSelection
+ });
+
+ // bind to submit event
+ var submit = DomTraverse.parentByTag(element, 'FORM');
+ if (submit !== null) {
+ submit.addEventListener('submit', _callbackSubmit);
+
+ var elementIds = _forms.get(submit);
+ if (elementIds === undefined) {
+ elementIds = [];
+ _forms.set(submit, elementIds);
+ }
+
+ elementIds.push(elementId);
+ }
+
+ if (activeItem !== null) {
+ callbackClick({ currentTarget: activeItem }, true);
+ }
+ },
+
+ /**
+ * Selects a language or non-i18n from the dropdown list.
+ *
+ * @param {string} elementId input element id
+ * @param {int} languageId language id or `0` to disable i18n
+ * @param {boolean} isInit triggers pre-selection on init
+ */
+ _select: function(elementId, languageId, isInit) {
+ var data = _elements.get(elementId);
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(data.element.closest('.inputAddon').id);
+ var item, label = '';
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ item = dropdownMenu.children[i];
+
+ var itemLanguageId = elData(item, 'language-id');
+ if (itemLanguageId.length && languageId === ~~itemLanguageId) {
+ label = item.children[0].textContent;
+ }
+ }
+
+ // save current value
+ if (data.languageId !== languageId) {
+ var values = _values.get(elementId);
+
+ if (data.languageId) {
+ values.set(data.languageId, data.element.value);
+ }
+
+ if (languageId === 0) {
+ _values.set(elementId, new Dictionary());
+ }
+ else if (data.buttonLabel.classList.contains('active') || isInit === true) {
+ data.element.value = (values.has(languageId)) ? values.get(languageId) : '';
+ }
+
+ // update label
+ data.buttonLabel.textContent = label;
+ data.buttonLabel.classList[(languageId ? 'add' : 'remove')]('active');
+
+ data.languageId = languageId;
+ }
+
+ if (!isInit) {
+ data.element.blur();
+ data.element.focus();
+ }
+
+ if (data.callbacks.has('select')) {
+ data.callbacks.get('select')(data.element);
+ }
+ },
+
+ /**
+ * Callback for dropdowns being opened, flags items with a missing value for one or more languages.
+ *
+ * @param {string} containerId dropdown container id
+ * @param {string} action toggle action, can be `open` or `close`
+ */
+ _dropdownToggle: function(containerId, action) {
+ if (action !== 'open') {
+ return;
+ }
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(containerId);
+ var elementId = elData(elById(containerId), 'input-id');
+ var data = _elements.get(elementId);
+ var values = _values.get(elementId);
+
+ var item, languageId;
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ item = dropdownMenu.children[i];
+ languageId = ~~elData(item, 'language-id');
+
+ if (languageId) {
+ var hasMissingValue = false;
+ if (data.languageId) {
+ if (languageId === data.languageId) {
+ hasMissingValue = (data.element.value.trim() === '');
+ }
+ else {
+ hasMissingValue = (!values.get(languageId));
+ }
+ }
+
+ item.classList[(hasMissingValue ? 'add' : 'remove')]('missingValue');
+ }
+ }
+ },
+
+ /**
+ * Inserts hidden fields for i18n input on submit.
+ *
+ * @param {Object} event event object
+ */
+ _submit: function(event) {
+ var elementIds = _forms.get(event.currentTarget);
+
+ var data, elementId, input, values;
+ for (var i = 0, length = elementIds.length; i < length; i++) {
+ elementId = elementIds[i];
+ data = _elements.get(elementId);
+ if (data.isEnabled) {
+ values = _values.get(elementId);
+
+ if (data.callbacks.has('submit')) {
+ data.callbacks.get('submit')(data.element);
+ }
+
+ // update with current value
+ if (data.languageId) {
+ values.set(data.languageId, data.element.value);
+ }
+
+ if (values.size) {
+ values.forEach(function(value, languageId) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = elementId + '_i18n[' + languageId + ']';
+ input.value = value;
+
+ event.currentTarget.appendChild(input);
+ });
+
+ // remove name attribute to enforce i18n values
+ data.element.removeAttribute('name');
+ }
+ }
+ }
+ },
+
+ /**
+ * Returns the values of an input field.
+ *
+ * @param {string} elementId input element id
+ * @return {Dictionary} values stored for the different languages
+ */
+ getValues: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ var values = _values.get(elementId);
+
+ // update with current value
+ values.set(element.languageId, element.element.value);
+
+ return values;
+ },
+
+ /**
+ * Sets the values of an input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Dictionary} values values for the different languages
+ */
+ setValues: function(elementId, values) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ if (Core.isPlainObject(values)) {
+ values = Dictionary.fromObject(values);
+ }
+
+ element.element.value = '';
+
+ if (values.has(0)) {
+ element.element.value = values.get(0);
+ values['delete'](0);
+ _values.set(elementId, values);
+ this._select(elementId, 0, true);
+ return;
+ }
+
+ _values.set(elementId, values);
+
+ element.languageId = 0;
+ //noinspection JSUnresolvedVariable
+ this._select(elementId, LANGUAGE_ID, true);
+ },
+
+ /**
+ * Disables the i18n interface for an input field.
+ *
+ * @param {string} elementId input element id
+ */
+ disable: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid element, '" + elementId + "' is not an i18n input field.");
+ }
+
+ if (!element.isEnabled) return;
+
+ element.isEnabled = false;
+
+ // hide language dropdown
+ //noinspection JSCheckFunctionSignatures
+ elHide(element.buttonLabel.parentNode);
+ var dropdownContainer = element.buttonLabel.parentNode.parentNode;
+ dropdownContainer.classList.remove('inputAddon');
+ dropdownContainer.classList.remove('dropdown');
+ },
+
+ /**
+ * Enables the i18n interface for an input field.
+ *
+ * @param {string} elementId input element id
+ */
+ enable: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ if (element.isEnabled) return;
+
+ element.isEnabled = true;
+
+ // show language dropdown
+ //noinspection JSCheckFunctionSignatures
+ elShow(element.buttonLabel.parentNode);
+ var dropdownContainer = element.buttonLabel.parentNode.parentNode;
+ dropdownContainer.classList.add('inputAddon');
+ dropdownContainer.classList.add('dropdown');
+ },
+
+ /**
+ * Returns true if i18n input is enabled for an input field.
+ *
+ * @param {string} elementId input element id
+ * @return {boolean}
+ */
+ isEnabled: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ return element.isEnabled;
+ },
+
+ /**
+ * Returns true if the value of an i18n input field is valid.
+ *
+ * If the element is disabled, true is returned.
+ *
+ * @param {string} elementId input element id
+ * @param {boolean} permitEmptyValue if true, input may be empty for all languages
+ * @return {boolean} true if input is valid
+ */
+ validate: function(elementId, permitEmptyValue) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ if (!element.isEnabled) return true;
+
+ var values = _values.get(elementId);
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(element.element.parentNode.id);
+
+ if (element.languageId) {
+ values.set(element.languageId, element.element.value);
+ }
+
+ var item, languageId;
+ var hasEmptyValue = false, hasNonEmptyValue = false;
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ item = dropdownMenu.children[i];
+ languageId = ~~elData(item, 'language-id');
+
+ if (languageId) {
+ if (!values.has(languageId) || values.get(languageId).length === 0) {
+ // input has non-empty value for previously checked language
+ if (hasNonEmptyValue) {
+ return false;
+ }
+
+ hasEmptyValue = true;
+ }
+ else {
+ // input has empty value for previously checked language
+ if (hasEmptyValue) {
+ return false;
+ }
+
+ hasNonEmptyValue = true;
+ }
+ }
+ }
+
+ return (!hasEmptyValue || permitEmptyValue);
+ }
+ };
+});
+
+/**
+ * I18n interface for wysiwyg input fields.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language/Text
+ */
+define('WoltLabSuite/Core/Language/Text',['Core', './Input'], function (Core, LanguageInput) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Language/Text
+ */
+ return {
+ /**
+ * Initializes an WYSIWYG input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Object} values preset values per language id
+ * @param {Object} availableLanguages language names per language id
+ * @param {boolean} forceSelection require i18n input
+ */
+ init: function(elementId, values, availableLanguages, forceSelection) {
+ var element = elById(elementId);
+ if (!element || element.nodeName !== 'TEXTAREA' || !element.classList.contains('wysiwygTextarea')) {
+ throw new Error("Expected <textarea class=\"wysiwygTextarea\" /> for id '" + elementId + "'.");
+ }
+
+ LanguageInput.init(elementId, values, availableLanguages, forceSelection);
+
+ //noinspection JSUnresolvedFunction
+ LanguageInput.registerCallback(elementId, 'select', this._callbackSelect.bind(this));
+ //noinspection JSUnresolvedFunction
+ LanguageInput.registerCallback(elementId, 'submit', this._callbackSubmit.bind(this));
+ },
+
+ /**
+ * Refreshes the editor content on language switch.
+ *
+ * @param {Element} element input element
+ * @protected
+ */
+ _callbackSelect: function (element) {
+ if (window.jQuery !== undefined) {
+ window.jQuery(element).redactor('code.set', element.value);
+ }
+ },
+
+ /**
+ * Refreshes the input element value on submit.
+ *
+ * @param {Element} element input element
+ * @protected
+ */
+ _callbackSubmit: function (element) {
+ if (window.jQuery !== undefined) {
+ element.value = window.jQuery(element).redactor('code.get');
+ }
+ }
+ }
+});
+
+/**
+ * Handles editing media files via dialog.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Editor
+ */
+define(
+ 'WoltLabSuite/Core/Media/Editor',[
+ 'Ajax', 'Core', 'Dictionary', 'Dom/ChangeListener',
+ 'Dom/Traverse', 'Language', 'Ui/Dialog', 'Ui/Notification',
+ 'WoltLabSuite/Core/Language/Chooser', 'WoltLabSuite/Core/Language/Input', 'EventKey'
+ ],
+ function(
+ Ajax, Core, Dictionary, DomChangeListener,
+ DomTraverse, Language, UiDialog, UiNotification,
+ LanguageChooser, LanguageInput, EventKey
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _close: function() {},
+ _keyPress: function() {},
+ _saveData: function() {},
+ _updateLanguageFields: function() {},
+ edit: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaEditor(callbackObject) {
+ this._callbackObject = callbackObject || {};
+
+ if (this._callbackObject._editorClose && typeof this._callbackObject._editorClose !== 'function') {
+ throw new TypeError("Callback object has no function '_editorClose'.");
+ }
+ if (this._callbackObject._editorSuccess && typeof this._callbackObject._editorSuccess !== 'function') {
+ throw new TypeError("Callback object has no function '_editorSuccess'.");
+ }
+
+ this._media = null;
+ this._availableLanguageCount = 1;
+ this._categoryIds = [];
+ this._oldCategoryId = 0;
+
+ this._dialogs = new Dictionary();
+ }
+ MediaEditor.prototype = {
+ /**
+ * Returns the data for Ajax to setup the Ajax/Request object.
+ *
+ * @return {object} setup data for Ajax/Request object
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'update',
+ className: 'wcf\\data\\media\\MediaAction'
+ }
+ };
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ UiNotification.show();
+
+ if (this._callbackObject._editorSuccess) {
+ this._callbackObject._editorSuccess(this._media, this._oldCategoryId);
+ this._oldCategoryId = 0;
+ }
+
+ UiDialog.close('mediaEditor_' + this._media.mediaID);
+
+ this._media = null;
+ },
+
+ /**
+ * Is called if an editor is manually closed by the user.
+ */
+ _close: function() {
+ this._media = null;
+
+ if (this._callbackObject._editorClose) {
+ this._callbackObject._editorClose();
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` key to submit the form.
+ *
+ * @param {object} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ this._saveData();
+ }
+ },
+
+ /**
+ * Saves the data of the currently edited media.
+ */
+ _saveData: function() {
+ var content = UiDialog.getDialog('mediaEditor_' + this._media.mediaID).content;
+
+ var categoryId = elBySel('select[name=categoryID]', content);
+ var altText = elBySel('input[name=altText]', content);
+ var caption = elBySel('textarea[name=caption]', content);
+ var captionEnableHtml = elBySel('input[name=captionEnableHtml]', content);
+ var title = elBySel('input[name=title]', content);
+
+ var hasError = false;
+ var altTextError = (altText ? DomTraverse.childByClass(altText.parentNode.parentNode, 'innerError') : false);
+ var captionError = (caption ? DomTraverse.childByClass(caption.parentNode.parentNode, 'innerError') : false);
+ var titleError = DomTraverse.childByClass(title.parentNode.parentNode, 'innerError');
+
+ // category
+ this._oldCategoryId = this._media.categoryID;
+ if (this._categoryIds.length) {
+ this._media.categoryID = ~~categoryId.value;
+
+ // if the selected category id not valid (manipulated DOM), ignore
+ if (this._categoryIds.indexOf(this._media.categoryID) === -1) {
+ this._media.categoryID = 0;
+ }
+ }
+
+ // language and multilingualism
+ if (this._availableLanguageCount > 1) {
+ this._media.isMultilingual = ~~elBySel('input[name=isMultilingual]', content).checked;
+ this._media.languageID = this._media.isMultilingual ? null : LanguageChooser.getLanguageId('mediaEditor_' + this._media.mediaID + '_languageID');
+ }
+ else {
+ this._media.languageID = LANGUAGE_ID;
+ }
+
+ // altText, caption and title
+ this._media.altText = {};
+ this._media.caption = {};
+ this._media.title = {};
+ if (this._availableLanguageCount > 1 && this._media.isMultilingual) {
+ if (elById('altText_' + this._media.mediaID) && !LanguageInput.validate('altText_' + this._media.mediaID, true)) {
+ hasError = true;
+ if (!altTextError) {
+ var error = elCreate('small');
+ error.className = 'innerError';
+ error.textContent = Language.get('wcf.global.form.error.multilingual');
+ altText.parentNode.parentNode.appendChild(error);
+ }
+ }
+ if (elById('caption_' + this._media.mediaID) && !LanguageInput.validate('caption_' + this._media.mediaID, true)) {
+ hasError = true;
+ if (!captionError) {
+ var error = elCreate('small');
+ error.className = 'innerError';
+ error.textContent = Language.get('wcf.global.form.error.multilingual');
+ caption.parentNode.parentNode.appendChild(error);
+ }
+ }
+ if (!LanguageInput.validate('title_' + this._media.mediaID, true)) {
+ hasError = true;
+ if (!titleError) {
+ var error = elCreate('small');
+ error.className = 'innerError';
+ error.textContent = Language.get('wcf.global.form.error.multilingual');
+ title.parentNode.parentNode.appendChild(error);
+ }
+ }
+
+ this._media.altText = (elById('altText_' + this._media.mediaID) ? LanguageInput.getValues('altText_' + this._media.mediaID).toObject() : '');
+ this._media.caption = (elById('caption_' + this._media.mediaID) ? LanguageInput.getValues('caption_' + this._media.mediaID).toObject() : '');
+ this._media.title = LanguageInput.getValues('title_' + this._media.mediaID).toObject();
+ }
+ else {
+ this._media.altText[this._media.languageID] = (altText ? altText.value : '');
+ this._media.caption[this._media.languageID] = (caption ? caption.value : '');
+ this._media.title[this._media.languageID] = title.value;
+ }
+
+ // captionEnableHtml
+ this._media.captionEnableHtml = ~~elBySel('input[name=captionEnableHtml]', content).checked;
+
+ var aclValues = {
+ allowAll: ~~elById('mediaEditor_' + this._media.mediaID + '_aclAllowAll').checked,
+ group: [],
+ user: []
+ };
+
+ var aclGroups = elBySelAll('input[name="aclValues[group][]"]', content);
+ for (var i = 0, length = aclGroups.length; i < length; i++) {
+ aclValues.group.push(~~aclGroups[i].value);
+ }
+
+ var aclUsers = elBySelAll('input[name="aclValues[user][]"]', content);
+ for (var i = 0, length = aclUsers.length; i < length; i++) {
+ aclValues.user.push(~~aclUsers[i].value);
+ }
+
+ if (!hasError) {
+ if (altTextError) elRemove(altTextError);
+ if (captionError) elRemove(captionError);
+ if (titleError) elRemove(titleError);
+
+ Ajax.api(this, {
+ actionName: 'update',
+ objectIDs: [ this._media.mediaID ],
+ parameters: {
+ aclValues: aclValues,
+ altText: this._media.altText,
+ caption: this._media.caption,
+ data: {
+ captionEnableHtml: this._media.captionEnableHtml,
+ categoryID: this._media.categoryID,
+ isMultilingual: this._media.isMultilingual,
+ languageID: this._media.languageID
+ },
+ title: this._media.title
+ }
+ });
+ }
+ },
+
+ /**
+ * Updates language-related input fields depending on whether multilingualism
+ * is enabled.
+ */
+ _updateLanguageFields: function(event, element) {
+ if (event) element = event.currentTarget;
+
+ var languageChooserContainer = elById('mediaEditor_' + this._media.mediaID + '_languageIDContainer').parentNode;
+
+ if (element.checked) {
+ LanguageInput.enable('title_' + this._media.mediaID);
+ if (elById('caption_' + this._media.mediaID)) LanguageInput.enable('caption_' + this._media.mediaID);
+ if (elById('altText_' + this._media.mediaID)) LanguageInput.enable('altText_' + this._media.mediaID);
+
+ elHide(languageChooserContainer);
+ }
+ else {
+ LanguageInput.disable('title_' + this._media.mediaID);
+ if (elById('caption_' + this._media.mediaID)) LanguageInput.disable('caption_' + this._media.mediaID);
+ if (elById('altText_' + this._media.mediaID)) LanguageInput.disable('altText_' + this._media.mediaID);
+
+ elShow(languageChooserContainer);
+ }
+ },
+
+ /**
+ * Edits the media with the given data.
+ *
+ * @param {object|integer} media data of the edited media or media id for which the data will be loaded
+ */
+ edit: function(media) {
+ if (typeof media !== 'object') {
+ media = {
+ mediaID: ~~media
+ };
+ }
+
+ if (this._media !== null) {
+ throw new Error("Cannot edit media with id '" + media.mediaID + "' while editing media with id '" + this._media.mediaID + "'");
+ }
+
+ this._media = media;
+
+ if (!this._dialogs.has('mediaEditor_' + media.mediaID)) {
+ this._dialogs.set('mediaEditor_' + media.mediaID, {
+ _dialogSetup: function() {
+ return {
+ id: 'mediaEditor_' + media.mediaID,
+ options: {
+ backdropCloseOnClick: false,
+ onClose: this._close.bind(this),
+ title: Language.get('wcf.media.edit')
+ },
+ source: {
+ after: (function(content, data) {
+ this._availableLanguageCount = ~~data.returnValues.availableLanguageCount;
+ this._categoryIds = data.returnValues.categoryIDs.map(function(number) {
+ return ~~number;
+ });
+
+ var didLoadMediaData = false;
+ if (data.returnValues.mediaData) {
+ this._media = data.returnValues.mediaData;
+
+ didLoadMediaData = true;
+ }
+
+ // make sure that the language chooser is initialized first
+ setTimeout(function() {
+ if (this._availableLanguageCount > 1) {
+ LanguageChooser.setLanguageId('mediaEditor_' + this._media.mediaID + '_languageID', this._media.languageID || LANGUAGE_ID);
+ }
+
+ if (this._categoryIds.length) {
+ elBySel('select[name=categoryID]', content).value = ~~this._media.categoryID;
+ }
+
+ var title = elBySel('input[name=title]', content);
+ var altText = elBySel('input[name=altText]', content);
+ var caption = elBySel('textarea[name=caption]', content);
+
+ if (this._availableLanguageCount > 1 && this._media.isMultilingual) {
+ if (elById('altText_' + this._media.mediaID)) LanguageInput.setValues('altText_' + this._media.mediaID, Dictionary.fromObject(this._media.altText || { }));
+ if (elById('caption_' + this._media.mediaID)) LanguageInput.setValues('caption_' + this._media.mediaID, Dictionary.fromObject(this._media.caption || { }));
+ LanguageInput.setValues('title_' + this._media.mediaID, Dictionary.fromObject(this._media.title || { }));
+ }
+ else {
+ title.value = this._media.title ? this._media.title[this._media.languageID || LANGUAGE_ID] : '';
+ if (altText) altText.value = this._media.altText ? this._media.altText[this._media.languageID || LANGUAGE_ID] : '';
+ if (caption) caption.value = this._media.caption ? this._media.caption[this._media.languageID || LANGUAGE_ID] : '';
+ }
+
+ if (this._availableLanguageCount > 1) {
+ var isMultilingual = elBySel('input[name=isMultilingual]', content);
+ isMultilingual.addEventListener('change', this._updateLanguageFields.bind(this));
+
+ this._updateLanguageFields(null, isMultilingual);
+ }
+
+ var keyPress = this._keyPress.bind(this);
+ if (altText) altText.addEventListener('keypress', keyPress);
+ title.addEventListener('keypress', keyPress);
+
+ elBySel('button[data-type=submit]', content).addEventListener(WCF_CLICK_EVENT, this._saveData.bind(this));
+
+ // remove focus from input elements and scroll dialog to top
+ document.activeElement.blur();
+ elById('mediaEditor_' + this._media.mediaID).parentNode.scrollTop = 0;
+
+ DomChangeListener.trigger();
+ }.bind(this), 200);
+ }).bind(this),
+ data: {
+ actionName: 'getEditorDialog',
+ className: 'wcf\\data\\media\\MediaAction',
+ objectIDs: [media.mediaID]
+ }
+ }
+ };
+ }.bind(this)
+ });
+ }
+
+ UiDialog.open(this._dialogs.get('mediaEditor_' + media.mediaID));
+ }
+ };
+
+ return MediaEditor;
+});
+
+/**
+ * Uploads media files.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Upload
+ */
+define(
+ 'WoltLabSuite/Core/Media/Upload',[
+ 'Core',
+ 'DateUtil',
+ 'Dom/ChangeListener',
+ 'Dom/Traverse',
+ 'Dom/Util',
+ 'EventHandler',
+ 'Language',
+ 'Permission',
+ 'Upload',
+ 'User',
+ 'WoltLabSuite/Core/FileUtil'
+ ],
+ function(
+ Core,
+ DateUtil,
+ DomChangeListener,
+ DomTraverse,
+ DomUtil,
+ EventHandler,
+ Language,
+ Permission,
+ Upload,
+ User,
+ FileUtil
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _createFileElement: function() {},
+ _getParameters: function() {},
+ _success: function() {},
+ _uploadFiles: function() {},
+ _createButton: function() {},
+ _createFileElements: function() {},
+ _failure: function() {},
+ _insertButton: function() {},
+ _progress: function() {},
+ _removeButton: function() {},
+ _upload: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaUpload(buttonContainerId, targetId, options) {
+ options = options || {};
+
+ this._mediaManager = null;
+ if (options.mediaManager) {
+ this._mediaManager = options.mediaManager;
+ delete options.mediaManager;
+ }
+ this._categoryId = null;
+
+ Upload.call(this, buttonContainerId, targetId, Core.extend({
+ className: 'wcf\\data\\media\\MediaAction',
+ multiple: this._mediaManager ? true : false,
+ singleFileRequests: true
+ }, options));
+ }
+ Core.inherit(MediaUpload, Upload, {
+ /**
+ * @see WoltLabSuite/Core/Upload#_createFileElement
+ */
+ _createFileElement: function(file) {
+ var fileElement;
+ if (this._target.nodeName === 'OL' || this._target.nodeName === 'UL') {
+ fileElement = elCreate('li');
+ }
+ else if (this._target.nodeName === 'TBODY') {
+ var firstTr = elByTag('TR', this._target)[0];
+ var tableContainer = this._target.parentNode.parentNode;
+ if (tableContainer.style.getPropertyValue('display') === 'none') {
+ fileElement = firstTr;
+
+ tableContainer.style.removeProperty('display');
+
+ elRemove(elById(elData(this._target, 'no-items-info')));
+ }
+ else {
+ fileElement = firstTr.cloneNode(true);
+
+ // regenerate id of table row
+ fileElement.removeAttribute('id');
+ DomUtil.identify(fileElement);
+ }
+
+ var cells = elByTag('TD', fileElement), cell;
+ for (var i = 0, length = cells.length; i < length; i++) {
+ cell = cells[i];
+
+ if (cell.classList.contains('columnMark')) {
+ elBySelAll('[data-object-id]', cell, elHide);
+ }
+ else if (cell.classList.contains('columnIcon')) {
+ elBySelAll('[data-object-id]', cell, elHide);
+
+ elByClass('mediaEditButton', cell)[0].classList.add('jsMediaEditButton');
+ elData(elByClass('jsDeleteButton', cell)[0], 'confirm-message-html', Language.get('wcf.media.delete.confirmMessage', {
+ title: file.name
+ }));
+ }
+ else if (cell.classList.contains('columnFilename')) {
+ // replace copied image with spinner
+ var image = elByTag('IMG', cell);
+
+ if (!image.length) {
+ image = elByClass('icon48', cell);
+ }
+
+ var spinner = elCreate('span');
+ spinner.className = 'icon icon48 fa-spinner mediaThumbnail';
+
+ DomUtil.replaceElement(image[0], spinner);
+
+ // replace title and uploading user
+ var ps = elBySelAll('.box48 > div > p', cell);
+ ps[0].textContent = file.name;
+
+ var userLink = elByTag('A', ps[1])[0];
+ if (!userLink) {
+ userLink = elCreate('a');
+ elByTag('SMALL', ps[1])[0].appendChild(userLink);
+ }
+
+ userLink.setAttribute('href', User.getLink());
+ userLink.textContent = User.username;
+ }
+ else if (cell.classList.contains('columnUploadTime')) {
+ cell.innerHTML = '';
+ cell.appendChild(DateUtil.getTimeElement(new Date()));
+ }
+ else if (cell.classList.contains('columnDigits')) {
+ cell.textContent = FileUtil.formatFilesize(file.size);
+ }
+ else {
+ // empty the other cells
+ cell.innerHTML = '';
+ }
+ }
+
+ DomUtil.prepend(fileElement, this._target);
+
+ return fileElement;
+ }
+ else {
+ fileElement = elCreate('p');
+ }
+
+ var thumbnail = elCreate('div');
+ thumbnail.className = 'mediaThumbnail';
+ fileElement.appendChild(thumbnail);
+
+ var fileIcon = elCreate('span');
+ fileIcon.className = 'icon icon144 fa-spinner';
+ thumbnail.appendChild(fileIcon);
+
+ var mediaInformation = elCreate('div');
+ mediaInformation.className = 'mediaInformation';
+ fileElement.appendChild(mediaInformation);
+
+ var p = elCreate('p');
+ p.className = 'mediaTitle';
+ p.textContent = file.name;
+ mediaInformation.appendChild(p);
+
+ var progress = elCreate('progress');
+ elAttr(progress, 'max', 100);
+ mediaInformation.appendChild(progress);
+
+ DomUtil.prepend(fileElement, this._target);
+
+ DomChangeListener.trigger();
+
+ return fileElement;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_getParameters
+ */
+ _getParameters: function() {
+ if (this._mediaManager) {
+ var parameters = {
+ imagesOnly: this._mediaManager.getOption('imagesOnly')
+ };
+
+ var categoryId = this._mediaManager.getCategoryId();
+ if (categoryId) {
+ parameters.categoryID = categoryId;
+ }
+
+ return Core.extend(MediaUpload._super.prototype._getParameters.call(this), parameters);
+ }
+
+ return MediaUpload._super.prototype._getParameters.call(this);
+ },
+
+ /**
+ * Replaces the default or copied file icon with the actual file icon.
+ *
+ * @param {HTMLElement} fileIcon file icon element
+ * @param {object} media media data
+ * @param {integer} size size of the file icon in pixels
+ */
+ _replaceFileIcon: function(fileIcon, media, size) {
+ if (media.tinyThumbnailType) {
+ var img = elCreate('img');
+ elAttr(img, 'src', media.tinyThumbnailLink);
+ elAttr(img, 'alt', '');
+ img.style.setProperty('width', size + 'px');
+ img.style.setProperty('height', size + 'px');
+
+ DomUtil.replaceElement(fileIcon, img);
+ }
+ else {
+ fileIcon.classList.remove('fa-spinner');
+
+ var fileIconName = FileUtil.getIconNameByFilename(media.filename);
+ if (fileIconName) {
+ fileIconName = '-' + fileIconName;
+ }
+ fileIcon.classList.add('fa-file' + fileIconName + '-o');
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_success
+ */
+ _success: function(uploadId, data) {
+ var files = this._fileElements[uploadId];
+
+ for (var i = 0, length = files.length; i < length; i++) {
+ var file = files[i];
+ var internalFileId = elData(file, 'internal-file-id');
+ var media = data.returnValues.media[internalFileId];
+
+ if (file.tagName === 'TR') {
+ if (media) {
+ // update object id
+ var objectIdElements = elBySelAll('[data-object-id]', file);
+ for (var i = 0, length = objectIdElements.length; i < length; i++) {
+ elData(objectIdElements[i], 'object-id', ~~media.mediaID);
+ elShow(objectIdElements[i]);
+ }
+
+ elByClass('columnMediaID', file)[0].textContent = media.mediaID;
+
+ // update icon
+ var fileIcon = elByClass('fa-spinner', file)[0];
+ this._replaceFileIcon(fileIcon, media, 48);
+ }
+ else {
+ var error = data.returnValues.errors[internalFileId];
+ if (!error) {
+ error = {
+ errorType: 'uploadFailed',
+ filename: elData(file, 'filename')
+ };
+ }
+
+ var fileIcon = elByClass('fa-spinner', file)[0];
+ fileIcon.classList.remove('fa-spinner');
+ fileIcon.classList.add('fa-remove');
+ fileIcon.classList.add('pointer');
+ fileIcon.classList.add('jsTooltip');
+ elAttr(fileIcon, 'title', Language.get('wcf.global.button.delete'));
+ fileIcon.addEventListener(WCF_CLICK_EVENT, function (event) {
+ elRemove(event.currentTarget.parentNode.parentNode.parentNode);
+
+ EventHandler.fire('com.woltlab.wcf.media.upload', 'removedErroneousUploadRow');
+ });
+
+ file.classList.add('uploadFailed');
+
+ var p = elBySelAll('.columnFilename .box48 > div > p', file)[1];
+
+ elInnerError(p, Language.get('wcf.media.upload.error.' + error.errorType, {
+ filename: error.filename
+ }));
+
+ elRemove(p);
+ }
+ }
+ else {
+ elRemove(DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaInformation'), 'PROGRESS'));
+
+ if (media) {
+ var fileIcon = DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaThumbnail'), 'SPAN');
+ this._replaceFileIcon(fileIcon, media, 144);
+
+ file.className = 'jsClipboardObject mediaFile';
+ elData(file, 'object-id', media.mediaID);
+
+ if (this._mediaManager) {
+ this._mediaManager.setupMediaElement(media, file);
+ this._mediaManager.addMedia(media, file);
+ }
+ }
+ else {
+ var error = data.returnValues.errors[internalFileId];
+ if (!error) {
+ error = {
+ errorType: 'uploadFailed',
+ filename: elData(file, 'filename')
+ };
+ }
+
+ var fileIcon = DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaThumbnail'), 'SPAN');
+ fileIcon.classList.remove('fa-spinner');
+ fileIcon.classList.add('fa-remove');
+ fileIcon.classList.add('pointer');
+
+ file.classList.add('uploadFailed');
+ file.classList.add('jsTooltip');
+ elAttr(file, 'title', Language.get('wcf.global.button.delete'));
+ file.addEventListener(WCF_CLICK_EVENT, function () {
+ elRemove(this);
+ });
+
+ var title = DomTraverse.childByClass(DomTraverse.childByClass(file, 'mediaInformation'), 'mediaTitle');
+ title.innerText = Language.get('wcf.media.upload.error.' + error.errorType, {
+ filename: error.filename
+ });
+ }
+ }
+
+ DomChangeListener.trigger();
+ }
+
+ EventHandler.fire('com.woltlab.wcf.media.upload', 'success', {
+ files: files,
+ isMultiFileUpload: this._multiFileUploadIds.indexOf(uploadId) !== -1,
+ media: data.returnValues.media,
+ upload: this,
+ uploadId: uploadId
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_uploadFiles
+ */
+ _uploadFiles: function(files, blob) {
+ return MediaUpload._super.prototype._uploadFiles.call(this, files, blob);
+ }
+ });
+
+ return MediaUpload;
+});
+
+/**
+ * Uploads media files.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/List/Upload
+ */
+define(
+ 'WoltLabSuite/Core/Media/List/Upload',[
+ 'Core', 'Dom/Util', '../Upload'
+ ],
+ function(
+ Core, DomUtil, MediaUpload
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _createButton: function() {},
+ _success: function() {},
+ _upload: function() {},
+ _createFileElement: function() {},
+ _getParameters: function() {},
+ _uploadFiles: function() {},
+ _createFileElements: function() {},
+ _failure: function() {},
+ _insertButton: function() {},
+ _progress: function() {},
+ _removeButton: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaListUpload(buttonContainerId, targetId, options) {
+ MediaUpload.call(this, buttonContainerId, targetId, options);
+ }
+ Core.inherit(MediaListUpload, MediaUpload, {
+ /**
+ * Creates the upload button.
+ */
+ _createButton: function() {
+ MediaListUpload._super.prototype._createButton.call(this);
+
+ var span = elBySel('span', this._button);
+
+ var space = document.createTextNode(' ');
+ DomUtil.prepend(space, span);
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-upload';
+ DomUtil.prepend(icon, span);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_getParameters
+ */
+ _getParameters: function() {
+ if (this._options.categoryId) {
+ return Core.extend(MediaListUpload._super.prototype._getParameters.call(this), {
+ categoryID: this._options.categoryId
+ });
+ }
+
+ return MediaListUpload._super.prototype._getParameters.call(this);
+ }
+ });
+
+ return MediaListUpload;
+});
+
+/**
+ * Initializes modules required for media clipboard.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Clipboard
+ */
+define('WoltLabSuite/Core/Media/Clipboard',[
+ 'Ajax',
+ 'Dom/ChangeListener',
+ 'EventHandler',
+ 'Language',
+ 'Ui/Dialog',
+ 'Ui/Notification',
+ 'WoltLabSuite/Core/Controller/Clipboard',
+ 'WoltLabSuite/Core/Media/Editor',
+ 'WoltLabSuite/Core/Media/List/Upload'
+ ],
+ function(
+ Ajax,
+ DomChangeListener,
+ EventHandler,
+ Language,
+ UiDialog,
+ UiNotification,
+ Clipboard,
+ MediaEditor,
+ MediaListUpload
+ ) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _clipboardAction: function() {},
+ _dialogSetup: function() {},
+ _edit: function() {},
+ _setCategory: function() {}
+ };
+ return Fake;
+ }
+
+ var _clipboardObjectIds = [];
+ var _mediaManager;
+
+ /**
+ * @exports WoltLabSuite/Core/Media/Clipboard
+ */
+ return {
+ init: function(pageClassName, hasMarkedItems, mediaManager) {
+ Clipboard.setup({
+ hasMarkedItems: hasMarkedItems,
+ pageClassName: pageClassName
+ });
+
+ _mediaManager = mediaManager;
+
+ EventHandler.add('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.media', this._clipboardAction.bind(this));
+ },
+
+ /**
+ * Returns the data used to setup the AJAX request object.
+ *
+ * @return {object} setup data
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\media\\MediaAction'
+ }
+ }
+ },
+
+ /**
+ * Handles successful AJAX request.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'getSetCategoryDialog':
+ UiDialog.open(this, data.returnValues.template);
+
+ break;
+
+ case 'setCategory':
+ UiDialog.close(this);
+
+ UiNotification.show();
+
+ Clipboard.reload();
+
+ break;
+ }
+ },
+
+ /**
+ * Returns the data used to setup the dialog.
+ *
+ * @return {object} setup data
+ */
+ _dialogSetup: function() {
+ return {
+ id: 'mediaSetCategoryDialog',
+ options: {
+ onSetup: function(content) {
+ elBySel('button', content).addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.preventDefault();
+
+ this._setCategory(~~elBySel('select[name="categoryID"]', content).value);
+
+ event.currentTarget.disabled = true;
+ }.bind(this));
+ }.bind(this),
+ title: Language.get('wcf.media.setCategory')
+ },
+ source: null
+ }
+ },
+
+ /**
+ * Handles successful clipboard actions.
+ *
+ * @param {object} actionData
+ */
+ _clipboardAction: function(actionData) {
+ var mediaIds = actionData.data.parameters.objectIDs;
+
+ switch (actionData.data.actionName) {
+ case 'com.woltlab.wcf.media.delete':
+ // only consider events if the action has been executed
+ if (actionData.responseData !== null) {
+ _mediaManager.clipboardDeleteMedia(mediaIds);
+ }
+
+ break;
+
+ case 'com.woltlab.wcf.media.insert':
+ _mediaManager.clipboardInsertMedia(mediaIds);
+
+ break;
+
+ case 'com.woltlab.wcf.media.setCategory':
+ _clipboardObjectIds = mediaIds;
+
+ Ajax.api(this, {
+ actionName: 'getSetCategoryDialog'
+ });
+
+ break;
+ }
+ },
+
+ /**
+ * Sets the category of the marked media files.
+ *
+ * @param {int} categoryID selected category id
+ */
+ _setCategory: function(categoryID) {
+ Ajax.api(this, {
+ actionName: 'setCategory',
+ objectIDs: _clipboardObjectIds,
+ parameters: {
+ categoryID: categoryID
+ }
+ });
+ }
+ }
+});
+/**
+ * Provides desktop notifications via periodic polling with an
+ * increasing request delay on inactivity.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Notification/Handler
+ */
+define('WoltLabSuite/Core/Notification/Handler',['Ajax', 'Core', 'EventHandler', 'StringUtil'], function(Ajax, Core, EventHandler, StringUtil) {
+ "use strict";
+
+ if (!('Promise' in window) || !('Notification' in window)) {
+ // fake object exposed to ancient browsers (*cough* IE11 *cough*)
+ return {
+ setup: function () {}
+ }
+ }
+
+ var _allowNotification = false;
+ var _icon = '';
+ var _inactiveSince = 0;
+ //noinspection JSUnresolvedVariable
+ var _lastRequestTimestamp = window.TIME_NOW;
+ var _requestTimer = null;
+ var _sessionKeepAlive = 0;
+
+ /**
+ * @exports WoltLabSuite/Core/Notification/Handler
+ */
+ return {
+ /**
+ * Initializes the desktop notification system.
+ *
+ * @param {Object} options initialization options
+ */
+ setup: function (options) {
+ options = Core.extend({
+ enableNotifications: false,
+ icon: '',
+ sessionKeepAlive: 0
+ }, options);
+
+ _icon = options.icon;
+ _sessionKeepAlive = options.sessionKeepAlive * 60;
+
+ this._prepareNextRequest();
+
+ document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
+ window.addEventListener('storage', this._onStorage.bind(this));
+
+ this._onVisibilityChange(null);
+
+ if (options.enableNotifications) {
+ switch (window.Notification.permission) {
+ case 'granted':
+ _allowNotification = true;
+ break;
+ case 'default':
+ window.Notification.requestPermission(function (result) {
+ if (result === 'granted') {
+ _allowNotification = true;
+ }
+ });
+ break;
+ }
+ }
+ },
+
+ /**
+ * Detects when this window is hidden or restored.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _onVisibilityChange: function(event) {
+ // document was hidden before
+ if (event !== null && !document.hidden) {
+ var difference = (Date.now() - _inactiveSince) / 60000;
+ if (difference > 4) {
+ this._resetTimer();
+ this._dispatchRequest();
+ }
+ }
+
+ _inactiveSince = (document.hidden) ? Date.now() : 0;
+ },
+
+ /**
+ * Returns the delay in minutes before the next request should be dispatched.
+ *
+ * @return {int}
+ * @protected
+ */
+ _getNextDelay: function() {
+ if (_inactiveSince === 0) return 5;
+
+ // milliseconds -> minutes
+ var inactiveMinutes = ~~((Date.now() - _inactiveSince) / 60000);
+ if (inactiveMinutes < 15) {
+ return 5;
+ }
+ else if (inactiveMinutes < 30) {
+ return 10;
+ }
+
+ return 15;
+ },
+
+ /**
+ * Resets the request delay timer.
+ *
+ * @protected
+ */
+ _resetTimer: function() {
+ if (_requestTimer !== null) {
+ window.clearTimeout(_requestTimer);
+ _requestTimer = null;
+ }
+ },
+
+ /**
+ * Schedules the next request using a calculated delay.
+ *
+ * @protected
+ */
+ _prepareNextRequest: function() {
+ this._resetTimer();
+
+ var delay = Math.min(this._getNextDelay(), _sessionKeepAlive);
+ _requestTimer = window.setTimeout(this._dispatchRequest.bind(this), delay * 60000);
+ },
+
+ /**
+ * Requests new data from the server.
+ *
+ * @protected
+ */
+ _dispatchRequest: function() {
+ var parameters = {};
+ EventHandler.fire('com.woltlab.wcf.notification', 'beforePoll', parameters);
+
+ // this timestamp is used to determine new notifications and to avoid
+ // notifications being displayed multiple times due to different origins
+ // (=subdomains) used, because we cannot synchronize them in the client
+ parameters.lastRequestTimestamp = _lastRequestTimestamp;
+
+ Ajax.api(this, {
+ parameters: parameters
+ });
+ },
+
+ /**
+ * Notifies subscribers for updated data received by another tab.
+ *
+ * @protected
+ */
+ _onStorage: function() {
+ // abort and re-schedule periodic request
+ this._prepareNextRequest();
+
+ var pollData, keepAliveData, abort = false;
+ try {
+ pollData = window.localStorage.getItem(Core.getStoragePrefix() + 'notification');
+ keepAliveData = window.localStorage.getItem(Core.getStoragePrefix() + 'keepAliveData');
+
+ pollData = JSON.parse(pollData);
+ keepAliveData = JSON.parse(keepAliveData);
+ }
+ catch (e) {
+ abort = true;
+ }
+
+ if (!abort) {
+ EventHandler.fire('com.woltlab.wcf.notification', 'onStorage', {
+ pollData: pollData,
+ keepAliveData: keepAliveData
+ });
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ var abort = false;
+ var keepAliveData = data.returnValues.keepAliveData;
+ var pollData = data.returnValues.pollData;
+
+ // forward keep alive data
+ window.WCF.System.PushNotification.executeCallbacks(keepAliveData);
+
+ // store response data in local storage
+ try {
+ window.localStorage.setItem(Core.getStoragePrefix() + 'notification', JSON.stringify(pollData));
+ window.localStorage.setItem(Core.getStoragePrefix() + 'keepAliveData', JSON.stringify(keepAliveData));
+ }
+ catch (e) {
+ // storage is unavailable, e.g. in private mode, log error and disable polling
+ abort = true;
+
+ window.console.log(e);
+ }
+
+ if (!abort) {
+ this._prepareNextRequest();
+ }
+
+ _lastRequestTimestamp = data.returnValues.lastRequestTimestamp;
+
+ EventHandler.fire('com.woltlab.wcf.notification', 'afterPoll', pollData);
+
+ this._showNotification(pollData);
+ },
+
+ /**
+ * Displays a desktop notification.
+ *
+ * @param {Object} pollData
+ * @protected
+ */
+ _showNotification: function(pollData) {
+ if (!_allowNotification) {
+ return;
+ }
+
+ //noinspection JSUnresolvedVariable
+ if (typeof pollData.notification === 'object' && typeof pollData.notification.message === 'string') {
+ //noinspection JSUnresolvedVariable
+ var notification = new window.Notification(pollData.notification.title, {
+ body: StringUtil.unescapeHTML(pollData.notification.message),
+ icon: _icon
+ });
+ notification.onclick = function () {
+ window.focus();
+ notification.close();
+
+ //noinspection JSUnresolvedVariable
+ window.location = pollData.notification.link;
+ };
+ }
+ },
+
+ _ajaxSetup: function() {
+ //noinspection JSUnresolvedVariable
+ return {
+ data: {
+ actionName: 'poll',
+ className: 'wcf\\data\\session\\SessionAction'
+ },
+ ignoreError: !window.ENABLE_DEBUG_MODE,
+ silent: !window.ENABLE_DEBUG_MODE
+ };
+ }
+ }
+});
+
+/**
+ * Drag and Drop file uploads.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/DragAndDrop
+ */
+define('WoltLabSuite/Core/Ui/Redactor/DragAndDrop',['Dictionary', 'EventHandler', 'Language'], function (Dictionary, EventHandler, Language) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _dragOver: function() {},
+ _drop: function() {},
+ _dragLeave: function() {},
+ _setup: function() {}
+ };
+ return Fake;
+ }
+
+ var _didInit = false;
+ var _dragArea = new Dictionary();
+ var _isDragging = false;
+ var _isFile = false;
+ var _timerLeave = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Redactor/DragAndDrop
+ */
+ return {
+ /**
+ * Initializes drag and drop support for provided editor instance.
+ *
+ * @param {$.Redactor} editor editor instance
+ */
+ init: function (editor) {
+ if (!_didInit) {
+ this._setup();
+ }
+
+ _dragArea.set(editor.uuid, {
+ editor: editor,
+ element: null
+ });
+ },
+
+ /**
+ * Handles items dragged into the browser window.
+ *
+ * @param {Event} event drag event
+ */
+ _dragOver: function (event) {
+ event.preventDefault();
+
+ //noinspection JSUnresolvedVariable
+ if (!event.dataTransfer || !event.dataTransfer.types) {
+ return;
+ }
+
+ var isFirefox = false;
+ //noinspection JSUnresolvedVariable
+ for (var property in event.dataTransfer) {
+ //noinspection JSUnresolvedVariable
+ if (event.dataTransfer.hasOwnProperty(property) && property.match(/^moz/)) {
+ isFirefox = true;
+ break;
+ }
+ }
+
+ // IE and WebKit set 'Files', Firefox sets 'application/x-moz-file' for files being dragged
+ // and Safari just provides 'Files' along with a huge list of garbage
+ _isFile = false;
+ if (isFirefox) {
+ // Firefox sets the 'Files' type even if the user is just dragging an on-page element
+ //noinspection JSUnresolvedVariable
+ if (event.dataTransfer.types[0] === 'application/x-moz-file') {
+ _isFile = true;
+ }
+ }
+ else {
+ //noinspection JSUnresolvedVariable
+ for (var i = 0; i < event.dataTransfer.types.length; i++) {
+ //noinspection JSUnresolvedVariable
+ if (event.dataTransfer.types[i] === 'Files') {
+ _isFile = true;
+ break;
+ }
+ }
+ }
+
+ if (!_isFile) {
+ // user is just dragging around some garbage, ignore it
+ return;
+ }
+
+ if (_isDragging) {
+ // user is still dragging the file around
+ return;
+ }
+
+ _isDragging = true;
+
+ _dragArea.forEach((function (data, uuid) {
+ var editor = data.editor.$editor[0];
+ if (!editor.parentNode) {
+ _dragArea.delete(uuid);
+ return;
+ }
+
+ var element = data.element;
+ if (element === null) {
+ element = elCreate('div');
+ element.className = 'redactorDropArea';
+ elData(element, 'element-id', data.editor.$element[0].id);
+ elData(element, 'drop-here', Language.get('wcf.attachment.dragAndDrop.dropHere'));
+ elData(element, 'drop-now', Language.get('wcf.attachment.dragAndDrop.dropNow'));
+
+ element.addEventListener('dragover', function () { element.classList.add('active'); });
+ element.addEventListener('dragleave', function () { element.classList.remove('active'); });
+ element.addEventListener('drop', this._drop.bind(this));
+
+ data.element = element;
+ }
+
+ editor.parentNode.insertBefore(element, editor);
+ element.style.setProperty('top', editor.offsetTop + 'px', '');
+ }).bind(this));
+ },
+
+ /**
+ * Handles items dropped onto an editor's drop area
+ *
+ * @param {Event} event drop event
+ * @protected
+ */
+ _drop: function (event) {
+ if (!_isFile) {
+ return;
+ }
+
+ //noinspection JSUnresolvedVariable
+ if (!event.dataTransfer || !event.dataTransfer.files.length) {
+ return;
+ }
+
+ event.preventDefault();
+
+ //noinspection JSCheckFunctionSignatures
+ var elementId = elData(event.currentTarget, 'element-id');
+
+ //noinspection JSUnresolvedVariable
+ for (var i = 0, length = event.dataTransfer.files.length; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'dragAndDrop_' + elementId, {
+ file: event.dataTransfer.files[i]
+ });
+ }
+
+ // this will reset all drop areas
+ this._dragLeave();
+ },
+
+ /**
+ * Invoked whenever the item is no longer dragged or was dropped.
+ *
+ * @protected
+ */
+ _dragLeave: function () {
+ if (!_isDragging || !_isFile) {
+ return;
+ }
+
+ if (_timerLeave !== null) {
+ window.clearTimeout(_timerLeave);
+ }
+
+ _timerLeave = window.setTimeout(function () {
+ if (!_isDragging) {
+ _dragArea.forEach(function (data) {
+ if (data.element && data.element.parentNode) {
+ data.element.classList.remove('active');
+ elRemove(data.element);
+ }
+ });
+ }
+
+ _timerLeave = null;
+ }, 100);
+
+ _isDragging = false;
+ },
+
+ /**
+ * Handles the global drop event.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _globalDrop: function (event) {
+ if (event.target.closest('.redactor-layer') === null) {
+ var eventData = { cancelDrop: true, event: event };
+ _dragArea.forEach(function(data) {
+ //noinspection JSUnresolvedVariable
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'dragAndDrop_globalDrop_' + data.editor.$element[0].id, eventData);
+ });
+
+ if (eventData.cancelDrop) {
+ event.preventDefault();
+ }
+ }
+
+ this._dragLeave(event);
+ },
+
+ /**
+ * Binds listeners to global events.
+ *
+ * @protected
+ */
+ _setup: function () {
+ // discard garbage event
+ window.addEventListener('dragend', function (event) { event.preventDefault(); });
+
+ window.addEventListener('dragover', this._dragOver.bind(this));
+ window.addEventListener('dragleave', this._dragLeave.bind(this));
+ window.addEventListener('drop', this._globalDrop.bind(this));
+
+ _didInit = true;
+ }
+ };
+});
+
+/**
+ * Generic interface for drag and Drop file uploads.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/DragAndDrop
+ */
+define('WoltLabSuite/Core/Ui/DragAndDrop',['Core', 'EventHandler', 'WoltLabSuite/Core/Ui/Redactor/DragAndDrop'], function (Core, EventHandler, UiRedactorDragAndDrop) {
+ /**
+ * @exports WoltLabSuite/Core/Ui/DragAndDrop
+ */
+ return {
+ /**
+ * @param {Object} options
+ */
+ register: function (options) {
+ var uuid = Core.getUuid();
+ options = Core.extend({
+ element: '',
+ elementId: '',
+ onDrop: function(data) {
+ /* data: { file: File } */
+ },
+ onGlobalDrop: function (data) {
+ /* data: { cancelDrop: boolean, event: DragEvent } */
+ }
+ });
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_' + options.elementId, options.onDrop);
+ EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_globalDrop_' + options.elementId, options.onGlobalDrop);
+
+ UiRedactorDragAndDrop.init({
+ uuid: uuid,
+ $editor: [options.element],
+ $element: [{id: options.elementId}]
+ });
+ }
+ };
+});
+
+/**
+ * Flexible UI element featuring both a list of items and an input field with suggestion support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Suggestion
+ */
+define('WoltLabSuite/Core/Ui/Suggestion',['Ajax', 'Core', 'Ui/SimpleDropdown'], function(Ajax, Core, UiSimpleDropdown) {
+ "use strict";
+
+ /**
+ * @constructor
+ * @param {string} elementId input element id
+ * @param {Object} options option list
+ */
+ function UiSuggestion(elementId, options) { this.init(elementId, options); }
+ UiSuggestion.prototype = {
+ /**
+ * Initializes a new suggestion input.
+ *
+ * @param {string} elementId input element id
+ * @param {Object} options option list
+ */
+ init: function(elementId, options) {
+ this._dropdownMenu = null;
+ this._value = '';
+
+ this._element = elById(elementId);
+ if (this._element === null) {
+ throw new Error("Expected a valid element id.");
+ }
+
+ this._options = Core.extend({
+ ajax: {
+ actionName: 'getSearchResultList',
+ className: '',
+ interfaceName: 'wcf\\data\\ISearchAction',
+ parameters: {
+ data: {}
+ }
+ },
+
+ // will be executed once a value from the dropdown has been selected
+ callbackSelect: null,
+ // list of excluded search values
+ excludedSearchValues: [],
+ // minimum number of characters required to trigger a search request
+ threshold: 3
+ }, options);
+
+ if (typeof this._options.callbackSelect !== 'function') {
+ throw new Error("Expected a valid callback for option 'callbackSelect'.");
+ }
+
+ this._element.addEventListener(WCF_CLICK_EVENT, function(event) { event.stopPropagation(); });
+ this._element.addEventListener('keydown', this._keyDown.bind(this));
+ this._element.addEventListener('keyup', this._keyUp.bind(this));
+ },
+
+ /**
+ * Adds an excluded search value.
+ *
+ * @param {string} value excluded value
+ */
+ addExcludedValue: function(value) {
+ if (this._options.excludedSearchValues.indexOf(value) === -1) {
+ this._options.excludedSearchValues.push(value);
+ }
+ },
+
+ /**
+ * Removes an excluded search value.
+ *
+ * @param {string} value excluded value
+ */
+ removeExcludedValue: function(value) {
+ var index = this._options.excludedSearchValues.indexOf(value);
+ if (index !== -1) {
+ this._options.excludedSearchValues.splice(index, 1);
+ }
+ },
+
+ /**
+ * Returns true if the suggestions are active.
+ * @return {boolean}
+ */
+ isActive: function() {
+ return (this._dropdownMenu !== null && UiSimpleDropdown.isOpen(this._element.id));
+ },
+
+ /**
+ * Handles the keyboard navigation for interaction with the suggestion list.
+ *
+ * @param {object} event event object
+ */
+ _keyDown: function(event) {
+ if (!this.isActive()) {
+ return true;
+ }
+
+ if (event.keyCode !== 13 && event.keyCode !== 27 && event.keyCode !== 38 && event.keyCode !== 40) {
+ return true;
+ }
+
+ var active, i = 0, length = this._dropdownMenu.childElementCount;
+ while (i < length) {
+ active = this._dropdownMenu.children[i];
+ if (active.classList.contains('active')) {
+ break;
+ }
+
+ i++;
+ }
+
+ if (event.keyCode === 13) {
+ // Enter
+ UiSimpleDropdown.close(this._element.id);
+
+ this._select(active);
+ }
+ else if (event.keyCode === 27) {
+ if (UiSimpleDropdown.isOpen(this._element.id)) {
+ UiSimpleDropdown.close(this._element.id);
+ }
+ else {
+ // let the event pass through
+ return true;
+ }
+ }
+ else {
+ var index = 0;
+
+ if (event.keyCode === 38) {
+ // ArrowUp
+ index = ((i === 0) ? length : i) - 1;
+ }
+ else if (event.keyCode === 40) {
+ // ArrowDown
+ index = i + 1;
+ if (index === length) index = 0;
+ }
+
+ if (index !== i) {
+ active.classList.remove('active');
+ this._dropdownMenu.children[index].classList.add('active');
+ }
+ }
+
+ event.preventDefault();
+ return false;
+ },
+
+ /**
+ * Selects an item from the list.
+ *
+ * @param {(Element|Event)} item list item or event object
+ */
+ _select: function(item) {
+ var isEvent = (item instanceof Event);
+ if (isEvent) {
+ item = item.currentTarget.parentNode;
+ }
+
+ var anchor = item.children[0];
+ this._options.callbackSelect(this._element.id, { objectId: elData(anchor, 'object-id'), value: item.textContent, type: elData(anchor, 'type') });
+
+ if (isEvent) {
+ this._element.focus();
+ }
+ },
+
+ /**
+ * Performs a search for the input value unless it is below the threshold.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ var value = event.currentTarget.value.trim();
+
+ if (this._value === value) {
+ return;
+ }
+ else if (value.length < this._options.threshold) {
+ if (this._dropdownMenu !== null) {
+ UiSimpleDropdown.close(this._element.id);
+ }
+
+ this._value = value;
+
+ return;
+ }
+
+ this._value = value;
+
+ Ajax.api(this, {
+ parameters: {
+ data: {
+ excludedSearchValues: this._options.excludedSearchValues,
+ searchString: value
+ }
+ }
+ });
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: this._options.ajax
+ };
+ },
+
+ /**
+ * Handles successful Ajax requests.
+ *
+ * @param {object} data response values
+ */
+ _ajaxSuccess: function(data) {
+ if (this._dropdownMenu === null) {
+ this._dropdownMenu = elCreate('div');
+ this._dropdownMenu.className = 'dropdownMenu';
+
+ UiSimpleDropdown.initFragment(this._element, this._dropdownMenu);
+ }
+ else {
+ this._dropdownMenu.innerHTML = '';
+ }
+
+ if (data.returnValues.length) {
+ var anchor, item, listItem;
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ item = data.returnValues[i];
+
+ anchor = elCreate('a');
+ if (item.icon) {
+ anchor.className = 'box16';
+ anchor.innerHTML = item.icon + ' <span></span>';
+ anchor.children[1].textContent = item.label;
+ }
+ else {
+ anchor.textContent = item.label;
+ }
+ elData(anchor, 'object-id', item.objectID);
+ if (item.type) elData(anchor, 'type', item.type);
+ anchor.addEventListener(WCF_CLICK_EVENT, this._select.bind(this));
+
+ listItem = elCreate('li');
+ if (i === 0) listItem.className = 'active';
+ listItem.appendChild(anchor);
+
+ this._dropdownMenu.appendChild(listItem);
+ }
+
+ UiSimpleDropdown.open(this._element.id, true);
+ }
+ else {
+ UiSimpleDropdown.close(this._element.id);
+ }
+ }
+ };
+
+ return UiSuggestion;
+});
+
+/**
+ * Flexible UI element featuring both a list of items and an input field with suggestion support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList
+ */
+define('WoltLabSuite/Core/Ui/ItemList',['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSuite/Core/Ui/Suggestion', 'Ui/SimpleDropdown'], function(Core, Dictionary, Language, DomTraverse, EventKey, UiSuggestion, UiSimpleDropdown) {
+ "use strict";
+
+ var _activeId = '';
+ var _data = new Dictionary();
+ var _didInit = false;
+
+ var _callbackKeyDown = null;
+ var _callbackKeyPress = null;
+ var _callbackKeyUp = null;
+ var _callbackPaste = null;
+ var _callbackRemoveItem = null;
+ var _callbackBlur = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/ItemList
+ */
+ return {
+ /**
+ * Initializes an item list.
+ *
+ * The `values` argument must be empty or contain a list of strings or object, e.g.
+ * `['foo', 'bar']` or `[{ objectId: 1337, value: 'baz'}, {...}]`
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of existing values
+ * @param {Object} options option list
+ */
+ init: function(elementId, values, options) {
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, '" + elementId + "' is invalid.");
+ }
+
+ // remove data from previous instance
+ if (_data.has(elementId)) {
+ var tmp = _data.get(elementId);
+
+ for (var key in tmp) {
+ if (tmp.hasOwnProperty(key)) {
+ var el = tmp[key];
+ if (el instanceof Element && el.parentNode) {
+ elRemove(el);
+ }
+ }
+ }
+
+ UiSimpleDropdown.destroy(elementId);
+ _data.delete(elementId);
+ }
+
+ options = Core.extend({
+ // search parameters for suggestions
+ ajax: {
+ actionName: 'getSearchResultList',
+ className: '',
+ data: {}
+ },
+
+ // list of excluded string values, e.g. `['ignore', 'these strings', 'when', 'searching']`
+ excludedSearchValues: [],
+ // maximum number of items this list may contain, `-1` for infinite
+ maxItems: -1,
+ // maximum length of an item value, `-1` for infinite
+ maxLength: -1,
+ // disallow custom values, only values offered by the suggestion dropdown are accepted
+ restricted: false,
+
+ // initial value will be interpreted as comma separated value and submitted as such
+ isCSV: false,
+
+ // will be invoked whenever the items change, receives the element id first and list of values second
+ callbackChange: null,
+ // callback once the form is about to be submitted
+ callbackSubmit: null,
+ // Callback for the custom shadow synchronization.
+ callbackSyncShadow: null,
+ // Callback to set values during the setup.
+ callbackSetupValues: null,
+ // value may contain the placeholder `{$objectId}`
+ submitFieldName: ''
+ }, options);
+
+ var form = DomTraverse.parentByTag(element, 'FORM');
+ if (form !== null) {
+ if (options.isCSV === false) {
+ if (!options.submitFieldName.length && typeof options.callbackSubmit !== 'function') {
+ throw new Error("Expected a valid function for option 'callbackSubmit', a non-empty value for option 'submitFieldName' or enabling the option 'submitFieldCSV'.");
+ }
+
+ form.addEventListener('submit', (function() {
+ var values = this.getValues(elementId);
+ if (options.submitFieldName.length) {
+ var input;
+ for (var i = 0, length = values.length; i < length; i++) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = options.submitFieldName.replace(/{$objectId}/, values[i].objectId);
+ input.value = values[i].value;
+
+ form.appendChild(input);
+ }
+ }
+ else {
+ options.callbackSubmit(form, values);
+ }
+ }).bind(this));
+ }
+ }
+
+ this._setup();
+
+ var data = this._createUI(element, options);
+ //noinspection JSUnresolvedVariable
+ var suggestion = new UiSuggestion(elementId, {
+ ajax: options.ajax,
+ callbackSelect: this._addItem.bind(this),
+ excludedSearchValues: options.excludedSearchValues
+ });
+
+ _data.set(elementId, {
+ dropdownMenu: null,
+ element: data.element,
+ list: data.list,
+ listItem: data.element.parentNode,
+ options: options,
+ shadow: data.shadow,
+ suggestion: suggestion
+ });
+
+ if (options.callbackSetupValues) {
+ values = options.callbackSetupValues();
+ }
+ else {
+ values = (data.values.length) ? data.values : values;
+ }
+
+ if (Array.isArray(values)) {
+ var value;
+ for (var i = 0, length = values.length; i < length; i++) {
+ value = values[i];
+ if (typeof value === 'string') {
+ value = { objectId: 0, value: value };
+ }
+
+ this._addItem(elementId, value);
+ }
+ }
+ },
+
+ /**
+ * Returns the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @return {Array} list of objects containing object id and value
+ */
+ getValues: function(elementId) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+ var values = [];
+ elBySelAll('.item > span', data.list, function(span) {
+ values.push({
+ objectId: ~~elData(span, 'object-id'),
+ value: span.textContent,
+ type: elData(span, 'type')
+ });
+ });
+
+ return values;
+ },
+
+ /**
+ * Sets the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of objects containing object id and value
+ */
+ setValues: function(elementId, values) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+
+ // remove all existing items first
+ var i, length;
+ var items = DomTraverse.childrenByClass(data.list, 'item');
+ for (i = 0, length = items.length; i < length; i++) {
+ this._removeItem(null, items[i], true);
+ }
+
+ // add new items
+ for (i = 0, length = values.length; i < length; i++) {
+ this._addItem(elementId, values[i]);
+ }
+ },
+
+ /**
+ * Binds static event listeners.
+ */
+ _setup: function() {
+ if (_didInit) {
+ return;
+ }
+
+ _didInit = true;
+
+ _callbackKeyDown = this._keyDown.bind(this);
+ _callbackKeyPress = this._keyPress.bind(this);
+ _callbackKeyUp = this._keyUp.bind(this);
+ _callbackPaste = this._paste.bind(this);
+ _callbackRemoveItem = this._removeItem.bind(this);
+ _callbackBlur = this._blur.bind(this);
+ },
+
+ /**
+ * Creates the DOM structure for target element. If `element` is a `<textarea>`
+ * it will be automatically replaced with an `<input>` element.
+ *
+ * @param {Element} element input element
+ * @param {Object} options option list
+ */
+ _createUI: function(element, options) {
+ var list = elCreate('ol');
+ list.className = 'inputItemList' + (element.disabled ? ' disabled' : '');
+ elData(list, 'element-id', element.id);
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ if (event.target === list) {
+ //noinspection JSUnresolvedFunction
+ element.focus();
+ }
+ });
+
+ var listItem = elCreate('li');
+ listItem.className = 'input';
+ list.appendChild(listItem);
+
+ element.addEventListener('keydown', _callbackKeyDown);
+ element.addEventListener('keypress', _callbackKeyPress);
+ element.addEventListener('keyup', _callbackKeyUp);
+ element.addEventListener('paste', _callbackPaste);
+ var hasFocus = element === document.activeElement;
+ if (hasFocus) {
+ //noinspection JSUnresolvedFunction
+ element.blur();
+ }
+ element.addEventListener('blur', _callbackBlur);
+ element.parentNode.insertBefore(list, element);
+ listItem.appendChild(element);
+ if (hasFocus) {
+ window.setTimeout(function() {
+ //noinspection JSUnresolvedFunction
+ element.focus();
+ }, 1);
+ }
+
+ if (options.maxLength !== -1) {
+ elAttr(element, 'maxLength', options.maxLength);
+ }
+
+ var shadow = null, values = [];
+ if (options.isCSV) {
+ shadow = elCreate('input');
+ shadow.className = 'itemListInputShadow';
+ shadow.type = 'hidden';
+ //noinspection JSUnresolvedVariable
+ shadow.name = element.name;
+ element.removeAttribute('name');
+
+ list.parentNode.insertBefore(shadow, list);
+
+ //noinspection JSUnresolvedVariable
+ var value, tmp = element.value.split(',');
+ for (var i = 0, length = tmp.length; i < length; i++) {
+ value = tmp[i].trim();
+ if (value.length) {
+ values.push(value);
+ }
+ }
+
+ if (element.nodeName === 'TEXTAREA') {
+ var inputElement = elCreate('input');
+ inputElement.type = 'text';
+ element.parentNode.insertBefore(inputElement, element);
+ inputElement.id = element.id;
+
+ elRemove(element);
+ element = inputElement;
+ }
+ }
+
+ return {
+ element: element,
+ list: list,
+ shadow: shadow,
+ values: values
+ };
+ },
+
+ /**
+ * Returns true if the input accepts new items.
+ *
+ * @param {string} elementId input element id
+ * @return {boolean} true if at least one more item can be added
+ * @protected
+ */
+ _acceptsNewItems: function (elementId) {
+ var data = _data.get(elementId);
+ if (data.options.maxItems === -1) {
+ return true;
+ }
+
+ return (data.list.childElementCount - 1 < data.options.maxItems);
+ },
+
+ /**
+ * Enforces the maximum number of items.
+ *
+ * @param {string} elementId input element id
+ */
+ _handleLimit: function(elementId) {
+ var data = _data.get(elementId);
+ if (this._acceptsNewItems(elementId)) {
+ if (data.element.disabled) {
+ data.element.disabled = false;
+ data.element.removeAttribute('placeholder');
+ }
+ }
+ else if (!data.element.disabled) {
+ data.element.disabled = true;
+ elAttr(data.element, 'placeholder', Language.get('wcf.global.form.input.maxItems'));
+ }
+ },
+
+ /**
+ * Sets the active item list id and handles keyboard access to remove an existing item.
+ *
+ * @param {object} event event object
+ */
+ _keyDown: function(event) {
+ var input = event.currentTarget;
+ var lastItem = input.parentNode.previousElementSibling;
+
+ _activeId = input.id;
+
+ if (event.keyCode === 8) {
+ // 8 = [BACKSPACE]
+ if (input.value.length === 0) {
+ if (lastItem !== null) {
+ if (lastItem.classList.contains('active')) {
+ this._removeItem(null, lastItem);
+ }
+ else {
+ lastItem.classList.add('active');
+ }
+ }
+ }
+ }
+ else if (event.keyCode === 27) {
+ // 27 = [ESC]
+ if (lastItem !== null && lastItem.classList.contains('active')) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` and `[,]` key to add an item to the list unless it is restricted.
+ *
+ * @param {Event} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event) || EventKey.Comma(event)) {
+ event.preventDefault();
+
+ if (_data.get(event.currentTarget.id).options.restricted) {
+ // restricted item lists only allow results from the dropdown to be picked
+ return;
+ }
+
+ var value = event.currentTarget.value.trim();
+ if (value.length) {
+ this._addItem(event.currentTarget.id, { objectId: 0, value: value });
+ }
+ }
+ },
+
+ /**
+ * Splits comma-separated values being pasted into the input field.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _paste: function (event) {
+ var text = '';
+ if (typeof window.clipboardData === 'object') {
+ // IE11
+ text = window.clipboardData.getData('Text');
+ }
+ else {
+ text = event.clipboardData.getData('text/plain');
+ }
+
+ var element = event.currentTarget;
+ var elementId = element.id;
+ var maxLength = ~~elAttr(element, 'maxLength');
+
+ text.split(/,/).forEach((function(item) {
+ item = item.trim();
+ if (maxLength && item.length > maxLength) {
+ // truncating items provides a better UX than throwing an error or silently discarding it
+ item = item.substr(0, maxLength);
+ }
+
+ if (item.length > 0 && this._acceptsNewItems(elementId)) {
+ this._addItem(elementId, {objectId: 0, value: item});
+ }
+ }).bind(this));
+
+ event.preventDefault();
+ },
+
+ /**
+ * Handles the keyup event to unmark an item for deletion.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ var input = event.currentTarget;
+
+ if (input.value.length > 0) {
+ var lastItem = input.parentNode.previousElementSibling;
+ if (lastItem !== null) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Adds an item to the list.
+ *
+ * @param {string} elementId input element id
+ * @param {object} value item value
+ */
+ _addItem: function(elementId, value) {
+ var data = _data.get(elementId);
+
+ var listItem = elCreate('li');
+ listItem.className = 'item';
+
+ var content = elCreate('span');
+ content.className = 'content';
+ elData(content, 'object-id', value.objectId);
+ if (value.type) elData(content, 'type', value.type);
+ content.textContent = value.value;
+ listItem.appendChild(content);
+
+ if (!data.element.disabled) {
+ var button = elCreate('a');
+ button.className = 'icon icon16 fa-times';
+ button.addEventListener(WCF_CLICK_EVENT, _callbackRemoveItem);
+ listItem.appendChild(button);
+ }
+
+ data.list.insertBefore(listItem, data.listItem);
+ data.suggestion.addExcludedValue(value.value);
+ data.element.value = '';
+
+ if (!data.element.disabled) {
+ this._handleLimit(elementId);
+ }
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Removes an item from the list.
+ *
+ * @param {?object} event event object
+ * @param {Element?} item list item
+ * @param {boolean?} noFocus input element will not be focused if true
+ */
+ _removeItem: function(event, item, noFocus) {
+ item = (event === null) ? item : event.currentTarget.parentNode;
+
+ var parent = item.parentNode;
+ //noinspection JSCheckFunctionSignatures
+ var elementId = elData(parent, 'element-id');
+ var data = _data.get(elementId);
+
+ data.suggestion.removeExcludedValue(item.children[0].textContent);
+ parent.removeChild(item);
+ if (!noFocus) data.element.focus();
+
+ this._handleLimit(elementId);
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Synchronizes the shadow input field with the current list item values.
+ *
+ * @param {object} data element data
+ */
+ _syncShadow: function(data) {
+ if (!data.options.isCSV) return null;
+ if (typeof data.options.callbackSyncShadow === 'function') {
+ return data.options.callbackSyncShadow(data);
+ }
+
+ var value = '', values = this.getValues(data.element.id);
+ for (var i = 0, length = values.length; i < length; i++) {
+ value += (value.length ? ',' : '') + values[i].value;
+ }
+
+ data.shadow.value = value;
+
+ return values;
+ },
+
+ /**
+ * Handles the blur event.
+ *
+ * @param {object} event event object
+ */
+ _blur: function(event) {
+ var input = event.currentTarget;
+ var data = _data.get(input.id);
+ if (data.options.restricted) {
+ // restricted item lists only allow results from the dropdown to be picked
+ return;
+ }
+
+ var value = input.value.trim();
+ if (value.length) {
+ if (!data.suggestion || !data.suggestion.isActive()) {
+ this._addItem(input.id, { objectId: 0, value: value });
+ }
+ }
+ }
+ };
+});
+
+/**
+ * Utility class to provide a 'Jump To' overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/JumpTo
+ */
+define('WoltLabSuite/Core/Ui/Page/JumpTo',['Language', 'ObjectMap', 'Ui/Dialog'], function(Language, ObjectMap, UiDialog) {
+ "use strict";
+
+ var _activeElement = null;
+ var _buttonSubmit = null;
+ var _description = null;
+ var _elements = new ObjectMap();
+ var _input = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/JumpTo
+ */
+ var UiPageJumpTo = {
+ /**
+ * Initializes a 'Jump To' element.
+ *
+ * @param {Element} element trigger element
+ * @param {function} callback callback function, receives the page number as first argument
+ */
+ init: function(element, callback) {
+ callback = callback || null;
+ if (callback === null) {
+ var redirectUrl = elData(element, 'link');
+ if (redirectUrl) {
+ callback = function(pageNo) {
+ window.location = redirectUrl.replace(/pageNo=%d/, 'pageNo=' + pageNo);
+ };
+ }
+ else {
+ callback = function() {};
+ }
+
+ }
+ else if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid function for parameter 'callback'.");
+ }
+
+ if (!_elements.has(element)) {
+ elBySelAll('.jumpTo', element, (function(jumpTo) {
+ jumpTo.addEventListener(WCF_CLICK_EVENT, this._click.bind(this, element));
+ _elements.set(element, { callback: callback });
+ }).bind(this));
+ }
+ },
+
+ /**
+ * Handles clicks on the trigger element.
+ *
+ * @param {Element} element trigger element
+ * @param {object} event event object
+ */
+ _click: function(element, event) {
+ _activeElement = element;
+
+ if (typeof event === 'object') {
+ event.preventDefault();
+ }
+
+ UiDialog.open(this);
+
+ var pages = elData(element, 'pages');
+ _input.value = pages;
+ _input.setAttribute('max', pages);
+ _input.select();
+
+ _description.textContent = Language.get('wcf.page.jumpTo.description').replace(/#pages#/, pages);
+ },
+
+ /**
+ * Handles changes to the page number input field.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ if (event.which === 13 && _buttonSubmit.disabled === false) {
+ this._submit();
+ return;
+ }
+
+ var pageNo = ~~_input.value;
+ if (pageNo < 1 || pageNo > ~~elAttr(_input, 'max')) {
+ _buttonSubmit.disabled = true;
+ }
+ else {
+ _buttonSubmit.disabled = false;
+ }
+ },
+
+ /**
+ * Invokes the callback with the chosen page number as first argument.
+ *
+ * @param {object} event event object
+ */
+ _submit: function(event) {
+ _elements.get(_activeElement).callback(~~_input.value);
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var source = '<dl>'
+ + '<dt><label for="jsPaginationPageNo">' + Language.get('wcf.page.jumpTo') + '</label></dt>'
+ + '<dd>'
+ + '<input type="number" id="jsPaginationPageNo" value="1" min="1" max="1" class="tiny">'
+ + '<small></small>'
+ + '</dd>'
+ + '</dl>'
+ + '<div class="formSubmit">'
+ + '<button class="buttonPrimary">' + Language.get('wcf.global.button.submit') + '</button>'
+ + '</div>';
+
+ return {
+ id: 'paginationOverlay',
+ options: {
+ onSetup: (function(content) {
+ _input = elByTag('input', content)[0];
+ _input.addEventListener('keyup', this._keyUp.bind(this));
+
+ _description = elByTag('small', content)[0];
+
+ _buttonSubmit = elByTag('button', content)[0];
+ _buttonSubmit.addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ }).bind(this),
+ title: Language.get('wcf.global.page.pagination')
+ },
+ source: source
+ };
+ }
+ };
+
+ return UiPageJumpTo;
+});
+/**
+ * Callback-based pagination.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Pagination
+ */
+define('WoltLabSuite/Core/Ui/Pagination',['Core', 'Language', 'ObjectMap', 'StringUtil', 'WoltLabSuite/Core/Ui/Page/JumpTo'], function(Core, Language, ObjectMap, StringUtil, UiPageJumpTo) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiPagination(element, options) { this.init(element, options); }
+ UiPagination.prototype = {
+ /**
+ * maximum number of displayed page links, should match the PHP implementation
+ * @var {int}
+ */
+ SHOW_LINKS: 11,
+
+ /**
+ * Initializes the pagination.
+ *
+ * @param {Element} element container element
+ * @param {object} options list of initialization options
+ */
+ init: function(element, options) {
+ this._element = element;
+ this._options = Core.extend({
+ activePage: 1,
+ maxPage: 1,
+
+ callbackShouldSwitch: null,
+ callbackSwitch: null
+ }, options);
+
+ if (typeof this._options.callbackShouldSwitch !== 'function') this._options.callbackShouldSwitch = null;
+ if (typeof this._options.callbackSwitch !== 'function') this._options.callbackSwitch = null;
+
+ this._element.classList.add('pagination');
+
+ this._rebuild(this._element);
+ },
+
+ /**
+ * Rebuilds the entire pagination UI.
+ */
+ _rebuild: function() {
+ var hasHiddenPages = false;
+
+ // clear content
+ this._element.innerHTML = '';
+
+ var list = elCreate('ul'), link;
+
+ var listItem = elCreate('li');
+ listItem.className = 'skip';
+ list.appendChild(listItem);
+
+ var iconClassNames = 'icon icon24 fa-chevron-left';
+ if (this._options.activePage > 1) {
+ link = elCreate('a');
+ link.className = iconClassNames + ' jsTooltip';
+ link.href = '#';
+ link.title = Language.get('wcf.global.page.previous');
+ link.rel = 'prev';
+ listItem.appendChild(link);
+
+ link.addEventListener(WCF_CLICK_EVENT, this.switchPage.bind(this, this._options.activePage - 1));
+ }
+ else {
+ listItem.innerHTML = '<span class="' + iconClassNames + '"></span>';
+ listItem.classList.add('disabled');
+ }
+
+ // add first page
+ list.appendChild(this._createLink(1));
+
+ // calculate page links
+ var maxLinks = this.SHOW_LINKS - 4;
+ var linksBefore = this._options.activePage - 2;
+ if (linksBefore < 0) linksBefore = 0;
+ var linksAfter = this._options.maxPage - (this._options.activePage + 1);
+ if (linksAfter < 0) linksAfter = 0;
+ if (this._options.activePage > 1 && this._options.activePage < this._options.maxPage) maxLinks--;
+
+ var half = maxLinks / 2;
+ var left = this._options.activePage;
+ var right = this._options.activePage;
+ if (left < 1) left = 1;
+ if (right < 1) right = 1;
+ if (right > this._options.maxPage - 1) right = this._options.maxPage - 1;
+
+ if (linksBefore >= half) {
+ left -= half;
+ }
+ else {
+ left -= linksBefore;
+ right += half - linksBefore;
+ }
+
+ if (linksAfter >= half) {
+ right += half;
+ }
+ else {
+ right += linksAfter;
+ left -= half - linksAfter;
+ }
+
+ right = Math.ceil(right);
+ left = Math.ceil(left);
+ if (left < 1) left = 1;
+ if (right > this._options.maxPage) right = this._options.maxPage;
+
+ // left ... links
+ var jumpToHtml = '<a class="jsTooltip" title="' + Language.get('wcf.page.jumpTo') + '">…</a>';
+ if (left > 1) {
+ if (left - 1 < 2) {
+ list.appendChild(this._createLink(2));
+ }
+ else {
+ listItem = elCreate('li');
+ listItem.className = 'jumpTo';
+ listItem.innerHTML = jumpToHtml;
+ list.appendChild(listItem);
+
+ hasHiddenPages = true;
+ }
+ }
+
+ // visible links
+ for (var i = left + 1; i < right; i++) {
+ list.appendChild(this._createLink(i));
+ }
+
+ // right ... links
+ if (right < this._options.maxPage) {
+ if (this._options.maxPage - right < 2) {
+ list.appendChild(this._createLink(this._options.maxPage - 1));
+ }
+ else {
+ listItem = elCreate('li');
+ listItem.className = 'jumpTo';
+ listItem.innerHTML = jumpToHtml;
+ list.appendChild(listItem);
+
+ hasHiddenPages = true;
+ }
+ }
+
+ // add last page
+ list.appendChild(this._createLink(this._options.maxPage));
+
+ // add next button
+ listItem = elCreate('li');
+ listItem.className = 'skip';
+ list.appendChild(listItem);
+
+ iconClassNames = 'icon icon24 fa-chevron-right';
+ if (this._options.activePage < this._options.maxPage) {
+ link = elCreate('a');
+ link.className = iconClassNames + ' jsTooltip';
+ link.href = '#';
+ link.title = Language.get('wcf.global.page.next');
+ link.rel = 'next';
+ listItem.appendChild(link);
+
+ link.addEventListener(WCF_CLICK_EVENT, this.switchPage.bind(this, this._options.activePage + 1));
+ }
+ else {
+ listItem.innerHTML = '<span class="' + iconClassNames + '"></span>';
+ listItem.classList.add('disabled');
+ }
+
+ if (hasHiddenPages) {
+ elData(list, 'pages', this._options.maxPage);
+
+ UiPageJumpTo.init(list, this.switchPage.bind(this));
+ }
+
+ this._element.appendChild(list);
+ },
+
+ /**
+ * Creates a link to a specific page.
+ *
+ * @param {int} pageNo page number
+ * @return {Element} link element
+ */
+ _createLink: function(pageNo) {
+ var listItem = elCreate('li');
+ if (pageNo !== this._options.activePage) {
+ var link = elCreate('a');
+ link.textContent = StringUtil.addThousandsSeparator(pageNo);
+ link.addEventListener(WCF_CLICK_EVENT, this.switchPage.bind(this, pageNo));
+ listItem.appendChild(link);
+ }
+ else {
+ listItem.classList.add('active');
+ listItem.innerHTML = '<span>' + StringUtil.addThousandsSeparator(pageNo) + '</span><span class="invisible">' + Language.get('wcf.page.pagePosition', { pageNo: pageNo, pages: this._options.maxPage }) + '</span>';
+ }
+
+ return listItem;
+ },
+
+ /**
+ * Returns the active page.
+ *
+ * @return {integer}
+ */
+ getActivePage: function() {
+ return this._options.activePage;
+ },
+
+ /**
+ * Returns the pagination Ui element.
+ *
+ * @return {HTMLElement}
+ */
+ getElement: function() {
+ return this._element;
+ },
+
+ /**
+ * Returns the maximum page.
+ *
+ * @return {integer}
+ */
+ getMaxPage: function() {
+ return this._options.maxPage;
+ },
+
+ /**
+ * Switches to given page number.
+ *
+ * @param {int} pageNo page number
+ * @param {object} event event object
+ */
+ switchPage: function(pageNo, event) {
+ if (typeof event === 'object') {
+ event.preventDefault();
+
+ // force tooltip to vanish and strip positioning
+ if (event.currentTarget && elData(event.currentTarget, 'tooltip')) {
+ var tooltip = elById('balloonTooltip');
+ if (tooltip) {
+ Core.triggerEvent(event.currentTarget, 'mouseleave');
+ tooltip.style.removeProperty('top');
+ tooltip.style.removeProperty('bottom');
+ }
+ }
+ }
+
+ pageNo = ~~pageNo;
+
+ if (pageNo > 0 && this._options.activePage !== pageNo && pageNo <= this._options.maxPage) {
+ if (this._options.callbackShouldSwitch !== null) {
+ if (this._options.callbackShouldSwitch(pageNo) !== true) {
+ return;
+ }
+ }
+
+ this._options.activePage = pageNo;
+ this._rebuild();
+
+ if (this._options.callbackSwitch !== null) {
+ this._options.callbackSwitch(pageNo);
+ }
+ }
+ }
+ };
+
+ return UiPagination;
+});
+
+/**
+ * Smoothly scrolls to an element while accounting for potential sticky headers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Scroll
+ */
+define('WoltLabSuite/Core/Ui/Scroll',['Dom/Util'], function(DomUtil) {
+ "use strict";
+
+ var _callback = null;
+ var _callbackScroll = null;
+ var _offset = null;
+ var _timeoutScroll = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Scroll
+ */
+ return {
+ /**
+ * Scrolls to target element, optionally invoking the provided callback once scrolling has ended.
+ *
+ * @param {Element} element target element
+ * @param {function=} callback callback invoked once scrolling has ended
+ */
+ element: function(element, callback) {
+ if (!(element instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element.");
+ }
+ else if (callback !== undefined && typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback function.");
+ }
+ else if (!document.body.contains(element)) {
+ throw new Error("Element must be part of the visible DOM.");
+ }
+ else if (_callback !== null) {
+ throw new Error("Cannot scroll to element, a concurrent request is running.");
+ }
+
+ if (callback) {
+ _callback = callback;
+
+ if (_callbackScroll === null) {
+ _callbackScroll = this._onScroll.bind(this);
+ }
+
+ window.addEventListener('scroll', _callbackScroll);
+ }
+
+ var y = DomUtil.offset(element).top;
+ if (_offset === null) {
+ _offset = 50;
+ var pageHeader = elById('pageHeaderPanel');
+ if (pageHeader !== null) {
+ var position = window.getComputedStyle(pageHeader).position;
+ if (position === 'fixed' || position === 'static') {
+ _offset = pageHeader.offsetHeight;
+ }
+ else {
+ _offset = 0;
+ }
+ }
+ }
+
+ if (_offset > 0) {
+ if (y <= _offset) {
+ y = 0;
+ }
+ else {
+ // add an offset to account for a sticky header
+ y -= _offset;
+ }
+ }
+
+ var offset = window.pageYOffset;
+
+ window.scrollTo({
+ left: 0,
+ top: y,
+ behavior: 'smooth'
+ });
+
+ window.setTimeout((function () {
+ // no scrolling took place
+ if (offset === window.pageYOffset) {
+ this._onScroll();
+ }
+ }).bind(this), 100);
+ },
+
+ /**
+ * Monitors scroll event to only execute the callback once scrolling has ended.
+ *
+ * @protected
+ */
+ _onScroll: function() {
+ if (_timeoutScroll !== null) window.clearTimeout(_timeoutScroll);
+
+ _timeoutScroll = window.setTimeout(function() {
+ if (_callback !== null) _callback();
+
+ window.removeEventListener('scroll', _callbackScroll);
+ _callback = null;
+ _timeoutScroll = null;
+ }, 100);
+ }
+ };
+});
+
+/**
+ * Initializes modules required for media list view.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Media/List
+ */
+define('WoltLabSuite/Core/Controller/Media/List',[
+ 'Dom/ChangeListener',
+ 'EventHandler',
+ 'WoltLabSuite/Core/Controller/Clipboard',
+ 'WoltLabSuite/Core/Media/Clipboard',
+ 'WoltLabSuite/Core/Media/Editor',
+ 'WoltLabSuite/Core/Media/List/Upload'
+ ],
+ function(
+ DomChangeListener,
+ EventHandler,
+ Clipboard,
+ MediaClipboard,
+ MediaEditor,
+ MediaListUpload
+ ) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _addButtonEventListeners: function() {},
+ _deleteCallback: function() {},
+ _deleteMedia: function(mediaIds) {},
+ _edit: function() {}
+ };
+ return Fake;
+ }
+
+ var _mediaEditor;
+ var _tableBody = elById('mediaListTableBody');
+ var _clipboardObjectIds = [];
+ var _upload;
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Media/List
+ */
+ return {
+ init: function(options) {
+ options = options || {};
+ _upload = new MediaListUpload('uploadButton', 'mediaListTableBody', {
+ categoryId: options.categoryId,
+ multiple: true
+ });
+
+ MediaClipboard.init(
+ 'wcf\\acp\\page\\MediaListPage',
+ options.hasMarkedItems || false,
+ this
+ );
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'removedErroneousUploadRow', this._deleteCallback.bind(this));
+
+ var deleteAction = new WCF.Action.Delete('wcf\\data\\media\\MediaAction', '.jsMediaRow');
+ deleteAction.setCallback(this._deleteCallback);
+
+ _mediaEditor = new MediaEditor({
+ _editorSuccess: function(media, oldCategoryId) {
+ if (media.categoryID != oldCategoryId) {
+ window.setTimeout(function() {
+ window.location.reload();
+ }, 500);
+ }
+ }
+ });
+
+ this._addButtonEventListeners();
+
+ DomChangeListener.add('WoltLabSuite/Core/Controller/Media/List', this._addButtonEventListeners.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._openEditorAfterUpload.bind(this));
+ },
+
+ /**
+ * Adds the `click` event listeners to the media edit icons in
+ * new media table rows.
+ */
+ _addButtonEventListeners: function() {
+ var buttons = elByClass('jsMediaEditButton', _tableBody), button;
+ while (buttons.length) {
+ button = buttons[0];
+ button.classList.remove('jsMediaEditButton');
+ button.addEventListener(WCF_CLICK_EVENT, this._edit.bind(this));
+ }
+ },
+
+ /**
+ * Is triggered after media files have been deleted using the delete icon.
+ *
+ * @param {int[]?} objectIds
+ */
+ _deleteCallback: function(objectIds) {
+ var tableRowCount = elByTag('tr', _tableBody).length;
+ if (objectIds.length === undefined) {
+ if (!tableRowCount) {
+ window.location.reload();
+ }
+ }
+ else if (objectIds.length === tableRowCount) {
+ // table is empty, reload page
+ window.location.reload();
+ }
+ else {
+ Clipboard.reload.bind(Clipboard)
+ }
+ },
+
+ /**
+ * Is called when a media edit icon is clicked.
+ *
+ * @param {Event} event
+ */
+ _edit: function(event) {
+ _mediaEditor.edit(elData(event.currentTarget, 'object-id'));
+ },
+
+ /**
+ * Opens the media editor after uploading a single file.
+ *
+ * @param {object} data upload event data
+ * @since 5.2
+ */
+ _openEditorAfterUpload: function(data) {
+ if (data.upload === _upload && !data.isMultiFileUpload && !_upload.hasPendingUploads()) {
+ var keys = Object.keys(data.media);
+
+ if (keys.length) {
+ _mediaEditor.edit(this._media.get(~~data.media[keys[0]].mediaID));
+ }
+ }
+ },
+
+ /**
+ * Is called after the media files with the given ids have been deleted via clipboard.
+ *
+ * @param {int[]} mediaIds ids of deleted media files
+ */
+ clipboardDeleteMedia: function(mediaIds) {
+ var mediaRows = elByClass('jsMediaRow');
+ for (var i = 0; i < mediaRows.length; i++) {
+ var media = mediaRows[i];
+ var mediaID = ~~elData(elByClass('jsClipboardItem', media)[0], 'object-id');
+
+ if (mediaIds.indexOf(mediaID) !== -1) {
+ elRemove(media);
+ i--;
+ }
+ }
+
+ if (!mediaRows.length) {
+ window.location.reload();
+ }
+ }
+ }
+});
+/**
+ * Handles dismissible user notices.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Notice/Dismiss
+ */
+define('WoltLabSuite/Core/Controller/Notice/Dismiss',['Ajax'], function(Ajax) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Notice/Dismiss
+ */
+ var ControllerNoticeDismiss = {
+ /**
+ * Initializes dismiss buttons.
+ */
+ setup: function() {
+ var buttons = elByClass('jsDismissNoticeButton');
+
+ if (buttons.length) {
+ var clickCallback = this._click.bind(this);
+ for (var i = 0, length = buttons.length; i < length; i++) {
+ buttons[i].addEventListener(WCF_CLICK_EVENT, clickCallback);
+ }
+ }
+ },
+
+ /**
+ * Sends a request to dismiss a notice and removes it afterwards.
+ */
+ _click: function(event) {
+ var button = event.currentTarget;
+
+ Ajax.apiOnce({
+ data: {
+ actionName: 'dismiss',
+ className: 'wcf\\data\\notice\\NoticeAction',
+ objectIDs: [ elData(button, 'object-id') ]
+ },
+ success: function() {
+ elRemove(button.parentNode);
+ }
+ });
+ }
+ };
+
+ return ControllerNoticeDismiss;
+});
+
+/**
+ * Manages form field dependencies.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager',['Dictionary', 'Dom/ChangeListener', 'EventHandler', 'List', 'Dom/Traverse', 'Dom/Util', 'ObjectMap'], function(Dictionary, DomChangeListener, EventHandler, List, DomTraverse, DomUtil, ObjectMap) {
+ "use strict";
+
+ /**
+ * is `true` if containters are currently checked for their availablility, otherwise `false`
+ * @type {boolean}
+ * @private
+ */
+ var _checkingContainers = false;
+
+ /**
+ * is `true` if containter will be checked again after the current check for their availablility
+ * has finished, otherwise `false`
+ * @type {boolean}
+ * @private
+ */
+ var _checkContainersAgain = true;
+
+ /**
+ * list of containers hidden due to their own dependencies
+ * @type {List}
+ * @private
+ */
+ var _dependencyHiddenNodes = new List();
+
+ /**
+ * list of fields for which event listeners have been registered
+ * @type {Dictionary}
+ * @private
+ */
+ var _fields = new Dictionary();
+
+ /**
+ * list of registered forms
+ * @type {List}
+ * @private
+ */
+ var _forms = new List();
+
+ /**
+ * list of dependencies grouped by the dependent node they belong to
+ * @type {Dictionary}
+ * @private
+ */
+ var _nodeDependencies = new Dictionary();
+
+ /**
+ * cache of validation-related properties of hidden form fields
+ * @type {ObjectMap}
+ * @private
+ */
+ var _validatedFieldProperties = new ObjectMap();
+
+ return {
+ /**
+ * Hides the given node because of its own dependencies.
+ *
+ * @param {HTMLElement} node hidden node
+ * @protected
+ */
+ _hide: function(node) {
+ elHide(node);
+ _dependencyHiddenNodes.add(node);
+
+ // also hide tab menu entry
+ if (node.classList.contains('tabMenuContent')) {
+ elBySelAll('li', DomTraverse.prevByClass(node, 'tabMenu'), function(tabLink) {
+ if (elData(tabLink, 'name') === elData(node, 'name')) {
+ elHide(tabLink);
+ }
+ });
+ }
+
+ elBySelAll('[max], [maxlength], [min], [required]', node, function(validatedField) {
+ var properties = new Dictionary();
+
+ var max = elAttr(validatedField, 'max');
+ if (max) {
+ properties.set('max', max);
+ validatedField.removeAttribute('max');
+ }
+
+ var maxlength = elAttr(validatedField, 'maxlength');
+ if (maxlength) {
+ properties.set('maxlength', maxlength);
+ validatedField.removeAttribute('maxlength');
+ }
+
+ var min = elAttr(validatedField, 'min');
+ if (min) {
+ properties.set('min', min);
+ validatedField.removeAttribute('min');
+ }
+
+ if (validatedField.required) {
+ properties.set('required', true);
+ validatedField.removeAttribute('required');
+ }
+
+ _validatedFieldProperties.set(validatedField, properties);
+ });
+ },
+
+ /**
+ * Shows the given node because of its own dependencies.
+ *
+ * @param {HTMLElement} node shown node
+ * @protected
+ */
+ _show: function(node) {
+ elShow(node);
+ _dependencyHiddenNodes.delete(node);
+
+ // also show tab menu entry
+ if (node.classList.contains('tabMenuContent')) {
+ elBySelAll('li', DomTraverse.prevByClass(node, 'tabMenu'), function(tabLink) {
+ if (elData(tabLink, 'name') === elData(node, 'name')) {
+ elShow(tabLink);
+ }
+ });
+ }
+
+ elBySelAll('input, select', node, function(validatedField) {
+ // if a container is shown, ignore all fields that
+ // have a hidden parent element within the container
+ var parentNode = validatedField.parentNode;
+ while (parentNode !== node && parentNode.style.getPropertyValue('display') !== 'none') {
+ parentNode = parentNode.parentNode;
+ }
+
+ if (parentNode === node && _validatedFieldProperties.has(validatedField)) {
+ var properties = _validatedFieldProperties.get(validatedField);
+
+ if (properties.has('max')) {
+ elAttr(validatedField, 'max', properties.get('max'));
+ }
+ if (properties.has('maxlength')) {
+ elAttr(validatedField, 'maxlength', properties.get('maxlength'));
+ }
+ if (properties.has('min')) {
+ elAttr(validatedField, 'min', properties.get('min'));
+ }
+ if (properties.has('required')) {
+ elAttr(validatedField, 'required', '');
+ }
+
+ _validatedFieldProperties.delete(validatedField);
+ }
+ });
+ },
+
+ /**
+ * Registers a new form field dependency.
+ *
+ * @param {WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract} dependency new dependency
+ */
+ addDependency: function(dependency) {
+ var dependentNode = dependency.getDependentNode();
+ if (!_nodeDependencies.has(dependentNode.id)) {
+ _nodeDependencies.set(dependentNode.id, [dependency]);
+ }
+ else {
+ _nodeDependencies.get(dependentNode.id).push(dependency);
+ }
+
+ var fields = dependency.getFields();
+ for (var i = 0, length = fields.length; i < length; i++) {
+ var field = fields[i];
+ var id = DomUtil.identify(field);
+
+ if (!_fields.has(id)) {
+ _fields.set(id, field);
+
+ if (field.tagName === 'INPUT' && (field.type === 'checkbox' || field.type === 'radio')) {
+ field.addEventListener('change', this.checkDependencies.bind(this));
+ }
+ else {
+ field.addEventListener('input', this.checkDependencies.bind(this));
+ }
+ }
+ }
+ },
+
+ /**
+ * Checks if all dependencies are met.
+ */
+ checkDependencies: function() {
+ var obsoleteNodes = [];
+
+ _nodeDependencies.forEach(function(nodeDependencies, nodeId) {
+ var dependentNode = elById(nodeId);
+
+ // check if dependent node still exists
+ if (dependentNode === null) {
+ obsoleteNodes.push(dependentNode);
+ return;
+ }
+
+ for (var i = 0, length = nodeDependencies.length; i < length; i++) {
+ // if any dependency is not met, hide the element
+ if (!nodeDependencies[i].checkDependency()) {
+ this._hide(dependentNode);
+ return;
+ }
+ }
+
+ // all node dependency is met
+ this._show(dependentNode);
+ }.bind(this));
+
+ // delete dependencies for removed elements
+ for (var i = 0, length = obsoleteNodes.length; i < length; i++) {
+ _nodeDependencies.delete(obsoleteNodes.id);
+ }
+
+ this.checkContainers();
+ },
+
+ /**
+ * Adds the given callback to the list of callbacks called when checking containers.
+ *
+ * @param {function} callback
+ */
+ addContainerCheckCallback: function(callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback for parameter 'callback'.");
+ }
+
+ EventHandler.add('com.woltlab.wcf.form.builder.dependency', 'checkContainers', callback);
+ },
+
+ /**
+ * Checks the containers for their availability.
+ *
+ * If this function is called while containers are currently checked, the containers
+ * will be checked after the current check has been finished completely.
+ */
+ checkContainers: function() {
+ // check if containers are currently being checked
+ if (_checkingContainers === true) {
+ // and if that is the case, calling this method indicates, that after the current round,
+ // containters should be checked to properly propagate changes in children to their parents
+ _checkContainersAgain = true;
+
+ return;
+ }
+
+ // starting to check containers also resets the flag to check containers again after the current check
+ _checkingContainers = true;
+ _checkContainersAgain = false;
+
+ EventHandler.fire('com.woltlab.wcf.form.builder.dependency', 'checkContainers');
+
+ // finish checking containers and check if containters should be checked again
+ _checkingContainers = false;
+ if (_checkContainersAgain) {
+ this.checkContainers();
+ }
+ },
+
+ /**
+ * Returns `true` if the given node has been hidden because of its own dependencies.
+ *
+ * @param {HTMLElement} node checked node
+ * @return {boolean}
+ */
+ isHiddenByDependencies: function(node) {
+ if (_dependencyHiddenNodes.has(node)) {
+ return true;
+ }
+
+ var returnValue = false;
+ _dependencyHiddenNodes.forEach(function(hiddenNode) {
+ if (DomUtil.contains(hiddenNode, node)) {
+ returnValue = true;
+ }
+ });
+
+ return returnValue;
+ },
+
+ /**
+ * Registers the form with the given id with the dependency manager.
+ *
+ * @param {string} formId id of register form
+ * @throws {Error} if given form id is invalid or has already been registered
+ */
+ register: function(formId) {
+ var form = elById(formId);
+
+ if (form === null) {
+ throw new Error("Unknown element with id '" + formId + "'");
+ }
+
+ if (_forms.has(form)) {
+ throw new Error("Form with id '" + formId + "' has already been registered.");
+ }
+
+ _forms.add(form);
+ },
+
+ /**
+ * Unregisters the form with the given id and all of its dependencies.
+ *
+ * @param {string} formId id of unregistered form
+ */
+ unregister: function(formId) {
+ var form = elById(formId);
+
+ if (form === null) {
+ throw new Error("Unknown element with id '" + formId + "'");
+ }
+
+ if (!_forms.has(form)) {
+ throw new Error("Form with id '" + formId + "' has not been registered.");
+ }
+
+ _forms.delete(form);
+
+ _dependencyHiddenNodes.forEach(function(hiddenNode) {
+ if (form.contains(hiddenNode)) {
+ _dependencyHiddenNodes.delete(hiddenNode);
+ }
+ });
+ _nodeDependencies.forEach(function(dependencies, nodeId) {
+ if (form.contains(elById(nodeId))) {
+ _nodeDependencies.delete(nodeId);
+ }
+
+ for (var i = 0, length = dependencies.length; i < length; i++) {
+ var fields = dependencies[i].getFields();
+ for (var j = 0, length = fields.length; j < length; j++) {
+ var field = fields[j];
+
+ _fields.delete(field.id);
+
+ _validatedFieldProperties.delete(field);
+ }
+ }
+ });
+ }
+ };
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Field
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Field',[], function() {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderField(fieldId) {
+ this.init(fieldId);
+ };
+ FormBuilderField.prototype = {
+ /**
+ * Initializes the form field.
+ *
+ * @param {string} fieldId id of the relevant form builder field
+ */
+ init: function(fieldId) {
+ this._fieldId = fieldId;
+
+ this._readField();
+ },
+
+ /**
+ * Returns the current data of the field or a promise returning the current data
+ * of the field.
+ *
+ * @return {Promise|data}
+ */
+ _getData: function() {
+ throw new Error("Missing implementation of WoltLabSuite/Core/Form/Builder/Field/Field._getData!");
+ },
+
+ /**
+ * Reads the field HTML element.
+ */
+ _readField: function() {
+ this._field = elById(this._fieldId);
+
+ if (this._field === null) {
+ throw new Error("Unknown field with id '" + this._fieldId + "'.");
+ }
+ },
+
+ /**
+ * Destroys the field.
+ *
+ * This function is useful for remove registered elements from other APIs like dialogs.
+ */
+ destroy: function() {
+ // does nothing
+ },
+
+ /**
+ * Returns a promise returning the current data of the field.
+ *
+ * @return {Promise}
+ */
+ getData: function() {
+ return Promise.resolve(this._getData());
+ },
+
+ /**
+ * Returns the id of the field.
+ *
+ * @return {string}
+ */
+ getId: function() {
+ return this._fieldId;
+ }
+ };
+
+ return FormBuilderField;
+});
+
+/**
+ * Manager for registered Ajax forms and its fields that can be used to retrieve the current data
+ * of the registered forms.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Manager
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Manager',[
+ 'Core',
+ 'Dictionary',
+ './Field/Dependency/Manager',
+ './Field/Field'
+], function(
+ Core,
+ Dictionary,
+ FormBuilderFieldDependencyManager,
+ FormBuilderField
+) {
+ "use strict";
+
+ var _fields = new Dictionary();
+ var _forms = new Dictionary();
+
+ return {
+ /**
+ * Returns a promise returning the data of the form with the given id.
+ *
+ * @param {string} formId
+ * @return {Promise}
+ */
+ getData: function(formId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ var promises = [];
+
+ _fields.get(formId).forEach(function(field) {
+ var fieldData = field.getData();
+
+ if (!(fieldData instanceof Promise)) {
+ throw new TypeError("Data for field with id '" + field.getId() + "' is no promise.");
+ }
+
+ promises.push(fieldData);
+ });
+
+ return Promise.all(promises).then(function(promiseData) {
+ var data = {};
+
+ for (var i = 0, length = promiseData.length; i < length; i++) {
+ data = Core.extend(data, promiseData[i]);
+ }
+
+ return data;
+ });
+ },
+
+ /**
+ * Returns the registered form with given id.
+ *
+ * @param {string} formId
+ * @return {HTMLElement}
+ */
+ getForm: function(formId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ return _forms.get(formId);
+ },
+
+ /**
+ * Returns `true` if a field with the given id has been registered for the form with
+ * the given id and `false` otherwise.
+ *
+ * @param {string} formId
+ * @param {string} fieldId
+ * @return {boolean}
+ */
+ hasField: function(formId, fieldId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ return _fields.get(formId).has(fieldId);
+ },
+
+ /**
+ * Returns `true` if a form with the given id has been registered and `false`
+ * otherwise.
+ *
+ * @param {string} formId
+ * @return {boolean}
+ */
+ hasForm: function(formId) {
+ return _forms.has(formId);
+ },
+
+ /**
+ * Registers the given field for the form with the given id.
+ *
+ * @param {string} formId
+ * @param {WoltLabSuite/Core/Form/Builder/Field/Field} field
+ */
+ registerField: function(formId, field) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ if (!(field instanceof FormBuilderField)) {
+ throw new Error("Add field is no instance of 'WoltLabSuite/Core/Form/Builder/Field/Field'.");
+ }
+
+ var fieldId = field.getId();
+
+ if (this.hasField(formId, fieldId)) {
+ throw new Error("Form field with id '" + fieldId + "' has already been registered for form with id '" + fieldId + "'.");
+ }
+
+ _fields.get(formId).set(fieldId, field);
+ },
+
+ /**
+ * Registers the form with the given id.
+ *
+ * @param {string} formId
+ */
+ registerForm: function(formId) {
+ if (this.hasForm(formId)) {
+ throw new Error("Form with id '" + formId + "' has already been registered.");
+ }
+
+ var form = elById(formId);
+ if (form === null) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ _forms.set(formId, form);
+ _fields.set(formId, new Dictionary());
+ },
+
+ /**
+ * Unregisters the form with the given id.
+ *
+ * @param {string} formId
+ */
+ unregisterForm: function(formId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ _forms.delete(formId);
+
+ _fields.get(formId).forEach(function(field) {
+ field.destroy();
+ });
+
+ _fields.delete(formId);
+
+ FormBuilderFieldDependencyManager.unregister(formId);
+ }
+ };
+});
+
+/**
+ * Provides API to easily create a dialog form created by form builder.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Dialog
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Dialog',['Ajax', 'Core', './Manager', 'Ui/Dialog'], function(Ajax, Core, FormBuilderManager, UiDialog) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderDialog(dialogId, className, actionName, options) {
+ this.init(dialogId, className, actionName, options);
+ };
+ FormBuilderDialog.prototype = {
+ /**
+ * Initializes the dialog.
+ *
+ * @param {string} dialogId
+ * @param {string} className
+ * @param {string} actionName
+ * @param {{actionParameters: object, destoryOnClose: boolean, dialog: object}} options
+ */
+ init: function(dialogId, className, actionName, options) {
+ this._dialogId = dialogId;
+ this._className = className;
+ this._actionName = actionName;
+ this._options = Core.extend({
+ actionParameters: {},
+ destroyOnClose: false,
+ usesDboAction: this._className.match(/\w+\\data\\/)
+ }, options);
+ this._options.dialog = Core.extend(this._options.dialog || {}, {
+ onClose: this._dialogOnClose.bind(this)
+ });
+
+ this._formId = '';
+ this._dialogContent = '';
+ },
+
+ /**
+ * Returns the data for Ajax to setup the Ajax/Request object.
+ *
+ * @return {object} setup data for Ajax/Request object
+ */
+ _ajaxSetup: function() {
+ var options = {
+ data: {
+ actionName: this._actionName,
+ className: this._className,
+ parameters: this._options.actionParameters
+ }
+ };
+
+ // by default, `AJAXProxyAction` is used which relies on an `IDatabaseObjectAction`
+ // object; if no such object is used but an `IAJAXInvokeAction` object,
+ // `AJAXInvokeAction` has to be used
+ if (!this._options.usesDboAction) {
+ options.url = 'index.php?ajax-invoke/&t=' + SECURITY_TOKEN;
+ options.withCredentials = true;
+ }
+
+ return options;
+ },
+
+ /**
+ * Handles successful Ajax requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case this._actionName:
+ if (data.returnValues === undefined) {
+ throw new Error("Missing return data.");
+ }
+ else if (data.returnValues.dialog === undefined) {
+ throw new Error("Missing dialog template in return data.");
+ }
+ else if (data.returnValues.formId === undefined) {
+ throw new Error("Missing form id in return data.");
+ }
+
+ this._openDialogContent(data.returnValues.formId, data.returnValues.dialog);
+
+ break;
+
+ case this._options.submitActionName:
+ // if the validation failed, the dialog is shown again
+ if (data.returnValues && data.returnValues.formId && data.returnValues.dialog) {
+ if (data.returnValues.formId !== this._formId) {
+ throw new Error("Mismatch between form ids: expected '" + this._formId + "' but got '" + data.returnValues.formId + "'.");
+ }
+
+ this._openDialogContent(data.returnValues.formId, data.returnValues.dialog);
+ }
+ else {
+ this.destroy();
+
+ if (typeof this._options.successCallback === 'function') {
+ this._options.successCallback(data.returnValues || {});
+ }
+ }
+
+ break;
+
+ default:
+ throw new Error("Cannot handle action '" + data.actionName + "'.");
+ }
+ },
+
+ /**
+ * Is called when clicking on the dialog form's close button.
+ */
+ _closeDialog: function() {
+ UiDialog.close(this);
+ },
+
+ /**
+ * Is called by the dialog API when the dialog is closed.
+ */
+ _dialogOnClose: function() {
+ if (this._options.destroyOnClose) {
+ this.destroy();
+ }
+ },
+
+ /**
+ * Returns the data used to setup the dialog.
+ *
+ * @return {object} setup data
+ */
+ _dialogSetup: function() {
+ return {
+ id: this._dialogId,
+ options : this._options.dialog,
+ source: this._dialogContent
+ };
+ },
+
+ /**
+ * Is called by the dialog API when the dialog form is submitted.
+ */
+ _dialogSubmit: function() {
+ this.getData().then(this._submitForm.bind(this));
+ },
+
+ /**
+ * Opens the form dialog with the given form content.
+ *
+ * @param {string} formId
+ * @param {string} dialogContent
+ */
+ _openDialogContent: function(formId, dialogContent) {
+ this.destroy(true);
+
+ this._formId = formId;
+ this._dialogContent = dialogContent;
+
+ var dialogData = UiDialog.open(this, this._dialogContent);
+
+ var cancelButton = elBySel('button[data-type=cancel]', dialogData.content);
+ if (cancelButton !== null && !elDataBool(cancelButton, 'has-event-listener')) {
+ cancelButton.addEventListener('click', this._closeDialog.bind(this));
+ elData(cancelButton, 'has-event-listener', 1);
+ }
+ },
+
+ /**
+ * Submits the form with the given form data.
+ *
+ * @param {object} formData
+ */
+ _submitForm: function(formData) {
+ var submitButton = elBySel('button[data-type=submit]', UiDialog.getDialog(this).content);
+
+ if (typeof this._options.onSubmit === 'function') {
+ this._options.onSubmit(formData, submitButton);
+ }
+ else if (typeof this._options.submitActionName === 'string') {
+ submitButton.disabled = true;
+
+ Ajax.api(this, {
+ actionName: this._options.submitActionName,
+ parameters: {
+ data: formData,
+ formId: this._formId
+ }
+ });
+ }
+ },
+
+ /**
+ * Destroys the dialog.
+ *
+ * @param {boolean} ignoreDialog if `true`, the actual dialog is not destroyed, only the form is
+ */
+ destroy: function(ignoreDialog) {
+ if (this._formId !== '') {
+ if (FormBuilderManager.hasForm(this._formId)) {
+ FormBuilderManager.unregisterForm(this._formId);
+ }
+
+ if (ignoreDialog !== true) {
+ UiDialog.destroy(this);
+ }
+ }
+ },
+
+ /**
+ * Returns a promise that all of the dialog form's data.
+ *
+ * @return {Promise}
+ */
+ getData: function() {
+ if (this._formId === '') {
+ throw new Error("Form has not been requested yet.");
+ }
+
+ return FormBuilderManager.getData(this._formId);
+ },
+
+ /**
+ * Opens the dialog form.
+ */
+ open: function() {
+ if (UiDialog.getDialog(this._dialogId)) {
+ UiDialog.openStatic(this._dialogId);
+ }
+ else {
+ Ajax.api(this);
+ }
+ }
+ };
+
+ return FormBuilderDialog;
+});
+
+/**
+ * Provides the media search for the media manager.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Search
+ */
+define('WoltLabSuite/Core/Media/Manager/Search',['Ajax', 'Core', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language', 'Ui/SimpleDropdown'], function(Ajax, Core, DomTraverse, DomUtil, EventKey, Language, UiSimpleDropdown) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _cancelSearch: function() {},
+ _keyPress: function() {},
+ _search: function() {},
+ hideSearch: function() {},
+ resetSearch: function() {},
+ showSearch: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaManagerSearch(mediaManager) {
+ this._mediaManager = mediaManager;
+ this._searchMode = false;
+
+ this._searchContainer = elByClass('mediaManagerSearch', mediaManager.getDialog())[0];
+ this._input = elByClass('mediaManagerSearchField', mediaManager.getDialog())[0];
+ this._input.addEventListener('keypress', this._keyPress.bind(this));
+
+ this._cancelButton = elByClass('mediaManagerSearchCancelButton', mediaManager.getDialog())[0];
+ this._cancelButton.addEventListener(WCF_CLICK_EVENT, this._cancelSearch.bind(this));
+ }
+ MediaManagerSearch.prototype = {
+ /**
+ * Returns the data for Ajax to setup the Ajax/Request object.
+ *
+ * @return {object} setup data for Ajax/Request object
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getSearchResultList',
+ className: 'wcf\\data\\media\\MediaAction',
+ interfaceName: 'wcf\\data\\ISearchAction'
+ }
+ };
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ this._mediaManager.setMedia(data.returnValues.media || { }, data.returnValues.template || '', {
+ pageCount: data.returnValues.pageCount || 0,
+ pageNo: data.returnValues.pageNo || 0
+ });
+
+ elByClass('dialogContent', this._mediaManager.getDialog())[0].scrollTop = 0;
+ },
+
+ /**
+ * Cancels the search after clicking on the cancel search button.
+ */
+ _cancelSearch: function() {
+ if (this._searchMode) {
+ this._searchMode = false;
+
+ this.resetSearch();
+ this._mediaManager.resetMedia();
+ }
+ },
+
+ /**
+ * Hides the search string threshold error.
+ */
+ _hideStringThresholdError: function() {
+ var innerInfo = DomTraverse.childByClass(this._input.parentNode.parentNode, 'innerInfo');
+ if (innerInfo) {
+ elHide(innerInfo);
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` key to submit the form.
+ *
+ * @param {Event} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ if (this._input.value.length >= this._mediaManager.getOption('minSearchLength')) {
+ this._hideStringThresholdError();
+
+ this.search();
+ }
+ else {
+ this._showStringThresholdError();
+ }
+ }
+ },
+
+ /**
+ * Shows the search string threshold error.
+ */
+ _showStringThresholdError: function() {
+ var innerInfo = DomTraverse.childByClass(this._input.parentNode.parentNode, 'innerInfo');
+ if (innerInfo) {
+ elShow(innerInfo);
+ }
+ else {
+ innerInfo = elCreate('p');
+ innerInfo.className = 'innerInfo';
+ innerInfo.textContent = Language.get('wcf.media.search.info.searchStringThreshold', {
+ minSearchLength: this._mediaManager.getOption('minSearchLength')
+ });
+
+ DomUtil.insertAfter(innerInfo, this._input.parentNode);
+ }
+ },
+
+ /**
+ * Hides the media search.
+ */
+ hideSearch: function() {
+ elHide(this._searchContainer);
+ },
+
+ /**
+ * Resets the media search.
+ */
+ resetSearch: function() {
+ this._input.value = '';
+ },
+
+ /**
+ * Shows the media search.
+ */
+ showSearch: function() {
+ elShow(this._searchContainer);
+ },
+
+ /**
+ * Sends an AJAX request to fetch search results.
+ *
+ * @param {integer} pageNo
+ */
+ search: function(pageNo) {
+ if (typeof pageNo !== "number") {
+ pageNo = 1;
+ }
+
+ var searchString = this._input.value;
+ if (searchString && this._input.value.length < this._mediaManager.getOption('minSearchLength')) {
+ this._showStringThresholdError();
+
+ searchString = '';
+ }
+ else {
+ this._hideStringThresholdError();
+ }
+
+ this._searchMode = true;
+
+ Ajax.api(this, {
+ parameters: {
+ categoryID: this._mediaManager.getCategoryId(),
+ imagesOnly: this._mediaManager.getOption('imagesOnly'),
+ mode: this._mediaManager.getMode(),
+ pageNo: pageNo,
+ searchString: searchString
+ }
+ });
+ },
+ };
+
+ return MediaManagerSearch;
+});
+
+/**
+ * Provides the media manager dialog.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Base
+ */
+define(
+ 'WoltLabSuite/Core/Media/Manager/Base',[
+ 'Core', 'Dictionary', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'EventHandler', 'Language', 'List',
+ 'Permission', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Controller/Clipboard',
+ 'WoltLabSuite/Core/Media/Editor', 'WoltLabSuite/Core/Media/Upload', 'WoltLabSuite/Core/Media/Manager/Search', 'StringUtil',
+ 'WoltLabSuite/Core/Ui/Pagination',
+ 'WoltLabSuite/Core/Media/Clipboard'
+ ],
+ function(
+ Core, Dictionary, DomChangeListener, DomTraverse,
+ DomUtil, EventHandler, Language, List,
+ Permission, UiDialog, UiNotification, Clipboard,
+ MediaEditor, MediaUpload, MediaManagerSearch, StringUtil,
+ UiPagination,
+ MediaClipboard
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _addButtonEventListeners: function() {},
+ _click: function() {},
+ _dialogClose: function() {},
+ _dialogInit: function() {},
+ _dialogSetup: function() {},
+ _dialogShow: function() {},
+ _editMedia: function() {},
+ _editorClose: function() {},
+ _editorSuccess: function() {},
+ _removeClipboardCheckboxes: function() {},
+ _setMedia: function() {},
+ addMedia: function() {},
+ clipboardDeleteMedia: function() {},
+ getDialog: function() {},
+ getMode: function() {},
+ getOption: function() {},
+ removeMedia: function() {},
+ resetMedia: function() {},
+ setMedia: function() {},
+ setupMediaElement: function() {}
+ };
+ return Fake;
+ }
+
+ var _mediaManagerCounter = 0;
+
+ /**
+ * @constructor
+ */
+ function MediaManagerBase(options) {
+ this._options = Core.extend({
+ dialogTitle: Language.get('wcf.media.manager'),
+ imagesOnly: false,
+ minSearchLength: 3
+ }, options);
+
+ this._id = 'mediaManager' + _mediaManagerCounter++;
+ this._listItems = new Dictionary();
+ this._media = new Dictionary();
+ this._mediaManagerMediaList = null;
+ this._search = null;
+ this._upload = null;
+ this._forceClipboard = false;
+ this._hadInitiallyMarkedItems = false;
+ this._pagination = null;
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ this._mediaEditor = new MediaEditor(this);
+ }
+
+ DomChangeListener.add('WoltLabSuite/Core/Media/Manager', this._addButtonEventListeners.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._openEditorAfterUpload.bind(this));
+ }
+ MediaManagerBase.prototype = {
+ /**
+ * Adds click event listeners to media buttons.
+ */
+ _addButtonEventListeners: function() {
+ if (!this._mediaManagerMediaList) return;
+
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ var editIcon = elByClass('jsMediaEditButton', listItem)[0];
+ if (editIcon) {
+ editIcon.classList.remove('jsMediaEditButton');
+ editIcon.addEventListener(WCF_CLICK_EVENT, this._editMedia.bind(this));
+ }
+ }
+ }
+ },
+
+ /**
+ * Is called when a new category is selected.
+ */
+ _categoryChange: function() {
+ this._search.search();
+ },
+
+ /**
+ * Handles clicks on the media manager button.
+ *
+ * @param {object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ UiDialog.open(this);
+ },
+
+ /**
+ * Is called if the media manager dialog is closed.
+ */
+ _dialogClose: function() {
+ // only show media clipboard if editor is open
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ Clipboard.hideEditor('com.woltlab.wcf.media');
+ }
+ },
+
+ /**
+ * Initializes the dialog when first loaded.
+ *
+ * @param {string} content dialog content
+ * @param {object} data AJAX request's response data
+ */
+ _dialogInit: function(content, data) {
+ // store media data locally
+ var media = data.returnValues.media || { };
+ for (var mediaId in media) {
+ if (objOwns(media, mediaId)) {
+ this._media.set(~~mediaId, media[mediaId]);
+ }
+ }
+
+ this._initPagination(~~data.returnValues.pageCount);
+
+ this._hadInitiallyMarkedItems = data.returnValues.hasMarkedItems;
+ },
+
+ /**
+ * Returns all data to setup the media manager dialog.
+ *
+ * @return {object} dialog setup data
+ */
+ _dialogSetup: function() {
+ return {
+ id: this._id,
+ options: {
+ onClose: this._dialogClose.bind(this),
+ onShow: this._dialogShow.bind(this),
+ title: this._options.dialogTitle
+ },
+ source: {
+ after: this._dialogInit.bind(this),
+ data: {
+ actionName: 'getManagementDialog',
+ className: 'wcf\\data\\media\\MediaAction',
+ parameters: {
+ mode: this.getMode(),
+ imagesOnly: this._options.imagesOnly
+ }
+ }
+ }
+ };
+ },
+
+ /**
+ * Is called if the media manager dialog is shown.
+ */
+ _dialogShow: function() {
+ if (!this._mediaManagerMediaList) {
+ var dialog = this.getDialog();
+
+ this._mediaManagerMediaList = elByClass('mediaManagerMediaList', dialog)[0];
+
+ this._mediaCategorySelect = elBySel('.mediaManagerCategoryList > select', dialog);
+ if (this._mediaCategorySelect) {
+ this._mediaCategorySelect.addEventListener('change', this._categoryChange.bind(this));
+ }
+
+ // store list items locally
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ this._listItems.set(~~elData(listItem, 'object-id'), listItem);
+ }
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ var uploadButton = elByClass('mediaManagerMediaUploadButton', UiDialog.getDialog(this).dialog)[0];
+ this._upload = new MediaUpload(DomUtil.identify(uploadButton), DomUtil.identify(this._mediaManagerMediaList), {
+ mediaManager: this
+ });
+
+ var deleteAction = new WCF.Action.Delete('wcf\\data\\media\\MediaAction', '.mediaFile');
+ deleteAction._didTriggerEffect = function(element) {
+ this.removeMedia(elData(element[0], 'object-id'));
+ }.bind(this);
+ }
+
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ MediaClipboard.init(
+ 'menuManagerDialog-' + this.getMode(),
+ this._hadInitiallyMarkedItems ? true : false,
+ this
+ );
+ }
+ else {
+ this._removeClipboardCheckboxes();
+ }
+
+ this._search = new MediaManagerSearch(this);
+
+ if (!listItems.length) {
+ this._search.hideSearch();
+ }
+ }
+
+ // only show media clipboard if editor is open
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ Clipboard.showEditor('com.woltlab.wcf.media');
+ }
+ },
+
+ /**
+ * Opens the media editor for a media file.
+ *
+ * @param {Event} event event object for clicks on edit icons
+ */
+ _editMedia: function(event) {
+ if (!Permission.get('admin.content.cms.canManageMedia')) {
+ throw new Error("You are not allowed to edit media files.");
+ }
+
+ UiDialog.close(this);
+
+ this._mediaEditor.edit(this._media.get(~~elData(event.currentTarget, 'object-id')));
+ },
+
+ /**
+ * Re-opens the manager dialog after closing the editor dialog.
+ */
+ _editorClose: function() {
+ UiDialog.open(this);
+ },
+
+ /**
+ * Re-opens the manager dialog and updates the media data after
+ * successfully editing a media file.
+ *
+ * @param {object} media updated media file data
+ * @param {integer} oldCategoryId old category id
+ */
+ _editorSuccess: function(media, oldCategoryId) {
+ // if the category changed of media changed and category
+ // is selected, check if media list needs to be refreshed
+ if (this._mediaCategorySelect) {
+ var selectedCategoryId = ~~this._mediaCategorySelect.value;
+
+ if (selectedCategoryId) {
+ var newCategoryId = ~~media.categoryID;
+
+ if (oldCategoryId != newCategoryId && (oldCategoryId == selectedCategoryId || newCategoryId == selectedCategoryId)) {
+ this._search.search();
+ }
+ }
+ }
+
+ UiDialog.open(this);
+
+ this._media.set(~~media.mediaID, media);
+
+ var listItem = this._listItems.get(~~media.mediaID);
+ var p = elByClass('mediaTitle', listItem)[0];
+ if (media.isMultilingual) {
+ p.textContent = media.title[LANGUAGE_ID] || media.filename;
+ }
+ else {
+ p.textContent = media.title[media.languageID] || media.filename;
+ }
+ },
+
+ /**
+ * Initializes the dialog pagination.
+ *
+ * @param {integer} pageCount
+ * @param {integer} pageNo
+ */
+ _initPagination: function(pageCount, pageNo) {
+ if (pageNo === undefined) pageNo = 1;
+
+ if (pageCount > 1) {
+ var newPagination = elCreate('div');
+ newPagination.className = 'paginationBottom jsPagination';
+ DomUtil.replaceElement(elBySel('.jsPagination', UiDialog.getDialog(this).content), newPagination);
+
+ this._pagination = new UiPagination(newPagination, {
+ activePage: pageNo,
+ callbackSwitch: this._search.search.bind(this._search),
+ maxPage: pageCount
+ });
+ }
+ else if (this._pagination) {
+ elHide(this._pagination.getElement());
+ }
+ },
+
+ /**
+ * Removes all media clipboard checkboxes.
+ */
+ _removeClipboardCheckboxes: function() {
+ var checkboxes = elByClass('mediaCheckbox', this._mediaManagerMediaList);
+ while (checkboxes.length) {
+ elRemove(checkboxes[0]);
+ }
+ },
+
+ /**
+ * Opens the media editor after uploading a single file.
+ *
+ * @param {object} data upload event data
+ * @since 5.2
+ */
+ _openEditorAfterUpload: function(data) {
+ if (data.upload === this._upload && !data.isMultiFileUpload && !this._upload.hasPendingUploads()) {
+ var keys = Object.keys(data.media);
+
+ if (keys.length) {
+ UiDialog.close(this);
+
+ this._mediaEditor.edit(this._media.get(~~data.media[keys[0]].mediaID));
+ }
+ }
+ },
+
+ /**
+ * Sets the displayed media (after a search).
+ *
+ * @param {Dictionary} media media to be set as active
+ */
+ _setMedia: function(media) {
+ if (Core.isPlainObject(media)) {
+ this._media = Dictionary.fromObject(media);
+ }
+ else {
+ this._media = media;
+ }
+
+ var info = DomTraverse.nextByClass(this._mediaManagerMediaList, 'info');
+
+ if (this._media.size) {
+ if (info) {
+ elHide(info);
+ }
+ }
+ else {
+ if (info === null) {
+ info = elCreate('p');
+ info.className = 'info';
+ info.textContent = Language.get('wcf.media.search.noResults');
+ }
+
+ elShow(info);
+ DomUtil.insertAfter(info, this._mediaManagerMediaList);
+ }
+
+ var mediaListItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = mediaListItems.length; i < length; i++) {
+ var listItem = mediaListItems[i];
+
+ if (!this._media.has(elData(listItem, 'object-id'))) {
+ elHide(listItem);
+ }
+ else {
+ elShow(listItem);
+ }
+ }
+
+ DomChangeListener.trigger();
+
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ Clipboard.reload();
+ }
+ else {
+ this._removeClipboardCheckboxes();
+ }
+ },
+
+ /**
+ * Adds a media file to the manager.
+ *
+ * @param {object} media data of the media file
+ * @param {Element} listItem list item representing the file
+ */
+ addMedia: function(media, listItem) {
+ if (!media.languageID) media.isMultilingual = 1;
+
+ this._media.set(~~media.mediaID, media);
+ this._listItems.set(~~media.mediaID, listItem);
+
+ if (this._listItems.size === 1) {
+ this._search.showSearch();
+ }
+ },
+
+ /**
+ * Is called after the media files with the given ids have been deleted via clipboard.
+ *
+ * @param {int[]} mediaIds ids of deleted media files
+ */
+ clipboardDeleteMedia: function(mediaIds) {
+ for (var i = 0, length = mediaIds.length; i < length; i++) {
+ this.removeMedia(~~mediaIds[i], true);
+ }
+
+ UiNotification.show();
+ },
+
+ /**
+ * Returns the id of the currently selected category or `0` if no category is selected.
+ *
+ * @return {integer}
+ */
+ getCategoryId: function() {
+ if (this._mediaCategorySelect) {
+ return this._mediaCategorySelect.value;
+ }
+
+ return 0;
+ },
+
+ /**
+ * Returns the media manager dialog element.
+ *
+ * @return {Element} media manager dialog
+ */
+ getDialog: function() {
+ return UiDialog.getDialog(this).dialog;
+ },
+
+ /**
+ * Returns the mode of the media manager.
+ *
+ * @return {string}
+ */
+ getMode: function() {
+ return '';
+ },
+
+ /**
+ * Returns the media manager option with the given name.
+ *
+ * @param {string} name option name
+ * @return {mixed} option value or null
+ */
+ getOption: function(name) {
+ if (this._options[name]) {
+ return this._options[name];
+ }
+
+ return null;
+ },
+
+ /**
+ * Removes a media file.
+ *
+ * @param {int} mediaId id of the removed media file
+ */
+ removeMedia: function(mediaId) {
+ if (this._listItems.has(mediaId)) {
+ // remove list item
+ try {
+ elRemove(this._listItems.get(mediaId));
+ }
+ catch (e) {
+ // ignore errors if item has already been removed like by WCF.Action.Delete
+ }
+
+ this._listItems.delete(mediaId);
+ this._media.delete(mediaId);
+ }
+ },
+
+ /**
+ * Changes the displayed media to the previously displayed media.
+ */
+ resetMedia: function() {
+ // calling WoltLabSuite/Core/Media/Manager/Search.search() reloads the first page of the dialog
+ this._search.search();
+ },
+
+ /**
+ * Sets the media files currently displayed.
+ *
+ * @param {object} media media data
+ * @param {string} template
+ * @param {object} additionalData
+ */
+ setMedia: function(media, template, additionalData) {
+ var hasMedia = false;
+ for (var mediaId in media) {
+ if (objOwns(media, mediaId)) {
+ hasMedia = true;
+ }
+ }
+
+ var newListItems = [];
+ if (hasMedia) {
+ var ul = elCreate('ul');
+ ul.innerHTML = template;
+
+ var listItems = DomTraverse.childrenByTag(ul, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+ if (!this._listItems.has(~~elData(listItem, 'object-id'))) {
+ this._listItems.set(elData(listItem, 'object-id'), listItem);
+
+ this._mediaManagerMediaList.appendChild(listItem);
+ }
+ }
+ }
+
+ this._initPagination(additionalData.pageCount, additionalData.pageNo);
+
+ this._setMedia(media);
+ },
+
+ /**
+ * Sets up a new media element.
+ *
+ * @param {object} media data of the media file
+ * @param {HTMLElement} mediaElement element representing the media file
+ */
+ setupMediaElement: function(media, mediaElement) {
+ var mediaInformation = DomTraverse.childByClass(mediaElement, 'mediaInformation');
+
+ var buttonGroupNavigation = elCreate('nav');
+ buttonGroupNavigation.className = 'jsMobileNavigation buttonGroupNavigation';
+ mediaInformation.parentNode.appendChild(buttonGroupNavigation);
+
+ var buttons = elCreate('ul');
+ buttons.className = 'buttonList iconList';
+ buttonGroupNavigation.appendChild(buttons);
+
+ var listItem = elCreate('li');
+ listItem.className = 'mediaCheckbox';
+ buttons.appendChild(listItem);
+
+ var a = elCreate('a');
+ listItem.appendChild(a);
+
+ var label = elCreate('label');
+ a.appendChild(label);
+
+ var checkbox = elCreate('input');
+ checkbox.className = 'jsClipboardItem';
+ elAttr(checkbox, 'type', 'checkbox');
+ elData(checkbox, 'object-id', media.mediaID);
+ label.appendChild(checkbox);
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ listItem = elCreate('li');
+ listItem.className = 'jsMediaEditButton';
+ elData(listItem, 'object-id', media.mediaID);
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-pencil jsTooltip" title="' + Language.get('wcf.global.button.edit') + '"></span> <span class="invisible">' + Language.get('wcf.global.button.edit') + '</span></a>';
+
+ listItem = elCreate('li');
+ listItem.className = 'jsDeleteButton';
+ elData(listItem, 'object-id', media.mediaID);
+
+ // use temporary title to not unescape html in filename
+ var uuid = Core.getUuid();
+ elData(listItem, 'confirm-message-html', StringUtil.unescapeHTML(Language.get('wcf.media.delete.confirmMessage', {
+ title: uuid
+ })).replace(uuid, StringUtil.escapeHTML(media.filename)));
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-times jsTooltip" title="' + Language.get('wcf.global.button.delete') + '"></span> <span class="invisible">' + Language.get('wcf.global.button.delete') + '</span></a>';
+ }
+ }
+ };
+
+ return MediaManagerBase;
+});
+
+/**
+ * Provides the media manager dialog for selecting media for Redactor editors.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Editor
+ */
+define('WoltLabSuite/Core/Media/Manager/Editor',['Core', 'Dictionary', 'Dom/Traverse', 'EventHandler', 'Language', 'Permission', 'Ui/Dialog', 'WoltLabSuite/Core/Controller/Clipboard', 'WoltLabSuite/Core/Media/Manager/Base'],
+ function(Core, Dictionary, DomTraverse, EventHandler, Language, Permission, UiDialog, ControllerClipboard, MediaManagerBase) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _addButtonEventListeners: function() {},
+ _buildInsertDialog: function() {},
+ _click: function() {},
+ _getInsertDialogId: function() {},
+ _getThumbnailSizes: function() {},
+ _insertMedia: function() {},
+ _insertMediaGallery: function() {},
+ _insertMediaItem: function() {},
+ _openInsertDialog: function() {},
+ insertMedia: function() {},
+ getMode: function() {},
+ setupMediaElement: function() {},
+ _dialogClose: function() {},
+ _dialogInit: function() {},
+ _dialogSetup: function() {},
+ _dialogShow: function() {},
+ _editMedia: function() {},
+ _editorClose: function() {},
+ _editorSuccess: function() {},
+ _removeClipboardCheckboxes: function() {},
+ _setMedia: function() {},
+ addMedia: function() {},
+ clipboardInsertMedia: function() {},
+ getDialog: function() {},
+ getOption: function() {},
+ removeMedia: function() {},
+ resetMedia: function() {},
+ setMedia: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaManagerEditor(options) {
+ options = Core.extend({
+ callbackInsert: null
+ }, options);
+
+ MediaManagerBase.call(this, options);
+
+ this._forceClipboard = true;
+ this._activeButton = null;
+ var context = (this._options.editor) ? this._options.editor.core.toolbar()[0] : undefined;
+ this._buttons = elByClass(this._options.buttonClass || 'jsMediaEditorButton', context);
+ for (var i = 0, length = this._buttons.length; i < length; i++) {
+ this._buttons[i].addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }
+ this._mediaToInsert = new Dictionary();
+ this._mediaToInsertByClipboard = false;
+ this._uploadData = null;
+ this._uploadId = null;
+
+ if (this._options.editor && !this._options.editor.opts.woltlab.attachments) {
+ var editorId = elData(this._options.editor.$editor[0], 'element-id');
+
+ var uuid1 = EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, this._editorUpload.bind(this));
+ var uuid2 = EventHandler.add('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + editorId, this._editorUpload.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'destory_' + editorId, function() {
+ EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid1);
+ EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid2);
+ });
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._mediaUploaded.bind(this));
+ }
+ }
+ Core.inherit(MediaManagerEditor, MediaManagerBase, {
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_addButtonEventListeners
+ */
+ _addButtonEventListeners: function() {
+ MediaManagerEditor._super.prototype._addButtonEventListeners.call(this);
+
+ if (!this._mediaManagerMediaList) return;
+
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ var insertIcon = elByClass('jsMediaInsertButton', listItem)[0];
+ if (insertIcon) {
+ insertIcon.classList.remove('jsMediaInsertButton');
+ insertIcon.addEventListener(WCF_CLICK_EVENT, this._openInsertDialog.bind(this));
+ }
+ }
+ },
+
+ /**
+ * Builds the dialog to setup inserting media files.
+ */
+ _buildInsertDialog: function() {
+ var thumbnailOptions = '';
+
+ var thumbnailSizes = this._getThumbnailSizes();
+ for (var i = 0, length = thumbnailSizes.length; i < length; i++) {
+ thumbnailOptions += '<option value="' + thumbnailSizes[i] + '">' + Language.get('wcf.media.insert.imageSize.' + thumbnailSizes[i]) + '</option>';
+ }
+ thumbnailOptions += '<option value="original">' + Language.get('wcf.media.insert.imageSize.original') + '</option>';
+
+ var dialog = '<div class="section">'
+ /*+ (this._mediaToInsert.size > 1 ? '<dl>'
+ + '<dt>' + Language.get('wcf.media.insert.type') + '</dt>'
+ + '<dd>'
+ + '<select name="insertType">'
+ + '<option value="separate">' + Language.get('wcf.media.insert.type.separate') + '</option>'
+ + '<option value="gallery">' + Language.get('wcf.media.insert.type.gallery') + '</option>'
+ + '</select>'
+ + '</dd>'
+ + '</dl>' : '')*/
+ + '<dl class="thumbnailSizeSelection">'
+ + '<dt>' + Language.get('wcf.media.insert.imageSize') + '</dt>'
+ + '<dd>'
+ + '<select name="thumbnailSize">'
+ + thumbnailOptions
+ + '</select>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button class="buttonPrimary">' + Language.get('wcf.global.button.insert') + '</button>'
+ + '</div>';
+
+ UiDialog.open({
+ _dialogSetup: (function() {
+ return {
+ id: this._getInsertDialogId(),
+ options: {
+ onClose: this._editorClose.bind(this),
+ onSetup: function(content) {
+ elByClass('buttonPrimary', content)[0].addEventListener(WCF_CLICK_EVENT, this._insertMedia.bind(this));
+
+ // toggle thumbnail size selection based on selected insert type
+ /*var insertType = elBySel('select[name=insertType]', content);
+ if (insertType !== null) {
+ var thumbnailSelection = elByClass('thumbnailSizeSelection', content)[0];
+ insertType.addEventListener('change', function(event) {
+ if (event.currentTarget.value === 'gallery') {
+ elHide(thumbnailSelection);
+ }
+ else {
+ elShow(thumbnailSelection);
+ }
+ });
+ }*/
+ var thumbnailSelection = elBySel('.thumbnailSizeSelection', content);
+ elShow(thumbnailSelection);
+ }.bind(this),
+ title: Language.get('wcf.media.insert')
+ },
+ source: dialog
+ };
+ }).bind(this)
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_click
+ */
+ _click: function(event) {
+ this._activeButton = event.currentTarget;
+
+ MediaManagerEditor._super.prototype._click.call(this, event);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_dialogShow
+ */
+ _dialogShow: function() {
+ MediaManagerEditor._super.prototype._dialogShow.call(this);
+
+ // check if data needs to be uploaded
+ if (this._uploadData) {
+ if (this._uploadData.file) {
+ this._upload.uploadFile(this._uploadData.file);
+ }
+ else {
+ this._uploadId = this._upload.uploadBlob(this._uploadData.blob);
+ }
+
+ this._uploadData = null;
+ }
+ },
+
+ /**
+ * Handles pasting and dragging and dropping files into the editor.
+ *
+ * @param {object} data data of the uploaded file
+ */
+ _editorUpload: function(data) {
+ this._uploadData = data;
+
+ UiDialog.open(this);
+ },
+
+ /**
+ * Returns the id of the insert dialog based on the media files to be inserted.
+ *
+ * @return {string} insert dialog id
+ */
+ _getInsertDialogId: function() {
+ var dialogId = 'mediaInsert';
+
+ this._mediaToInsert.forEach(function(media, mediaId) {
+ dialogId += '-' + mediaId;
+ });
+
+ return dialogId;
+ },
+
+ /**
+ * Returns the supported thumbnail sizes (excluding `original`) for all media images to be inserted.
+ *
+ * @return {string[]}
+ */
+ _getThumbnailSizes: function() {
+ var sizes = [];
+
+ var supportedSizes = ['small', 'medium', 'large'];
+ var size, supportSize;
+ for (var i = 0, length = supportedSizes.length; i < length; i++) {
+ size = supportedSizes[i];
+
+ supportSize = true;
+ this._mediaToInsert.forEach(function(media) {
+ if (!media[size + 'ThumbnailType']) {
+ supportSize = false;
+ }
+ });
+
+ if (supportSize) {
+ sizes.push(size);
+ }
+ }
+
+ return sizes;
+ },
+
+ /**
+ * Inserts media files into redactor.
+ *
+ * @param {Event?} event
+ * @param {string?} thumbnailSize
+ * @param {boolean?} closeEditor
+ */
+ _insertMedia: function(event, thumbnailSize, closeEditor) {
+ if (closeEditor === undefined) closeEditor = true;
+
+ var insertType = 'separate';
+
+ // update insert options with selected values if method is called by clicking on 'insert' button
+ // in dialog
+ if (event) {
+ UiDialog.close(this._getInsertDialogId());
+
+ var dialogContent = event.currentTarget.closest('.dialogContent');
+
+ /*if (this._mediaToInsert.size > 1) {
+ insertType = elBySel('select[name=insertType]', dialogContent).value;
+ }*/
+ thumbnailSize = elBySel('select[name=thumbnailSize]', dialogContent).value;
+ }
+
+ if (this._options.callbackInsert !== null) {
+ this._options.callbackInsert(this._mediaToInsert, insertType, thumbnailSize);
+ }
+ else {
+ if (insertType === 'separate') {
+ this._options.editor.buffer.set();
+
+ this._mediaToInsert.forEach(this._insertMediaItem.bind(this, thumbnailSize));
+ }
+ else {
+ this._insertMediaGallery();
+ }
+ }
+
+ if (this._mediaToInsertByClipboard) {
+ var mediaIds = [];
+ this._mediaToInsert.forEach(function(media) {
+ mediaIds.push(media.mediaID);
+ });
+
+ ControllerClipboard.unmark('com.woltlab.wcf.media', mediaIds);
+ }
+
+ this._mediaToInsert = new Dictionary();
+ this._mediaToInsertByClipboard = false;
+
+ // close manager dialog
+ if (closeEditor) {
+ UiDialog.close(this);
+ }
+ },
+
+ /**
+ * Inserts a series of uploaded images using a slider.
+ *
+ * @protected
+ */
+ _insertMediaGallery: function() {
+ var mediaIds = [];
+ this._mediaToInsert.forEach(function(item) {
+ mediaIds.push(item.mediaID);
+ });
+
+ this._options.editor.buffer.set();
+ this._options.editor.insert.text("[wsmg='" + mediaIds.join(',') + "'][/wsmg]");
+ },
+
+ /**
+ * Inserts a single media item.
+ *
+ * @param {string} thumbnailSize preferred image dimension, is ignored for non-images
+ * @param {Object} item media item data
+ * @protected
+ */
+ _insertMediaItem: function(thumbnailSize, item) {
+ if (item.isImage) {
+ var sizes = ['small', 'medium', 'large', 'original'];
+
+ // check if size is actually available
+ var available = '', size;
+ for (var i = 0; i < 4; i++) {
+ size = sizes[i];
+
+ if (item[size + 'ThumbnailHeight'] != 0) {
+ available = size;
+
+ if (thumbnailSize == size) {
+ break;
+ }
+ }
+ }
+
+ thumbnailSize = available;
+
+ if (!thumbnailSize) thumbnailSize = 'original';
+
+ var link = item.link;
+ if (thumbnailSize !== 'original') {
+ link = item[thumbnailSize + 'ThumbnailLink'];
+ }
+
+ this._options.editor.insert.html('<img src="' + link + '" class="woltlabSuiteMedia" data-media-id="' + item.mediaID + '" data-media-size="' + thumbnailSize + '">');
+ }
+ else {
+ this._options.editor.insert.text("[wsm='" + item.mediaID + "'][/wsm]");
+ }
+ },
+
+ /**
+ * Is called after media files are successfully uploaded to insert copied media.
+ *
+ * @param {object} data upload data
+ */
+ _mediaUploaded: function(data) {
+ if (this._uploadId !== null && this._upload === data.upload) {
+ if (this._uploadId === data.uploadId || (Array.isArray(this._uploadId) && this._uploadId.indexOf(data.uploadId) !== -1)) {
+ this._mediaToInsert = Dictionary.fromObject(data.media);
+ this._insertMedia(null, 'medium', false);
+
+ this._uploadId = null;
+ }
+ }
+ },
+
+ /**
+ * Handles clicking on the insert button.
+ *
+ * @param {Event} event insert button click event
+ */
+ _openInsertDialog: function(event) {
+ this.insertMedia([~~elData(event.currentTarget, 'object-id')]);
+ },
+
+ /**
+ * Is called to insert the media files with the given ids into an editor.
+ *
+ * @param {int[]} mediaIds
+ */
+ clipboardInsertMedia: function(mediaIds) {
+ this.insertMedia(mediaIds, true);
+ },
+
+ /**
+ * Prepares insertion of the media files with the given ids.
+ *
+ * @param {array<int>} mediaIds ids of the media files to be inserted
+ * @param {boolean?} insertedByClipboard is true if the media files are inserted by clipboard
+ */
+ insertMedia: function(mediaIds, insertedByClipboard) {
+ this._mediaToInsert = new Dictionary();
+ this._mediaToInsertByClipboard = insertedByClipboard || false;
+
+ // open the insert dialog if all media files are images
+ var imagesOnly = true, media;
+ for (var i = 0, length = mediaIds.length; i < length; i++) {
+ media = this._media.get(mediaIds[i]);
+ this._mediaToInsert.set(media.mediaID, media);
+
+ if (!media.isImage) {
+ imagesOnly = false;
+ }
+ }
+
+ if (imagesOnly) {
+ var thumbnailSizes = this._getThumbnailSizes();
+ if (thumbnailSizes.length) {
+ UiDialog.close(this);
+ var dialogId = this._getInsertDialogId();
+ if (UiDialog.getDialog(dialogId)) {
+ UiDialog.openStatic(dialogId);
+ }
+ else {
+ this._buildInsertDialog();
+ }
+ }
+ else {
+ this._insertMedia(undefined, 'original');
+ }
+ }
+ else {
+ this._insertMedia();
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#getMode
+ */
+ getMode: function() {
+ return 'editor';
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#setupMediaElement
+ */
+ setupMediaElement: function(media, mediaElement) {
+ MediaManagerEditor._super.prototype.setupMediaElement.call(this, media, mediaElement);
+
+ // add media insertion icon
+ var buttons = elBySel('nav.buttonGroupNavigation > ul', mediaElement);
+
+ var listItem = elCreate('li');
+ listItem.className = 'jsMediaInsertButton';
+ elData(listItem, 'object-id', media.mediaID);
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-plus jsTooltip" title="' + Language.get('wcf.media.button.insert') + '"></span> <span class="invisible">' + Language.get('wcf.media.button.insert') + '</span></a>';
+ }
+ });
+
+ return MediaManagerEditor;
+});
+
+/**
+ * Provides the media manager dialog for selecting media for input elements.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Select
+ */
+define('WoltLabSuite/Core/Media/Manager/Select',['Core', 'Dom/Traverse', 'Dom/Util', 'Language', 'ObjectMap', 'Ui/Dialog', 'WoltLabSuite/Core/FileUtil', 'WoltLabSuite/Core/Media/Manager/Base'],
+ function(Core, DomTraverse, DomUtil, Language, ObjectMap, UiDialog, FileUtil, MediaManagerBase) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _addButtonEventListeners: function() {},
+ _chooseMedia: function() {},
+ _click: function() {},
+ getMode: function() {},
+ setupMediaElement: function() {},
+ _removeMedia: function() {},
+ _clipboardAction: function() {},
+ _dialogClose: function() {},
+ _dialogInit: function() {},
+ _dialogSetup: function() {},
+ _dialogShow: function() {},
+ _editMedia: function() {},
+ _editorClose: function() {},
+ _editorSuccess: function() {},
+ _removeClipboardCheckboxes: function() {},
+ _setMedia: function() {},
+ addMedia: function() {},
+ getDialog: function() {},
+ getOption: function() {},
+ removeMedia: function() {},
+ resetMedia: function() {},
+ setMedia: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaManagerSelect(options) {
+ MediaManagerBase.call(this, options);
+
+ this._activeButton = null;
+ this._buttons = elByClass(this._options.buttonClass || 'jsMediaSelectButton');
+ this._storeElements = new ObjectMap();
+
+ for (var i = 0, length = this._buttons.length; i < length; i++) {
+ var button = this._buttons[i];
+
+ // only consider buttons with a proper store specified
+ var store = elData(button, 'store');
+ if (store) {
+ var storeElement = elById(store);
+ if (storeElement && storeElement.tagName === 'INPUT') {
+ this._buttons[i].addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+
+ this._storeElements.set(button, storeElement);
+
+ // add remove button
+ var removeButton = elCreate('p');
+ removeButton.className = 'button';
+ DomUtil.insertAfter(removeButton, button);
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-times';
+ removeButton.appendChild(icon);
+
+ if (!storeElement.value) elHide(removeButton);
+ removeButton.addEventListener(WCF_CLICK_EVENT, this._removeMedia.bind(this));
+ }
+ }
+ }
+ }
+ Core.inherit(MediaManagerSelect, MediaManagerBase, {
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_addButtonEventListeners
+ */
+ _addButtonEventListeners: function() {
+ MediaManagerSelect._super.prototype._addButtonEventListeners.call(this);
+
+ if (!this._mediaManagerMediaList) return;
+
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ var chooseIcon = elByClass('jsMediaSelectButton', listItem)[0];
+ if (chooseIcon) {
+ chooseIcon.classList.remove('jsMediaSelectButton');
+ chooseIcon.addEventListener(WCF_CLICK_EVENT, this._chooseMedia.bind(this));
+ }
+ }
+ },
+
+ /**
+ * Handles clicking on a media choose icon.
+ *
+ * @param {Event} event click event
+ */
+ _chooseMedia: function(event) {
+ if (this._activeButton === null) {
+ throw new Error("Media cannot be chosen if no button is active.");
+ }
+
+ var media = this._media.get(~~elData(event.currentTarget, 'object-id'));
+
+ // save selected media in store element
+ elById(elData(this._activeButton, 'store')).value = media.mediaID;
+
+ // display selected media
+ var display = elData(this._activeButton, 'display');
+ if (display) {
+ var displayElement = elById(display);
+ if (displayElement) {
+ if (media.isImage) {
+ displayElement.innerHTML = '<img src="' + (media.smallThumbnailLink ? media.smallThumbnailLink : media.link) + '" alt="' + (media.altText && media.altText[LANGUAGE_ID] ? media.altText[LANGUAGE_ID] : '') + '" />';
+ }
+ else {
+ var fileIcon = FileUtil.getIconNameByFilename(media.filename);
+ if (fileIcon) {
+ fileIcon = '-' + fileIcon;
+ }
+
+ displayElement.innerHTML = '<div class="box48" style="margin-bottom: 10px;">'
+ + '<span class="icon icon48 fa-file' + fileIcon + '-o"></span>'
+ + '<div class="containerHeadline">'
+ + '<h3>' + media.filename + '</h3>'
+ + '<p>' + media.formattedFilesize + '</p>'
+ + '</div>'
+ + '</div>';
+ }
+ }
+ }
+
+ // show remove button
+ elShow(this._activeButton.nextElementSibling);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_click
+ */
+ _click: function(event) {
+ event.preventDefault();
+ this._activeButton = event.currentTarget;
+
+ MediaManagerSelect._super.prototype._click.call(this, event);
+
+ if (!this._mediaManagerMediaList) return;
+
+ var storeElement = this._storeElements.get(this._activeButton);
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI'), listItem;
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ listItem = listItems[i];
+ if (storeElement.value && storeElement.value == elData(listItem, 'object-id')) {
+ listItem.classList.add('jsSelected');
+ }
+ else {
+ listItem.classList.remove('jsSelected');
+ }
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#getMode
+ */
+ getMode: function() {
+ return 'select';
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#setupMediaElement
+ */
+ setupMediaElement: function(media, mediaElement) {
+ MediaManagerSelect._super.prototype.setupMediaElement.call(this, media, mediaElement);
+
+ // add media insertion icon
+ var buttons = elBySel('nav.buttonGroupNavigation > ul', mediaElement);
+
+ var listItem = elCreate('li');
+ listItem.className = 'jsMediaSelectButton';
+ elData(listItem, 'object-id', media.mediaID);
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-check jsTooltip" title="' + Language.get('wcf.media.button.select') + '"></span> <span class="invisible">' + Language.get('wcf.media.button.select') + '</span></a>';
+ },
+
+ /**
+ * Handles clicking on the remove button.
+ *
+ * @param {Event} event click event
+ */
+ _removeMedia: function(event) {
+ event.preventDefault();
+
+ var removeButton = event.currentTarget;
+ elHide(removeButton);
+
+ var button = removeButton.previousElementSibling;
+ elById(elData(button, 'store')).value = 0;
+ var display = elData(button, 'display');
+ if (display) {
+ var displayElement = elById(display);
+ if (displayElement) {
+ displayElement.innerHTML = '';
+ }
+ }
+ }
+ });
+
+ return MediaManagerSelect;
+});
+
+/**
+ * Provides suggestions using an input field, designed to work with `wcf\data\ISearchAction`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Search/Input
+ */
+define('WoltLabSuite/Core/Ui/Search/Input',['Ajax', 'Core', 'EventKey', 'Dom/Util', 'Ui/SimpleDropdown'], function(Ajax, Core, EventKey, DomUtil, UiSimpleDropdown) {
+ "use strict";
+
+ /**
+ * @param {Element} element target input[type="text"]
+ * @param {Object} options search options and settings
+ * @constructor
+ */
+ function UiSearchInput(element, options) { this.init(element, options); }
+ UiSearchInput.prototype = {
+ /**
+ * Initializes the search input field.
+ *
+ * @param {Element} element target input[type="text"]
+ * @param {Object} options search options and settings
+ */
+ init: function(element, options) {
+ this._element = element;
+ if (!(this._element instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element.");
+ }
+ else if (this._element.nodeName !== 'INPUT' || (this._element.type !== 'search' && this._element.type !== 'text')) {
+ throw new Error('Expected an input[type="text"].');
+ }
+
+ this._activeItem = null;
+ this._dropdownContainerId = '';
+ this._lastValue = '';
+ this._list = null;
+ this._request = null;
+ this._timerDelay = null;
+
+ this._options = Core.extend({
+ ajax: {
+ actionName: 'getSearchResultList',
+ className: '',
+ interfaceName: 'wcf\\data\\ISearchAction'
+ },
+ callbackDropdownInit: null,
+ callbackSelect: null,
+ delay: 500,
+ excludedSearchValues: [],
+ minLength: 3,
+ noResultPlaceholder: '',
+ preventSubmit: false
+ }, options);
+
+ // disable auto-complete as it collides with the suggestion dropdown
+ elAttr(this._element, 'autocomplete', 'off');
+
+ this._element.addEventListener('keydown', this._keydown.bind(this));
+ this._element.addEventListener('keyup', this._keyup.bind(this));
+ },
+
+ /**
+ * Adds an excluded search value.
+ *
+ * @param {string} value excluded value
+ */
+ addExcludedSearchValues: function (value) {
+ if (this._options.excludedSearchValues.indexOf(value) === -1) {
+ this._options.excludedSearchValues.push(value);
+ }
+ },
+
+ /**
+ * Removes a value from the excluded search values.
+ *
+ * @param {string} value excluded value
+ */
+ removeExcludedSearchValues: function (value) {
+ var index = this._options.excludedSearchValues.indexOf(value);
+ if (index !== -1) {
+ this._options.excludedSearchValues.splice(index, 1);
+ }
+ },
+
+ /**
+ * Handles the 'keydown' event.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _keydown: function(event) {
+ if ((this._activeItem !== null && UiSimpleDropdown.isOpen(this._dropdownContainerId)) || this._options.preventSubmit) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+ }
+ }
+
+ if (EventKey.ArrowUp(event) || EventKey.ArrowDown(event) || EventKey.Escape(event)) {
+ event.preventDefault();
+ }
+ },
+
+ /**
+ * Handles the 'keyup' event, provides keyboard navigation and executes search queries.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _keyup: function(event) {
+ // handle dropdown keyboard navigation
+ if (this._activeItem !== null) {
+ if (UiSimpleDropdown.isOpen(this._dropdownContainerId)) {
+ if (EventKey.ArrowUp(event)) {
+ event.preventDefault();
+
+ return this._keyboardPreviousItem();
+ }
+ else if (EventKey.ArrowDown(event)) {
+ event.preventDefault();
+
+ return this._keyboardNextItem();
+ }
+ else if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ return this._keyboardSelectItem();
+ }
+ }
+ else {
+ this._activeItem = null;
+ }
+ }
+
+ // close list on escape
+ if (EventKey.Escape(event)) {
+ UiSimpleDropdown.close(this._dropdownContainerId);
+
+ return;
+ }
+
+ var value = this._element.value.trim();
+ if (this._lastValue === value) {
+ // value did not change, e.g. previously it was "Test" and now it is "Test ",
+ // but the trailing whitespace has been ignored
+ return;
+ }
+
+ this._lastValue = value;
+
+ if (value.length < this._options.minLength) {
+ if (this._dropdownContainerId) {
+ UiSimpleDropdown.close(this._dropdownContainerId);
+ this._activeItem = null;
+ }
+
+ // value below threshold
+ return;
+ }
+
+ if (this._options.delay) {
+ if (this._timerDelay !== null) {
+ window.clearTimeout(this._timerDelay);
+ }
+
+ this._timerDelay = window.setTimeout((function() {
+ this._search(value);
+ }).bind(this), this._options.delay);
+ }
+ else {
+ this._search(value);
+ }
+ },
+
+ /**
+ * Queries the server with the provided search string.
+ *
+ * @param {string} value search string
+ * @protected
+ */
+ _search: function(value) {
+ if (this._request) {
+ this._request.abortPrevious();
+ }
+
+ this._request = Ajax.api(this, this._getParameters(value));
+ },
+
+ /**
+ * Returns additional AJAX parameters.
+ *
+ * @param {string} value search string
+ * @return {Object} additional AJAX parameters
+ * @protected
+ */
+ _getParameters: function(value) {
+ return {
+ parameters: {
+ data: {
+ excludedSearchValues: this._options.excludedSearchValues,
+ searchString: value
+ }
+ }
+ };
+ },
+
+ /**
+ * Selects the next dropdown item.
+ *
+ * @protected
+ */
+ _keyboardNextItem: function() {
+ this._activeItem.classList.remove('active');
+
+ if (this._activeItem.nextElementSibling) {
+ this._activeItem = this._activeItem.nextElementSibling;
+ }
+ else {
+ this._activeItem = this._list.children[0];
+ }
+
+ this._activeItem.classList.add('active');
+ },
+
+ /**
+ * Selects the previous dropdown item.
+ *
+ * @protected
+ */
+ _keyboardPreviousItem: function() {
+ this._activeItem.classList.remove('active');
+
+ if (this._activeItem.previousElementSibling) {
+ this._activeItem = this._activeItem.previousElementSibling;
+ }
+ else {
+ this._activeItem = this._list.children[this._list.childElementCount - 1];
+ }
+
+ this._activeItem.classList.add('active');
+ },
+
+ /**
+ * Selects the active item from the dropdown.
+ *
+ * @protected
+ */
+ _keyboardSelectItem: function() {
+ this._selectItem(this._activeItem);
+ },
+
+ /**
+ * Selects an item from the dropdown by clicking it.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _clickSelectItem: function(event) {
+ this._selectItem(event.currentTarget);
+ },
+
+ /**
+ * Selects an item.
+ *
+ * @param {Element} item selected item
+ * @protected
+ */
+ _selectItem: function(item) {
+ if (this._options.callbackSelect && this._options.callbackSelect(item) === false) {
+ this._element.value = '';
+ }
+ else {
+ this._element.value = elData(item, 'label');
+ }
+
+ this._activeItem = null;
+ UiSimpleDropdown.close(this._dropdownContainerId);
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _ajaxSuccess: function(data) {
+ var createdList = false;
+ if (this._list === null) {
+ this._list = elCreate('ul');
+ this._list.className = 'dropdownMenu';
+
+ createdList = true;
+
+ if (typeof this._options.callbackDropdownInit === 'function') {
+ this._options.callbackDropdownInit(this._list);
+ }
+ }
+ else {
+ // reset current list
+ this._list.innerHTML = '';
+ }
+
+ if (typeof data.returnValues === 'object') {
+ var callbackClick = this._clickSelectItem.bind(this), listItem;
+
+ for (var key in data.returnValues) {
+ if (data.returnValues.hasOwnProperty(key)) {
+ listItem = this._createListItem(data.returnValues[key]);
+
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ this._list.appendChild(listItem);
+ }
+ }
+ }
+
+ if (createdList) {
+ DomUtil.insertAfter(this._list, this._element);
+ UiSimpleDropdown.initFragment(this._element.parentNode, this._list);
+
+ this._dropdownContainerId = DomUtil.identify(this._element.parentNode);
+ }
+
+ if (this._dropdownContainerId) {
+ this._activeItem = null;
+
+ if (!this._list.childElementCount && this._handleEmptyResult() === false) {
+ UiSimpleDropdown.close(this._dropdownContainerId);
+ }
+ else {
+ UiSimpleDropdown.open(this._dropdownContainerId, true);
+
+ // mark first item as active
+ if (this._list.childElementCount && ~~elData(this._list.children[0], 'object-id')) {
+ this._activeItem = this._list.children[0];
+ this._activeItem.classList.add('active');
+ }
+ }
+ }
+ },
+
+ /**
+ * Handles an empty result set, return a boolean false to hide the dropdown.
+ *
+ * @return {boolean} false to close the dropdown
+ * @protected
+ */
+ _handleEmptyResult: function() {
+ if (!this._options.noResultPlaceholder) {
+ return false;
+ }
+
+ var listItem = elCreate('li');
+ listItem.className = 'dropdownText';
+
+ var span = elCreate('span');
+ span.textContent = this._options.noResultPlaceholder;
+ listItem.appendChild(span);
+
+ this._list.appendChild(listItem);
+
+ return true;
+ },
+
+ /**
+ * Creates an list item from response data.
+ *
+ * @param {Object} item response data
+ * @return {Element} list item
+ * @protected
+ */
+ _createListItem: function(item) {
+ var listItem = elCreate('li');
+ elData(listItem, 'object-id', item.objectID);
+ elData(listItem, 'label', item.label);
+
+ var span = elCreate('span');
+ span.textContent = item.label;
+ listItem.appendChild(span);
+
+ return listItem;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: this._options.ajax
+ };
+ }
+ };
+
+ return UiSearchInput;
+});
+
+/**
+ * Provides suggestions for users, optionally supporting groups.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Search/Input
+ * @see module:WoltLabSuite/Core/Ui/Search/Input
+ */
+define('WoltLabSuite/Core/Ui/User/Search/Input',['Core', 'WoltLabSuite/Core/Ui/Search/Input'], function(Core, UiSearchInput) {
+ "use strict";
+
+ /**
+ * @param {Element} element input element
+ * @param {Object=} options search options and settings
+ * @constructor
+ */
+ function UiUserSearchInput(element, options) { this.init(element, options); }
+ Core.inherit(UiUserSearchInput, UiSearchInput, {
+ init: function(element, options) {
+ var includeUserGroups = (Core.isPlainObject(options) && options.includeUserGroups === true);
+
+ options = Core.extend({
+ ajax: {
+ className: 'wcf\\data\\user\\UserAction',
+ parameters: {
+ data: {
+ includeUserGroups: (includeUserGroups ? 1 : 0)
+ }
+ }
+ }
+ }, options);
+
+ UiUserSearchInput._super.prototype.init.call(this, element, options);
+ },
+
+ _createListItem: function(item) {
+ var listItem = UiUserSearchInput._super.prototype._createListItem.call(this, item);
+ elData(listItem, 'type', item.type);
+
+ var box = elCreate('div');
+ box.className = 'box16';
+ box.innerHTML = (item.type === 'group') ? '<span class="icon icon16 fa-users"></span>' : item.icon;
+ box.appendChild(listItem.children[0]);
+ listItem.appendChild(box);
+
+ return listItem;
+ }
+ });
+
+ return UiUserSearchInput;
+});
+
+define('WoltLabSuite/Core/Ui/Acl/Simple',['Language', 'StringUtil', 'Dom/ChangeListener', 'WoltLabSuite/Core/Ui/User/Search/Input'], function(Language, StringUtil, DomChangeListener, UiUserSearchInput) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _build: function() {},
+ _select: function() {},
+ _removeItem: function() {}
+ };
+ return Fake;
+ }
+
+ function UiAclSimple(prefix, inputName) { this.init(prefix, inputName); }
+ UiAclSimple.prototype = {
+ init: function(prefix, inputName) {
+ this._prefix = prefix || '';
+ this._inputName = inputName || 'aclValues';
+
+ this._build();
+ },
+
+ _build: function () {
+ var container = elById(this._prefix + 'aclInputContainer');
+
+ elById(this._prefix + 'aclAllowAll').addEventListener('change', (function() {
+ elHide(container);
+ }));
+ elById(this._prefix + 'aclAllowAll_no').addEventListener('change', (function() {
+ elShow(container);
+ }));
+
+ this._list = elById(this._prefix + 'aclAccessList');
+ this._list.addEventListener(WCF_CLICK_EVENT, this._removeItem.bind(this));
+
+ var excludedSearchValues = [];
+ elBySelAll('.aclLabel', this._list, function(label) {
+ excludedSearchValues.push(label.textContent);
+ });
+
+ this._searchInput = new UiUserSearchInput(elById(this._prefix + 'aclSearchInput'), {
+ callbackSelect: this._select.bind(this),
+ includeUserGroups: true,
+ excludedSearchValues: excludedSearchValues,
+ preventSubmit: true,
+ });
+
+ this._aclListContainer = elById(this._prefix + 'aclListContainer');
+
+ DomChangeListener.trigger();
+ },
+
+ _select: function(listItem) {
+ var type = elData(listItem, 'type');
+ var label = elData(listItem, 'label');
+
+ var html = '<span class="icon icon16 fa-' + (type === 'group' ? 'users' : 'user') + '"></span>';
+ html += '<span class="aclLabel">' + StringUtil.escapeHTML(label) + '</span>';
+ html += '<span class="icon icon16 fa-times pointer jsTooltip" title="' + Language.get('wcf.global.button.delete') + '"></span>';
+ html += '<input type="hidden" name="' + this._inputName + '[' + type + '][]" value="' + elData(listItem, 'object-id') + '">';
+
+ var item = elCreate('li');
+ item.innerHTML = html;
+
+ var firstUser = elBySel('.fa-user', this._list);
+ if (firstUser === null) {
+ this._list.appendChild(item);
+ }
+ else {
+ this._list.insertBefore(item, firstUser.parentNode);
+ }
+
+ elShow(this._aclListContainer);
+
+ this._searchInput.addExcludedSearchValues(label);
+
+ DomChangeListener.trigger();
+
+ return false;
+ },
+
+ _removeItem: function (event) {
+ if (event.target.classList.contains('fa-times')) {
+ var label = elBySel('.aclLabel', event.target.parentNode);
+ this._searchInput.removeExcludedSearchValues(label.textContent);
+
+ elRemove(event.target.parentNode);
+
+ if (this._list.childElementCount === 0) {
+ elHide(this._aclListContainer);
+ }
+ }
+ }
+ };
+
+ return UiAclSimple;
+});
+
+/**
+ * Handles the 'mark as read' action for articles.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Article/MarkAllAsRead
+ */
+define('WoltLabSuite/Core/Ui/Article/MarkAllAsRead',['Ajax'], function(Ajax) {
+ "use strict";
+
+ return {
+ init: function() {
+ elBySelAll('.markAllAsReadButton', undefined, (function(button) {
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }).bind(this));
+ },
+
+ _click: function(event) {
+ event.preventDefault();
+
+ Ajax.api(this);
+ },
+
+ _ajaxSuccess: function() {
+ /* remove obsolete badges */
+ // main menu
+ var badge = elBySel('.mainMenu .active .badge');
+ if (badge) elRemove(badge);
+
+ // article list
+ elBySelAll('.articleList .newMessageBadge', undefined, elRemove);
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'markAllAsRead',
+ className: 'wcf\\data\\article\\ArticleAction'
+ }
+ };
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Article/Search',['Ajax', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function(Ajax, EventKey, Language, StringUtil, DomUtil, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ open: function() {},
+ _search: function() {},
+ _click: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callbackSelect, _resultContainer, _resultList, _searchInput = null;
+
+ return {
+ open: function(callbackSelect) {
+ _callbackSelect = callbackSelect;
+
+ UiDialog.open(this);
+ },
+
+ _search: function (event) {
+ event.preventDefault();
+
+ var inputContainer = _searchInput.parentNode;
+
+ var value = _searchInput.value.trim();
+ if (value.length < 3) {
+ elInnerError(inputContainer, Language.get('wcf.article.search.error.tooShort'));
+ return;
+ }
+ else {
+ elInnerError(inputContainer, false);
+ }
+
+ Ajax.api(this, {
+ parameters: {
+ searchString: value
+ }
+ });
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ _callbackSelect(elData(event.currentTarget, 'article-id'));
+
+ UiDialog.close(this);
+ },
+
+ _ajaxSuccess: function(data) {
+ var html = '', article;
+ //noinspection JSUnresolvedVariable
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ article = data.returnValues[i];
+
+ html += '<li>'
+ + '<div class="containerHeadline pointer" data-article-id="' + article.articleID + '">'
+ + '<h3>' + StringUtil.escapeHTML(article.name) + '</h3>'
+ + '<small>' + StringUtil.escapeHTML(article.displayLink) + '</small>'
+ + '</div>'
+ + '</li>';
+ }
+
+ _resultList.innerHTML = html;
+
+ window[html ? 'elShow' : 'elHide'](_resultContainer);
+
+ if (html) {
+ elBySelAll('.containerHeadline', _resultList, (function(item) {
+ item.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }).bind(this));
+ }
+ else {
+ elInnerError(_searchInput.parentNode, Language.get('wcf.article.search.error.noResults'));
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ actionName: 'search',
+ className: 'wcf\\data\\article\\ArticleAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiArticleSearch',
+ options: {
+ onSetup: (function() {
+ var callbackSearch = this._search.bind(this);
+
+ _searchInput = elById('wcfUiArticleSearchInput');
+ _searchInput.addEventListener('keydown', function(event) {
+ if (EventKey.Enter(event)) {
+ callbackSearch(event);
+ }
+ });
+
+ _searchInput.nextElementSibling.addEventListener(WCF_CLICK_EVENT, callbackSearch);
+
+ _resultContainer = elById('wcfUiArticleSearchResultContainer');
+ _resultList = elById('wcfUiArticleSearchResultList');
+ }).bind(this),
+ onShow: function() {
+ _searchInput.focus();
+ },
+ title: Language.get('wcf.article.search')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiArticleSearchInput">' + Language.get('wcf.article.search.name') + '</label></dt>'
+ + '<dd>'
+ + '<div class="inputAddon">'
+ + '<input type="text" id="wcfUiArticleSearchInput" class="long">'
+ + '<a href="#" class="inputSuffix"><span class="icon icon16 fa-search"></span></a>'
+ + '</div>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<section id="wcfUiArticleSearchResultContainer" class="section" style="display: none;">'
+ + '<header class="sectionHeader">'
+ + '<h2 class="sectionTitle">' + Language.get('wcf.article.search.results') + '</h2>'
+ + '</header>'
+ + '<ol id="wcfUiArticleSearchResultList" class="containerList"></ol>'
+ + '</section>'
+ };
+ }
+ };
+});
+
+/**
+ * Wrapper class to provide color picker support. Constructing a new object does not
+ * guarantee the picker to be ready at the time of call.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Color/Picker
+ */
+define('WoltLabSuite/Core/Ui/Color/Picker',['Core'], function (Core) {
+ "use strict";
+
+ var _marshal = function (element, options) {
+ if (typeof window.WCF === 'object' && typeof window.WCF.ColorPicker === 'function') {
+ _marshal = function (element, options) {
+ var picker = new window.WCF.ColorPicker(element);
+
+ if (typeof options.callbackSubmit === 'function') {
+ picker.setCallbackSubmit(options.callbackSubmit);
+ }
+
+ return picker;
+ };
+
+ return _marshal(element, options);
+ }
+ else {
+ if (_queue.length === 0) {
+ window.__wcf_bc_colorPickerInit = function () {
+ _queue.forEach(function (data) {
+ _marshal(data[0], data[1]);
+ });
+
+ window.__wcf_bc_colorPickerInit = undefined;
+ _queue = [];
+ };
+ }
+
+ _queue.push([element, options]);
+ }
+ };
+ var _queue = [];
+
+ /**
+ * @constructor
+ */
+ function UiColorPicker(element, options) { this.init(element, options); }
+ UiColorPicker.prototype = {
+ /**
+ * Initializes a new color picker instance. This is actually just a wrapper that does
+ * not guarantee the picker to be ready at the time of call.
+ *
+ * @param {Element} element input element
+ * @param {Object} options list of initialization options
+ */
+ init: function (element, options) {
+ if (!(element instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element, use `UiColorPicker.fromSelector()` if you want to use a CSS selector.");
+ }
+
+ this._options = Core.extend({
+ callbackSubmit: null
+ }, options);
+
+ _marshal(element, options);
+ }
+ };
+
+ /**
+ * Initializes a color picker for all input elements matching the given selector.
+ *
+ * @param {string} selector CSS selector
+ */
+ UiColorPicker.fromSelector = function (selector) {
+ elBySelAll(selector, undefined, function (element) {
+ new UiColorPicker(element);
+ });
+ };
+
+ return UiColorPicker;
+});
+
+/**
+ * Handles the comment add feature.
+ *
+ * Warning: This implementation is also used for responses, but in a slightly
+ * modified version. Changes made to this class need to be verified
+ * against the response implementation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Add
+ */
+define('WoltLabSuite/Core/Ui/Comment/Add',[
+ 'Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Scroll', 'EventKey', 'User', 'WoltLabSuite/Core/Controller/Captcha'
+],
+function(
+ Ajax, Core, EventHandler, Language, DomChangeListener, DomUtil, DomTraverse, UiDialog, UiNotification, UiScroll, EventKey, User, ControllerCaptcha
+) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _submitGuestDialog: function() {},
+ _submit: function() {},
+ _getParameters: function () {},
+ _validate: function() {},
+ throwError: function() {},
+ _showLoadingOverlay: function() {},
+ _hideLoadingOverlay: function() {},
+ _reset: function() {},
+ _handleError: function() {},
+ _getEditor: function() {},
+ _insertMessage: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSetup: function() {},
+ _cancelGuestDialog: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentAdd(container) { this.init(container); }
+ UiCommentAdd.prototype = {
+ /**
+ * Initializes a new quick reply field.
+ *
+ * @param {Element} container container element
+ */
+ init: function(container) {
+ this._container = container;
+ this._content = elBySel('.jsOuterEditorContainer', this._container);
+ this._textarea = elBySel('.wysiwygTextarea', this._container);
+ this._editor = null;
+ this._loadingOverlay = null;
+
+ this._content.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ if (this._content.classList.contains('collapsed')) {
+ event.preventDefault();
+
+ this._content.classList.remove('collapsed');
+
+ this._focusEditor();
+ }
+ }).bind(this));
+
+ // handle submit button
+ var submitButton = elBySel('button[data-type="save"]', this._container);
+ submitButton.addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ },
+
+ /**
+ * Scrolls the editor into view and sets the caret to the end of the editor.
+ *
+ * @protected
+ */
+ _focusEditor: function () {
+ UiScroll.element(this._container, (function () {
+ window.jQuery(this._textarea).redactor('WoltLabCaret.endOfEditor');
+ }).bind(this));
+ },
+
+ /**
+ * Submits the guest dialog.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _submitGuestDialog: function(event) {
+ // only submit when enter key is pressed
+ if (event.type === 'keypress' && !EventKey.Enter(event)) {
+ return;
+ }
+
+ var usernameInput = elBySel('input[name=username]', event.currentTarget.closest('.dialogContent'));
+ if (usernameInput.value === '') {
+ elInnerError(usernameInput, Language.get('wcf.global.form.error.empty'));
+ usernameInput.closest('dl').classList.add('formError');
+
+ return;
+ }
+
+ var parameters = {
+ parameters: {
+ data: {
+ username: usernameInput.value
+ }
+ }
+ };
+
+ if (ControllerCaptcha.has('commentAdd')) {
+ var data = ControllerCaptcha.getData('commentAdd');
+ if (data instanceof Promise) {
+ data.then((function (data) {
+ parameters = Core.extend(parameters, data);
+ this._submit(undefined, parameters);
+ }).bind(this));
+ }
+ else {
+ parameters = Core.extend(parameters, data);
+ this._submit(undefined, parameters);
+ }
+ }
+ else {
+ this._submit(undefined, parameters);
+ }
+ },
+
+ /**
+ * Validates the message and submits it to the server.
+ *
+ * @param {Event?} event event object
+ * @param {Object?} additionalParameters additional parameters sent to the server
+ * @protected
+ */
+ _submit: function(event, additionalParameters) {
+ if (event) {
+ event.preventDefault();
+ }
+
+ if (!this._validate()) {
+ // validation failed, bail out
+ return;
+ }
+
+ this._showLoadingOverlay();
+
+ // build parameters
+ var parameters = this._getParameters();
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_text', parameters.data);
+
+ if (!User.userId && !additionalParameters) {
+ parameters.requireGuestDialog = true;
+ }
+
+ Ajax.api(this, Core.extend({
+ parameters: parameters
+ }, additionalParameters));
+ },
+
+ /**
+ * Returns the request parameters to add a comment.
+ *
+ * @return {{data: {message: string, objectID: number, objectTypeID: number}}}
+ * @protected
+ */
+ _getParameters: function () {
+ var commentList = this._container.closest('.commentList');
+
+ return {
+ data: {
+ message: this._getEditor().code.get(),
+ objectID: ~~elData(commentList, 'object-id'),
+ objectTypeID: ~~elData(commentList, 'object-type-id')
+ }
+ };
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function() {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._container, elRemove);
+
+ // check if editor contains actual content
+ if (this._getEditor().utils.isEmpty()) {
+ this.throwError(this._textarea, Language.get('wcf.global.form.error.empty'));
+ return false;
+ }
+
+ var data = {
+ api: this,
+ editor: this._getEditor(),
+ message: this._getEditor().code.get(),
+ valid: true
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_text', data);
+
+ return (data.valid !== false);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, (message === 'empty' ? Language.get('wcf.global.form.error.empty') : message));
+ },
+
+ /**
+ * Displays a loading spinner while the request is processed by the server.
+ *
+ * @protected
+ */
+ _showLoadingOverlay: function() {
+ if (this._loadingOverlay === null) {
+ this._loadingOverlay = elCreate('div');
+ this._loadingOverlay.className = 'commentLoadingOverlay';
+ this._loadingOverlay.innerHTML = '<span class="icon icon96 fa-spinner"></span>';
+ }
+
+ this._content.classList.add('loading');
+ this._content.appendChild(this._loadingOverlay);
+ },
+
+ /**
+ * Hides the loading spinner.
+ *
+ * @protected
+ */
+ _hideLoadingOverlay: function() {
+ this._content.classList.remove('loading');
+
+ var loadingOverlay = elBySel('.commentLoadingOverlay', this._content);
+ if (loadingOverlay !== null) {
+ loadingOverlay.parentNode.removeChild(loadingOverlay);
+ }
+ },
+
+ /**
+ * Resets the editor contents and notifies event listeners.
+ *
+ * @protected
+ */
+ _reset: function() {
+ this._getEditor().code.set('<p>\u200b</p>');
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'reset_text');
+
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+
+ this._content.classList.add('collapsed');
+ },
+
+ /**
+ * Handles errors occurred during server processing.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _handleError: function(data) {
+ //noinspection JSUnresolvedVariable
+ this.throwError(this._textarea, data.returnValues.errorType);
+ },
+
+ /**
+ * Returns the current editor instance.
+ *
+ * @return {Object} editor instance
+ * @protected
+ */
+ _getEditor: function() {
+ if (this._editor === null) {
+ if (typeof window.jQuery === 'function') {
+ this._editor = window.jQuery(this._textarea).data('redactor');
+ }
+ else {
+ throw new Error("Unable to access editor, jQuery has not been loaded yet.");
+ }
+ }
+
+ return this._editor;
+ },
+
+ /**
+ * Inserts the rendered message.
+ *
+ * @param {Object} data response data
+ * @return {Element} scroll target
+ * @protected
+ */
+ _insertMessage: function(data) {
+ // insert HTML
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.insertHtml(data.returnValues.template, this._container, 'after');
+
+ UiNotification.show(Language.get('wcf.global.success.add'));
+
+ DomChangeListener.trigger();
+
+ return this._container.nextElementSibling;
+ },
+
+ /**
+ * @param {{returnValues:{guestDialog:string}}} data
+ * @protected
+ */
+ _ajaxSuccess: function(data) {
+ if (!User.userId && data.returnValues.guestDialog) {
+ UiDialog.openStatic('jsDialogGuestComment', data.returnValues.guestDialog, {
+ closable: false,
+ onClose: function() {
+ if (ControllerCaptcha.has('commentAdd')) {
+ ControllerCaptcha.delete('commentAdd');
+ }
+ },
+ title: Language.get('wcf.global.confirmation.title')
+ });
+
+ var dialog = UiDialog.getDialog('jsDialogGuestComment');
+ elBySel('input[type=submit]', dialog.content).addEventListener(WCF_CLICK_EVENT, this._submitGuestDialog.bind(this));
+ elBySel('button[data-type="cancel"]', dialog.content).addEventListener(WCF_CLICK_EVENT, this._cancelGuestDialog.bind(this));
+ elBySel('input[type=text]', dialog.content).addEventListener('keypress', this._submitGuestDialog.bind(this));
+ }
+ else {
+ var scrollTarget = this._insertMessage(data);
+
+ if (!User.userId) {
+ UiDialog.close('jsDialogGuestComment');
+ }
+
+ this._reset();
+
+ this._hideLoadingOverlay();
+
+ window.setTimeout((function () {
+ UiScroll.element(scrollTarget);
+ }).bind(this), 100);
+ }
+ },
+
+ _ajaxFailure: function(data) {
+ this._hideLoadingOverlay();
+
+ //noinspection JSUnresolvedVariable
+ if (data === null || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+ return true;
+ }
+
+ this._handleError(data);
+
+ return false;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'addComment',
+ className: 'wcf\\data\\comment\\CommentAction'
+ },
+ silent: true
+ };
+ },
+
+ /**
+ * Cancels the guest dialog and restores the comment editor.
+ */
+ _cancelGuestDialog: function() {
+ UiDialog.close('jsDialogGuestComment');
+
+ this._hideLoadingOverlay();
+ }
+ };
+
+ return UiCommentAdd;
+});
+
+/**
+ * Provides editing support for comments.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Edit
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Comment/Edit',[
+ 'Ajax', 'Core', 'Dictionary', 'Environment',
+ 'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll'
+ ],
+ function(
+ Ajax, Core, Dictionary, Environment,
+ EventHandler, Language, List, DomChangeListener, DomTraverse,
+ DomUtil, UiNotification, UiReusableDropdown, UiScroll
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ _click: function() {},
+ _prepare: function() {},
+ _showEditor: function() {},
+ _restoreMessage: function() {},
+ _save: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showMessage: function() {},
+ _hideEditor: function() {},
+ _restoreEditor: function() {},
+ _destroyEditor: function() {},
+ _getEditorId: function() {},
+ _getObjectId: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentEdit(container) { this.init(container); }
+ UiCommentEdit.prototype = {
+ /**
+ * Initializes the comment edit manager.
+ *
+ * @param {Element} container container element
+ */
+ init: function(container) {
+ this._activeElement = null;
+ this._callbackClick = null;
+ this._comments = new List();
+ this._container = container;
+ this._editorContainer = null;
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Comment/Edit_' + DomUtil.identify(this._container), this.rebuild.bind(this));
+ },
+
+ /**
+ * Initializes each applicable message, should be called whenever new
+ * messages are being displayed.
+ */
+ rebuild: function() {
+ elBySelAll('.comment', this._container, (function (comment) {
+ if (this._comments.has(comment)) {
+ return;
+ }
+
+ if (elDataBool(comment, 'can-edit')) {
+ var button = elBySel('.jsCommentEditButton', comment);
+ if (button !== null) {
+ if (this._callbackClick === null) {
+ this._callbackClick = this._click.bind(this);
+ }
+
+ button.addEventListener(WCF_CLICK_EVENT, this._callbackClick);
+ }
+ }
+
+ this._comments.add(comment);
+ }).bind(this));
+ },
+
+ /**
+ * Handles clicks on the edit button.
+ *
+ * @param {?Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ if (this._activeElement === null) {
+ this._activeElement = event.currentTarget.closest('.comment');
+
+ this._prepare();
+
+ Ajax.api(this, {
+ actionName: 'beginEdit',
+ objectIDs: [this._getObjectId(this._activeElement)]
+ });
+ }
+ else {
+ UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+ }
+ },
+
+ /**
+ * Prepares the message for editor display.
+ *
+ * @protected
+ */
+ _prepare: function() {
+ this._editorContainer = elCreate('div');
+ this._editorContainer.className = 'commentEditorContainer';
+ this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
+
+ var content = elBySel('.commentContentContainer', this._activeElement);
+ content.insertBefore(this._editorContainer, content.firstChild);
+ },
+
+ /**
+ * Shows the message editor.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showEditor: function(data) {
+ var id = this._getEditorId();
+
+ var icon = elBySel('.icon', this._editorContainer);
+ elRemove(icon);
+
+ var editor = elCreate('div');
+ editor.className = 'editorContainer';
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(editor, data.returnValues.template);
+ this._editorContainer.appendChild(editor);
+
+ // bind buttons
+ var formSubmit = elBySel('.formSubmit', editor);
+
+ var buttonSave = elBySel('button[data-type="save"]', formSubmit);
+ buttonSave.addEventListener(WCF_CLICK_EVENT, this._save.bind(this));
+
+ var buttonCancel = elBySel('button[data-type="cancel"]', formSubmit);
+ buttonCancel.addEventListener(WCF_CLICK_EVENT, this._restoreMessage.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.redactor', 'submitEditor_' + id, (function(data) {
+ data.cancel = true;
+
+ this._save();
+ }).bind(this));
+
+ var editorElement = elById(id);
+ if (Environment.editor() === 'redactor') {
+ window.setTimeout((function() {
+ UiScroll.element(this._activeElement);
+ }).bind(this), 250);
+ }
+ else {
+ editorElement.focus();
+ }
+ },
+
+ /**
+ * Restores the message view.
+ *
+ * @protected
+ */
+ _restoreMessage: function() {
+ this._destroyEditor();
+
+ elRemove(this._editorContainer);
+
+ this._activeElement = null;
+ },
+
+ /**
+ * Saves the editor message.
+ *
+ * @protected
+ */
+ _save: function() {
+ var parameters = {
+ data: {
+ message: ''
+ }
+ };
+
+ var id = this._getEditorId();
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'getText_' + id, parameters.data);
+
+ if (!this._validate(parameters)) {
+ // validation failed
+ return;
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_' + id, parameters);
+
+ Ajax.api(this, {
+ actionName: 'save',
+ objectIDs: [this._getObjectId(this._activeElement)],
+ parameters: parameters
+ });
+
+ this._hideEditor();
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @param {Object} parameters request parameters
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function(parameters) {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._activeElement, elRemove);
+
+ // check if editor contains actual content
+ var editorElement = elById(this._getEditorId());
+ if (window.jQuery(editorElement).data('redactor').utils.isEmpty()) {
+ this.throwError(editorElement, Language.get('wcf.global.form.error.empty'));
+ return false;
+ }
+
+ var data = {
+ api: this,
+ parameters: parameters,
+ valid: true
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_' + this._getEditorId(), data);
+
+ return (data.valid !== false);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, message);
+ },
+
+ /**
+ * Shows the update message.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showMessage: function(data) {
+ // set new content
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.setInnerHtml(elBySel('.commentContent .userMessage', this._editorContainer.parentNode), data.returnValues.message);
+
+ this._restoreMessage();
+
+ UiNotification.show();
+ },
+
+ /**
+ * Hides the editor from view.
+ *
+ * @protected
+ */
+ _hideEditor: function() {
+ elHide(elBySel('.editorContainer', this._editorContainer));
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ this._editorContainer.appendChild(icon);
+ },
+
+ /**
+ * Restores the previously hidden editor.
+ *
+ * @protected
+ */
+ _restoreEditor: function() {
+ var icon = elBySel('.fa-spinner', this._editorContainer);
+ elRemove(icon);
+
+ var editorContainer = elBySel('.editorContainer', this._editorContainer);
+ if (editorContainer !== null) elShow(editorContainer);
+ },
+
+ /**
+ * Destroys the editor instance.
+ *
+ * @protected
+ */
+ _destroyEditor: function() {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveDestroy_' + this._getEditorId());
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'destroy_' + this._getEditorId());
+ },
+
+ /**
+ * Returns the unique editor id.
+ *
+ * @return {string} editor id
+ * @protected
+ */
+ _getEditorId: function() {
+ return 'commentEditor' + this._getObjectId(this._activeElement);
+ },
+
+ /**
+ * Returns the element's `data-object-id` value.
+ *
+ * @param {Element} element target element
+ * @return {int}
+ * @protected
+ */
+ _getObjectId: function(element) {
+ return ~~elData(element, 'object-id');
+ },
+
+ _ajaxFailure: function(data) {
+ var editor = elBySel('.redactor-layer', this._editorContainer);
+
+ // handle errors occurring on editor load
+ if (editor === null) {
+ this._restoreMessage();
+
+ return true;
+ }
+
+ this._restoreEditor();
+
+ //noinspection JSUnresolvedVariable
+ if (!data || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+ return true;
+ }
+
+ //noinspection JSUnresolvedVariable
+ elInnerError(editor, data.returnValues.errorType);
+
+ return false;
+ },
+
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'beginEdit':
+ this._showEditor(data);
+ break;
+
+ case 'save':
+ this._showMessage(data);
+ break;
+ }
+ },
+
+ _ajaxSetup: function() {
+ var objectTypeId = ~~elData(this._container, 'object-type-id');
+
+ return {
+ data: {
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: {
+ objectTypeID: objectTypeId
+ }
+ }
+ },
+ silent: true
+ };
+ }
+ };
+
+ return UiCommentEdit;
+});
+
+/**
+ * Simplified and consistent dropdown creation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dropdown/Builder
+ */
+define('WoltLabSuite/Core/Ui/Dropdown/Builder',['Core', 'Ui/SimpleDropdown'], function (Core, UiSimpleDropdown) {
+ "use strict";
+
+ var _validIconSizes = [16, 24, 32, 48, 64, 96, 144];
+
+ function _validateList(list) {
+ if (!(list instanceof HTMLUListElement)) {
+ throw new TypeError('Expected a reference to an <ul> element.');
+ }
+
+ if (!list.classList.contains('dropdownMenu')) {
+ throw new Error('List does not appear to be a dropdown menu.');
+ }
+ }
+
+ function _buildItem(data) {
+ var item = elCreate('li');
+
+ // handle special `divider` type
+ if (data === 'divider') {
+ item.className = 'dropdownDivider';
+ return item;
+ }
+
+ if (typeof data.identifier === 'string') {
+ elData(item, 'identifier', data.identifier);
+ }
+
+ var link = elCreate('a');
+ link.href = (typeof data.href === 'string') ? data.href : '#';
+ if (typeof data.callback === 'function') {
+ link.addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+
+ data.callback(link);
+ });
+ }
+ else if (link.getAttribute('href') === '#') {
+ throw new Error('Expected either a `href` value or a `callback`.');
+ }
+
+ if (data.hasOwnProperty('attributes') && Core.isPlainObject(data.attributes)) {
+ for (var key in data.attributes) {
+ if (data.attributes.hasOwnProperty(key)) {
+ elData(link, key, data.attributes[key]);
+ }
+ }
+ }
+
+ item.appendChild(link);
+
+ if (typeof data.icon !== 'undefined' && Core.isPlainObject(data.icon)) {
+ if (typeof data.icon.name !== 'string') {
+ throw new TypeError('Expected a valid icon name.');
+ }
+
+ var size = 16;
+ if (typeof data.icon.size === 'number' && _validIconSizes.indexOf(~~data.icon.size) !== -1) {
+ size = ~~data.icon.size;
+ }
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon' + size + ' fa-' + data.icon.name;
+
+ link.appendChild(icon);
+ }
+
+ var label = (typeof data.label === 'string') ? data.label.trim() : '';
+ var labelHtml = (typeof data.labelHtml === 'string') ? data.labelHtml.trim() : '';
+ if (label === '' && labelHtml === '') {
+ throw new TypeError('Expected either a label or a `labelHtml`.');
+ }
+
+ var span = elCreate('span');
+ span[label ? 'textContent' : 'innerHTML'] = (label) ? label : labelHtml;
+ link.appendChild(document.createTextNode(' '));
+ link.appendChild(span);
+
+ return item;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dropdown/Builder
+ */
+ return {
+ /**
+ * Creates a new dropdown menu, optionally pre-populated with the supplied list of
+ * dropdown items. The list element will be returned and must be manually injected
+ * into the DOM by the callee.
+ *
+ * @param {(Object|string)[]} items
+ * @param {string?} identifier
+ * @return {Element}
+ */
+ create: function (items, identifier) {
+ var list = elCreate('ul');
+ list.className = 'dropdownMenu';
+ if (typeof identifier === 'string') {
+ elData(list, 'identifier', identifier);
+ }
+
+ if (Array.isArray(items) && items.length > 0) {
+ this.appendItems(list, items);
+ }
+
+ return list;
+ },
+
+ /**
+ * Creates a new dropdown item that can be inserted into lists using regular DOM operations.
+ *
+ * @param {(Object|string)} item
+ * @return {Element}
+ */
+ buildItem: function (item) {
+ return _buildItem(item);
+ },
+
+ /**
+ * Appends a single item to the target list.
+ *
+ * @param {Element} list
+ * @param {(Object|string)} item
+ */
+ appendItem: function (list, item) {
+ _validateList(list);
+
+ list.appendChild(_buildItem(item));
+ },
+
+ /**
+ * Appends a list of items to the target list.
+ *
+ * @param {Element} list
+ * @param {(Object|string)[]} items
+ */
+ appendItems: function (list, items) {
+ _validateList(list);
+
+ if (!Array.isArray(items)) {
+ throw new TypeError('Expected an array of items.');
+ }
+
+ var length = items.length;
+ if (length === 0) {
+ throw new Error('Expected a non-empty list of items.');
+ }
+
+ if (length === 1) {
+ this.appendItem(list, items[0]);
+ }
+ else {
+ var fragment = document.createDocumentFragment();
+ for (var i = 0; i < length; i++) {
+ fragment.appendChild(_buildItem(items[i]));
+ }
+ list.appendChild(fragment);
+ }
+ },
+
+ /**
+ * Replaces the existing list items with the provided list of new items.
+ *
+ * @param {Element} list
+ * @param {(Object|string)[]} items
+ */
+ setItems: function (list, items) {
+ _validateList(list);
+
+ list.innerHTML = '';
+
+ this.appendItems(list, items);
+ },
+
+ /**
+ * Attaches the list to a button, visibility is from then on controlled through clicks
+ * on the provided button element. Internally calls `Ui/SimpleDropdown.initFragment()`
+ * to delegate the DOM management.
+ *
+ * @param {Element} list
+ * @param {Element} button
+ */
+ attach: function (list, button) {
+ _validateList(list);
+
+ UiSimpleDropdown.initFragment(button, list);
+
+ button.addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ UiSimpleDropdown.toggleDropdown(button.id);
+ });
+ },
+
+ /**
+ * Helper method that returns the special string `"divider"` that causes a divider to
+ * be created.
+ *
+ * @return {string}
+ */
+ divider: function () {
+ return 'divider';
+ }
+ };
+});
+
+/**
+ * Delete files which are uploaded via AJAX.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/File/Delete
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/File/Delete',['Ajax', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util', 'Dom/Traverse', 'Dictionary'], function(Ajax, Core, DomChangeListener, Language, DomUtil, DomTraverse, Dictionary) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Delete(buttonContainerId, targetId, isSingleImagePreview, uploadHandler) {
+ this._isSingleImagePreview = isSingleImagePreview;
+ this._uploadHandler = uploadHandler;
+
+ this._buttonContainer = elById(buttonContainerId);
+ if (this._buttonContainer === null) {
+ throw new Error("Element id '" + buttonContainerId + "' is unknown.");
+ }
+
+ this._target = elById(targetId);
+ if (targetId === null) {
+ throw new Error("Element id '" + targetId + "' is unknown.");
+ }
+ this._containers = new Dictionary();
+
+ this._internalId = elData(this._target, 'internal-id');
+
+ if (!this._internalId) {
+ throw new Error("InternalId is unknown.");
+ }
+
+ this.rebuild();
+ }
+
+ Delete.prototype = {
+ /**
+ * Creates the upload button.
+ */
+ _createButtons: function() {
+ var element, elements = elBySelAll('li.uploadedFile', this._target), elementData, triggerChange = false, uniqueFileId;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ uniqueFileId = elData(element, 'unique-file-id');
+ if (this._containers.has(uniqueFileId)) {
+ continue;
+ }
+
+ elementData = {
+ uniqueFileId: uniqueFileId,
+ element: element
+ };
+
+ this._containers.set(uniqueFileId, elementData);
+ this._initDeleteButton(element, elementData);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Init the delete button for a specific element.
+ *
+ * @param {HTMLElement} element
+ * @param {string} elementData
+ */
+ _initDeleteButton: function(element, elementData) {
+ var buttonGroup = elBySel('.buttonGroup', element);
+
+ if (buttonGroup === null) {
+ throw new Error("Button group in '" + targetId + "' is unknown.");
+ }
+
+ var li = elCreate('li');
+ var span = elCreate('span');
+ span.classList = "button jsDeleteButton small";
+ span.textContent = Language.get('wcf.global.button.delete');
+ li.appendChild(span);
+ buttonGroup.appendChild(li);
+
+ li.addEventListener(WCF_CLICK_EVENT, this._delete.bind(this, elementData.uniqueFileId));
+ },
+
+ /**
+ * Delete a specific file with the given uniqueFileId.
+ *
+ * @param {string} uniqueFileId
+ */
+ _delete: function(uniqueFileId) {
+ Ajax.api(this, {
+ uniqueFileId: uniqueFileId,
+ internalId: this._internalId
+ });
+ },
+
+ /**
+ * Rebuilds the delete buttons for unknown files.
+ */
+ rebuild: function() {
+ if (this._isSingleImagePreview) {
+ var img = elBySel('img', this._target);
+
+ if (img !== null) {
+ var uniqueFileId = elData(img, 'unique-file-id');
+
+ if (!this._containers.has(uniqueFileId)) {
+ var elementData = {
+ uniqueFileId: uniqueFileId,
+ element: img
+ };
+
+ this._containers.set(uniqueFileId, elementData);
+
+ this._deleteButton = elCreate('p');
+ this._deleteButton.className = 'button deleteButton';
+
+ var span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.delete');
+ this._deleteButton.appendChild(span);
+
+ this._buttonContainer.appendChild(this._deleteButton);
+
+ this._deleteButton.addEventListener(WCF_CLICK_EVENT, this._delete.bind(this, elementData.uniqueFileId));
+ }
+ }
+ }
+ else {
+ this._createButtons();
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ elRemove(this._containers.get(data.uniqueFileId).element);
+
+ if (this._isSingleImagePreview) {
+ elRemove(this._deleteButton);
+ this._deleteButton = null;
+ }
+
+ this._uploadHandler.checkMaxFiles();
+ },
+
+ _ajaxSetup: function () {
+ return {
+ url: 'index.php?ajax-file-delete/&t=' + SECURITY_TOKEN
+ };
+ }
+ };
+
+ return Delete;
+});
+
+/**
+ * Uploads file via AJAX.
+ *
+ * @author Joshua Ruesweg, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/File/Upload
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/File/Upload',['Core', 'Language', 'Dom/Util', 'WoltLabSuite/Core/Ui/File/Delete', 'Upload'], function(Core, Language, DomUtil, DeleteHandler, CoreUpload) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Upload(buttonContainerId, targetId, options) {
+ options = options || {};
+
+ if (options.internalId === undefined) {
+ throw new Error("Missing internal id.");
+ }
+
+ // set default options
+ this._options = Core.extend({
+ // name if the upload field
+ name: '__files[]',
+ // is true if every file from a multi-file selection is uploaded in its own request
+ singleFileRequests: false,
+ // url for uploading file
+ url: 'index.php?ajax-file-upload/&t=' + SECURITY_TOKEN,
+ // image preview
+ imagePreview: false,
+ // max files
+ maxFiles: null
+ }, options);
+
+ this._options.multiple = this._options.maxFiles === null || this._options.maxFiles > 1;
+
+ this._options.url = this._options.url;
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
+ }
+
+ this._buttonContainer = elById(buttonContainerId);
+ if (this._buttonContainer === null) {
+ throw new Error("Element id '" + buttonContainerId + "' is unknown.");
+ }
+
+ this._target = elById(targetId);
+ if (targetId === null) {
+ throw new Error("Element id '" + targetId + "' is unknown.");
+ }
+
+ if (options.multiple && this._target.nodeName !== 'UL' && this._target.nodeName !== 'OL') {
+ throw new Error("Target element has to be list or table body if uploading multiple files is supported.");
+ }
+
+ this._fileElements = [];
+ this._internalFileId = 0;
+
+ // upload ids that belong to an upload of multiple files at once
+ this._multiFileUploadIds = [];
+
+ this._createButton();
+ this.checkMaxFiles();
+
+ this._deleteHandler = new DeleteHandler(buttonContainerId, targetId, this._options.imagePreview, this);
+ }
+
+ Core.inherit(Upload, CoreUpload, {
+ _createFileElement: function(file) {
+ var element = Upload._super.prototype._createFileElement.call(this, file);
+ element.classList.add('box64', 'uploadedFile');
+
+ var progress = elBySel('progress', element);
+
+ var icon = elCreate('span');
+ icon.classList = 'icon icon64 fa-spinner';
+
+ var fileName = element.textContent;
+ element.textContent = "";
+ element.append(icon);
+
+ var innerDiv = elCreate('div');
+ var fileNameP = elCreate('p');
+ fileNameP.textContent = fileName; // file.name
+
+ var smallProgress = elCreate('small');
+ smallProgress.appendChild(progress);
+
+ innerDiv.appendChild(fileNameP);
+ innerDiv.appendChild(smallProgress);
+
+ var div = elCreate('div');
+ div.appendChild(innerDiv);
+
+ var ul = elCreate('ul');
+ ul.classList = 'buttonGroup';
+ div.appendChild(ul);
+
+ // reset element textContent and replace with own element style
+ element.append(div);
+
+ return element;
+ },
+
+ _failure: function(uploadId, data, responseText, xhr, requestOptions) {
+ for (var i = 0, length = this._fileElements[uploadId].length; i < length; i++) {
+ this._fileElements[uploadId][i].classList.add('uploadFailed');
+
+ elBySel('small', this._fileElements[uploadId][i]).innerHTML = '';
+ var icon = elBySel('.icon', this._fileElements[uploadId][i]);
+ icon.classList.remove('fa-spinner');
+ icon.classList.add('fa-ban');
+
+ var innerError = elCreate('span');
+ innerError.classList = 'innerError';
+ innerError.textContent = Language.get('wcf.upload.error.uploadFailed');
+ DomUtil.insertAfter(innerError, elBySel('small', this._fileElements[uploadId][i]));
+ }
+
+ throw new Error("Upload failed: " + data.message);
+ },
+
+ _upload: function(event, file, blob) {
+ var innerError = elBySel('small.innerError:not(.innerFileError)', this._buttonContainer.parentNode);
+ if (innerError) elRemove(innerError);
+
+ return Upload._super.prototype._upload.call(this, event, file, blob);
+ },
+
+ _success: function(uploadId, data, responseText, xhr, requestOptions) {
+ for (var i = 0, length = this._fileElements[uploadId].length; i < length; i++) {
+ if (data['files'][i] !== undefined) {
+ if (this._options.imagePreview) {
+ if (data['files'][i].image === null) {
+ throw new Error("Expect image for uploaded file. None given.");
+ }
+
+ elRemove(this._fileElements[uploadId][i]);
+
+ if (elBySel('img.previewImage', this._target) !== null) {
+ elBySel('img.previewImage', this._target).setAttribute('src', data['files'][i].image);
+ }
+ else {
+ var image = elCreate('img');
+ image.classList.add('previewImage');
+ image.setAttribute('src', data['files'][i].image);
+ image.setAttribute('style', "max-width: 100%;");
+ elData(image, 'unique-file-id', data['files'][i].uniqueFileId);
+ this._target.appendChild(image);
+ }
+ }
+ else {
+ elData(this._fileElements[uploadId][i], 'unique-file-id', data['files'][i].uniqueFileId);
+ elBySel('small', this._fileElements[uploadId][i]).textContent = data['files'][i].filesize;
+ var icon = elBySel('.icon', this._fileElements[uploadId][i]);
+ icon.classList.remove('fa-spinner');
+ icon.classList.add('fa-' + data['files'][i].icon);
+ }
+ }
+ else if (data['error'][i] !== undefined) {
+ this._fileElements[uploadId][i].classList.add('uploadFailed');
+
+ elBySel('small', this._fileElements[uploadId][i]).innerHTML = '';
+ var icon = elBySel('.icon', this._fileElements[uploadId][i]);
+ icon.classList.remove('fa-spinner');
+ icon.classList.add('fa-ban');
+
+ if (elBySel('.innerError', this._fileElements[uploadId][i]) === null) {
+ var innerError = elCreate('span');
+ innerError.classList = 'innerError';
+ innerError.textContent = data['error'][i].errorMessage;
+ DomUtil.insertAfter(innerError, elBySel('small', this._fileElements[uploadId][i]));
+ }
+ else {
+ elBySel('.innerError', this._fileElements[uploadId][i]).textContent = data['error'][i].errorMessage;
+ }
+ }
+ else {
+ throw new Error('Unknown uploaded file for uploadId ' + uploadId + '.');
+ }
+ }
+
+ // create delete buttons
+ this._deleteHandler.rebuild();
+ this.checkMaxFiles();
+ },
+
+ _getFormData: function() {
+ return {
+ internalId: this._options.internalId
+ };
+ },
+
+ validateUpload: function(files) {
+ if (this._options.maxFiles === null || files.length + this.countFiles() <= this._options.maxFiles) {
+ return true;
+ }
+ else {
+ var innerError = elBySel('small.innerError:not(.innerFileError)', this._buttonContainer.parentNode);
+
+ if (innerError === null) {
+ innerError = elCreate('small');
+ innerError.classList = 'innerError';
+ DomUtil.insertAfter(innerError, this._buttonContainer);
+ }
+
+ innerError.textContent = Language.get('wcf.upload.error.reachedRemainingLimit', {
+ maxFiles: this._options.maxFiles - this.countFiles()
+ });
+
+ return false;
+ }
+ },
+
+ /**
+ * Returns the count of the uploaded images.
+ *
+ * @return {int}
+ */
+ countFiles: function() {
+ if (this._options.imagePreview) {
+ return elBySel('img', this._target) !== null ? 1 : 0;
+ }
+ else {
+ return this._target.childElementCount;
+ }
+ },
+
+ /**
+ * Checks the maximum number of files and enables or disables the upload button.
+ */
+ checkMaxFiles: function() {
+ if (this._options.maxFiles !== null && this.countFiles() >= this._options.maxFiles) {
+ elHide(this._button);
+ }
+ else {
+ elShow(this._button);
+ }
+ }
+ });
+
+ return Upload;
+});
+
+/**
+ * Provides a filter input for checkbox lists.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList/Filter
+ */
+define('WoltLabSuite/Core/Ui/ItemList/Filter',['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util', 'Ui/SimpleDropdown'], function (Core, EventKey, Language, List, StringUtil, DomUtil, UiSimpleDropdown) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _buildItems: function() {},
+ _prepareItem: function() {},
+ _keyup: function() {},
+ _toggleVisibility: function () {},
+ _setupVisibilityFilter: function () {},
+ _setVisibility: function () {}
+ };
+ return Fake;
+ }
+
+ /**
+ * Creates a new filter input.
+ *
+ * @param {string} elementId list element id
+ * @param {Object=} options options
+ * @constructor
+ */
+ function UiItemListFilter(elementId, options) { this.init(elementId, options); }
+ UiItemListFilter.prototype = {
+ /**
+ * Creates a new filter input.
+ *
+ * @param {string} elementId list element id
+ * @param {Object=} options options
+ */
+ init: function(elementId, options) {
+ this._value = '';
+
+ this._options = Core.extend({
+ callbackPrepareItem: undefined,
+ enableVisibilityFilter: true
+ }, options);
+
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, '" + elementId + "' does not match anything.");
+ }
+ else if (!element.classList.contains('scrollableCheckboxList') && typeof this._options.callbackPrepareItem !== 'function') {
+ throw new Error("Filter only works with elements with the CSS class 'scrollableCheckboxList'.");
+ }
+
+ elData(element, 'filter', 'showAll');
+
+ var container = elCreate('div');
+ container.className = 'itemListFilter';
+
+ element.parentNode.insertBefore(container, element);
+ container.appendChild(element);
+
+ var inputAddon = elCreate('div');
+ inputAddon.className = 'inputAddon';
+
+ var input = elCreate('input');
+ input.className = 'long';
+ input.type = 'text';
+ input.placeholder = Language.get('wcf.global.filter.placeholder');
+ input.addEventListener('keydown', function (event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+ }
+ });
+ input.addEventListener('keyup', this._keyup.bind(this));
+
+ var clearButton = elCreate('a');
+ clearButton.href = '#';
+ clearButton.className = 'button inputSuffix jsTooltip';
+ clearButton.title = Language.get('wcf.global.filter.button.clear');
+ clearButton.innerHTML = '<span class="icon icon16 fa-times"></span>';
+ clearButton.addEventListener('click', (function(event) {
+ event.preventDefault();
+
+ this.reset();
+ }).bind(this));
+
+ inputAddon.appendChild(input);
+ inputAddon.appendChild(clearButton);
+
+ if (this._options.enableVisibilityFilter) {
+ var visibilityButton = elCreate('a');
+ visibilityButton.href = '#';
+ visibilityButton.className = 'button inputSuffix jsTooltip';
+ visibilityButton.title = Language.get('wcf.global.filter.button.visibility');
+ visibilityButton.innerHTML = '<span class="icon icon16 fa-eye"></span>';
+ visibilityButton.addEventListener(WCF_CLICK_EVENT, this._toggleVisibility.bind(this));
+ inputAddon.appendChild(visibilityButton);
+ }
+
+ container.appendChild(inputAddon);
+
+ this._container = container;
+ this._dropdown = null;
+ this._dropdownId = '';
+ this._element = element;
+ this._input = input;
+ this._items = null;
+ this._fragment = null;
+ },
+
+ /**
+ * Resets the filter.
+ */
+ reset: function () {
+ this._input.value = '';
+ this._keyup();
+ },
+
+ /**
+ * Builds the item list and rebuilds the items' DOM for easier manipulation.
+ *
+ * @protected
+ */
+ _buildItems: function() {
+ this._items = new List();
+
+ var callback = (typeof this._options.callbackPrepareItem === 'function') ? this._options.callbackPrepareItem : this._prepareItem.bind(this);
+ for (var i = 0, length = this._element.childElementCount; i < length; i++) {
+ this._items.add(callback(this._element.children[i]));
+ }
+ },
+
+ /**
+ * Processes an item and returns the meta data.
+ *
+ * @param {Element} item current item
+ * @return {{item: *, span: Element, text: string}}
+ * @protected
+ */
+ _prepareItem: function(item) {
+ var label = item.children[0];
+ var text = label.textContent.trim();
+
+ var checkbox = label.children[0];
+ while (checkbox.nextSibling) {
+ label.removeChild(checkbox.nextSibling);
+ }
+
+ label.appendChild(document.createTextNode(' '));
+
+ var span = elCreate('span');
+ span.textContent = text;
+ label.appendChild(span);
+
+ return {
+ item: item,
+ span: span,
+ text: text
+ };
+ },
+
+ /**
+ * Rebuilds the list on keyup, uses case-insensitive matching.
+ *
+ * @protected
+ */
+ _keyup: function() {
+ var value = this._input.value.trim();
+ if (this._value === value) {
+ return;
+ }
+
+ if (this._fragment === null) {
+ this._fragment = document.createDocumentFragment();
+
+ // set fixed height to avoid layout jumps
+ this._element.style.setProperty('height', this._element.offsetHeight + 'px', '');
+ }
+
+ // move list into fragment before editing items, increases performance
+ // by avoiding the browser to perform repaint/layout over and over again
+ this._fragment.appendChild(this._element);
+
+ if (this._items === null) {
+ this._buildItems();
+ }
+
+ var regexp = new RegExp('(' + StringUtil.escapeRegExp(value) + ')', 'i');
+ var hasVisibleItems = (value === '');
+ this._items.forEach(function (item) {
+ if (value === '') {
+ item.span.textContent = item.text;
+
+ elShow(item.item);
+ }
+ else {
+ if (regexp.test(item.text)) {
+ item.span.innerHTML = item.text.replace(regexp, '<u>$1</u>');
+
+ elShow(item.item);
+ hasVisibleItems = true;
+ }
+ else {
+ elHide(item.item);
+ }
+ }
+ });
+
+ this._container.insertBefore(this._fragment.firstChild, this._container.firstChild);
+ this._value = value;
+
+ elInnerError(this._container, (hasVisibleItems) ? false : Language.get('wcf.global.filter.error.noMatches'));
+ },
+
+ /**
+ * Toggles the visibility mode for marked items.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _toggleVisibility: function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var button = event.currentTarget;
+ if (this._dropdown === null) {
+ var dropdown = elCreate('ul');
+ dropdown.className = 'dropdownMenu';
+
+ ['activeOnly', 'highlightActive', 'showAll'].forEach((function (type) {
+ var link = elCreate('a');
+ elData(link, 'type', type);
+ link.href = '#';
+ link.textContent = Language.get('wcf.global.filter.visibility.' + type);
+ link.addEventListener(WCF_CLICK_EVENT, this._setVisibility.bind(this));
+
+ var li = elCreate('li');
+ li.appendChild(link);
+
+ if (type === 'showAll') {
+ li.className = 'active';
+
+ var divider = elCreate('li');
+ divider.className = 'dropdownDivider';
+ dropdown.appendChild(divider);
+ }
+
+ dropdown.appendChild(li);
+ }).bind(this));
+
+ UiSimpleDropdown.initFragment(button, dropdown);
+
+ // add `active` classes required for the visibility filter
+ this._setupVisibilityFilter();
+
+ this._dropdown = dropdown;
+ this._dropdownId = button.id;
+ }
+
+ UiSimpleDropdown.toggleDropdown(button.id, button);
+ },
+
+ /**
+ * Set-ups the visibility filter by assigning an active class to the
+ * list items that hold the checkboxes and observing the checkboxes
+ * for any changes.
+ *
+ * This process involves quite a few DOM changes and new event listeners,
+ * therefore we'll delay this until the filter has been accessed for
+ * the first time, because none of these changes matter before that.
+ *
+ * @protected
+ */
+ _setupVisibilityFilter: function () {
+ var nextSibling = this._element.nextSibling;
+ var parent = this._element.parentNode;
+ var scrollTop = this._element.scrollTop;
+
+ // mass-editing of DOM elements is slow while they're part of the document
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild(this._element);
+
+ elBySelAll('li', this._element, function(li) {
+ var checkbox = elBySel('input[type="checkbox"]', li);
+ if (checkbox) {
+ if (checkbox.checked) li.classList.add('active');
+
+ checkbox.addEventListener('change', function() {
+ li.classList[(checkbox.checked ? 'add' : 'remove')]('active');
+ });
+ }
+ else {
+ var radioButton = elBySel('input[type="radio"]', li);
+ if (radioButton) {
+ if (radioButton.checked) li.classList.add('active');
+
+ radioButton.addEventListener('change', function() {
+ elBySelAll('li', this._element, function(everyLi) {
+ everyLi.classList.remove('active');
+ });
+
+ li.classList[(radioButton.checked ? 'add' : 'remove')]('active');
+ }.bind(this));
+ }
+ }
+ }.bind(this));
+
+ // re-insert the modified DOM
+ parent.insertBefore(this._element, nextSibling);
+ this._element.scrollTop = scrollTop;
+ },
+
+ /**
+ * Sets the visibility of marked items.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _setVisibility: function (event) {
+ event.preventDefault();
+
+ var link = event.currentTarget;
+ var type = elData(link, 'type');
+
+ UiSimpleDropdown.close(this._dropdownId);
+
+ if (elData(this._element, 'filter') === type) {
+ // filter did not change
+ return;
+ }
+
+ elData(this._element, 'filter', type);
+
+ elBySel('.active', this._dropdown).classList.remove('active');
+ link.parentNode.classList.add('active');
+
+ var button = elById(this._dropdownId);
+ button.classList[(type === 'showAll' ? 'remove' : 'add')]('active');
+
+ var icon = elBySel('.icon', button);
+ icon.classList[(type === 'showAll' ? 'add' : 'remove')]('fa-eye');
+ icon.classList[(type === 'showAll' ? 'remove' : 'add')]('fa-eye-slash');
+ }
+ };
+
+ return UiItemListFilter;
+});
+
+/**
+ * Flexible UI element featuring both a list of items and an input field.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList/Static
+ */
+define('WoltLabSuite/Core/Ui/ItemList/Static',['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'Ui/SimpleDropdown'], function(Core, Dictionary, Language, DomTraverse, EventKey, UiSimpleDropdown) {
+ "use strict";
+
+ var _activeId = '';
+ var _data = new Dictionary();
+ var _didInit = false;
+
+ var _callbackKeyDown = null;
+ var _callbackKeyPress = null;
+ var _callbackKeyUp = null;
+ var _callbackPaste = null;
+ var _callbackRemoveItem = null;
+ var _callbackBlur = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/ItemList/Static
+ */
+ return {
+ /**
+ * Initializes an item list.
+ *
+ * The `values` argument must be empty or contain a list of strings or object, e.g.
+ * `['foo', 'bar']` or `[{ objectId: 1337, value: 'baz'}, {...}]`
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of existing values
+ * @param {Object} options option list
+ */
+ init: function(elementId, values, options) {
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, '" + elementId + "' is invalid.");
+ }
+
+ // remove data from previous instance
+ if (_data.has(elementId)) {
+ var tmp = _data.get(elementId);
+
+ for (var key in tmp) {
+ if (tmp.hasOwnProperty(key)) {
+ var el = tmp[key];
+ if (el instanceof Element && el.parentNode) {
+ elRemove(el);
+ }
+ }
+ }
+
+ UiSimpleDropdown.destroy(elementId);
+ _data.delete(elementId);
+ }
+
+ options = Core.extend({
+ // maximum number of items this list may contain, `-1` for infinite
+ maxItems: -1,
+ // maximum length of an item value, `-1` for infinite
+ maxLength: -1,
+
+ // initial value will be interpreted as comma separated value and submitted as such
+ isCSV: false,
+
+ // will be invoked whenever the items change, receives the element id first and list of values second
+ callbackChange: null,
+ // callback once the form is about to be submitted
+ callbackSubmit: null,
+ // value may contain the placeholder `{$objectId}`
+ submitFieldName: ''
+ }, options);
+
+ var form = DomTraverse.parentByTag(element, 'FORM');
+ if (form !== null) {
+ if (options.isCSV === false) {
+ if (!options.submitFieldName.length && typeof options.callbackSubmit !== 'function') {
+ throw new Error("Expected a valid function for option 'callbackSubmit', a non-empty value for option 'submitFieldName' or enabling the option 'submitFieldCSV'.");
+ }
+
+ form.addEventListener('submit', (function() {
+ var values = this.getValues(elementId);
+ if (options.submitFieldName.length) {
+ var input;
+ for (var i = 0, length = values.length; i < length; i++) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = options.submitFieldName.replace(/{$objectId}/, values[i].objectId);
+ input.value = values[i].value;
+
+ form.appendChild(input);
+ }
+ }
+ else {
+ options.callbackSubmit(form, values);
+ }
+ }).bind(this));
+ }
+ }
+
+ this._setup();
+
+ var data = this._createUI(element, options);
+ _data.set(elementId, {
+ dropdownMenu: null,
+ element: data.element,
+ list: data.list,
+ listItem: data.element.parentNode,
+ options: options,
+ shadow: data.shadow
+ });
+
+ values = (data.values.length) ? data.values : values;
+ if (Array.isArray(values)) {
+ var value;
+ for (var i = 0, length = values.length; i < length; i++) {
+ value = values[i];
+ if (typeof value === 'string') {
+ value = { objectId: 0, value: value };
+ }
+
+ this._addItem(elementId, value);
+ }
+ }
+ },
+
+ /**
+ * Returns the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @return {Array} list of objects containing object id and value
+ */
+ getValues: function(elementId) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+ var values = [];
+ elBySelAll('.item > span', data.list, function(span) {
+ values.push({
+ objectId: ~~elData(span, 'object-id'),
+ value: span.textContent
+ });
+ });
+
+ return values;
+ },
+
+ /**
+ * Sets the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of objects containing object id and value
+ */
+ setValues: function(elementId, values) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+
+ // remove all existing items first
+ var i, length;
+ var items = DomTraverse.childrenByClass(data.list, 'item');
+ for (i = 0, length = items.length; i < length; i++) {
+ this._removeItem(null, items[i], true);
+ }
+
+ // add new items
+ for (i = 0, length = values.length; i < length; i++) {
+ this._addItem(elementId, values[i]);
+ }
+ },
+
+ /**
+ * Binds static event listeners.
+ */
+ _setup: function() {
+ if (_didInit) {
+ return;
+ }
+
+ _didInit = true;
+
+ _callbackKeyDown = this._keyDown.bind(this);
+ _callbackKeyPress = this._keyPress.bind(this);
+ _callbackKeyUp = this._keyUp.bind(this);
+ _callbackPaste = this._paste.bind(this);
+ _callbackRemoveItem = this._removeItem.bind(this);
+ _callbackBlur = this._blur.bind(this);
+ },
+
+ /**
+ * Creates the DOM structure for target element. If `element` is a `<textarea>`
+ * it will be automatically replaced with an `<input>` element.
+ *
+ * @param {Element} element input element
+ * @param {Object} options option list
+ */
+ _createUI: function(element, options) {
+ var list = elCreate('ol');
+ list.className = 'inputItemList' + (element.disabled ? ' disabled' : '');
+ elData(list, 'element-id', element.id);
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ if (event.target === list) {
+ //noinspection JSUnresolvedFunction
+ element.focus();
+ }
+ });
+
+ var listItem = elCreate('li');
+ listItem.className = 'input';
+ list.appendChild(listItem);
+
+ element.addEventListener('keydown', _callbackKeyDown);
+ element.addEventListener('keypress', _callbackKeyPress);
+ element.addEventListener('keyup', _callbackKeyUp);
+ element.addEventListener('paste', _callbackPaste);
+ element.addEventListener('blur', _callbackBlur);
+
+ element.parentNode.insertBefore(list, element);
+ listItem.appendChild(element);
+
+ if (options.maxLength !== -1) {
+ elAttr(element, 'maxLength', options.maxLength);
+ }
+
+ var shadow = null, values = [];
+ if (options.isCSV) {
+ shadow = elCreate('input');
+ shadow.className = 'itemListInputShadow';
+ shadow.type = 'hidden';
+ //noinspection JSUnresolvedVariable
+ shadow.name = element.name;
+ element.removeAttribute('name');
+
+ list.parentNode.insertBefore(shadow, list);
+
+ //noinspection JSUnresolvedVariable
+ var value, tmp = element.value.split(',');
+ for (var i = 0, length = tmp.length; i < length; i++) {
+ value = tmp[i].trim();
+ if (value.length) {
+ values.push(value);
+ }
+ }
+
+ if (element.nodeName === 'TEXTAREA') {
+ var inputElement = elCreate('input');
+ inputElement.type = 'text';
+ element.parentNode.insertBefore(inputElement, element);
+ inputElement.id = element.id;
+
+ elRemove(element);
+ element = inputElement;
+ }
+ }
+
+ return {
+ element: element,
+ list: list,
+ shadow: shadow,
+ values: values
+ };
+ },
+
+ /**
+ * Enforces the maximum number of items.
+ *
+ * @param {string} elementId input element id
+ */
+ _handleLimit: function(elementId) {
+ var data = _data.get(elementId);
+ if (data.options.maxItems === -1) {
+ return;
+ }
+
+ if (data.list.childElementCount - 1 < data.options.maxItems) {
+ if (data.element.disabled) {
+ data.element.disabled = false;
+ data.element.removeAttribute('placeholder');
+ }
+ }
+ else if (!data.element.disabled) {
+ data.element.disabled = true;
+ elAttr(data.element, 'placeholder', Language.get('wcf.global.form.input.maxItems'));
+ }
+ },
+
+ /**
+ * Sets the active item list id and handles keyboard access to remove an existing item.
+ *
+ * @param {object} event event object
+ */
+ _keyDown: function(event) {
+ var input = event.currentTarget;
+ var lastItem = input.parentNode.previousElementSibling;
+
+ _activeId = input.id;
+
+ if (event.keyCode === 8) {
+ // 8 = [BACKSPACE]
+ if (input.value.length === 0) {
+ if (lastItem !== null) {
+ if (lastItem.classList.contains('active')) {
+ this._removeItem(null, lastItem);
+ }
+ else {
+ lastItem.classList.add('active');
+ }
+ }
+ }
+ }
+ else if (event.keyCode === 27) {
+ // 27 = [ESC]
+ if (lastItem !== null && lastItem.classList.contains('active')) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` and `[,]` key to add an item to the list.
+ *
+ * @param {Event} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event) || EventKey.Comma(event)) {
+ event.preventDefault();
+
+ var value = event.currentTarget.value.trim();
+ if (value.length) {
+ this._addItem(event.currentTarget.id, { objectId: 0, value: value });
+ }
+ }
+ },
+
+ /**
+ * Splits comma-separated values being pasted into the input field.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _paste: function (event) {
+ var text = '';
+ if (typeof window.clipboardData === 'object') {
+ // IE11
+ text = window.clipboardData.getData('Text');
+ }
+ else {
+ text = event.clipboardData.getData('text/plain');
+ }
+
+ text.split(/,/).forEach((function(item) {
+ item = item.trim();
+ if (item.length !== 0) {
+ this._addItem(event.currentTarget.id, { objectId: 0, value: item });
+ }
+ }).bind(this));
+
+ event.preventDefault();
+ },
+
+ /**
+ * Handles the keyup event to unmark an item for deletion.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ var input = event.currentTarget;
+
+ if (input.value.length > 0) {
+ var lastItem = input.parentNode.previousElementSibling;
+ if (lastItem !== null) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Adds an item to the list.
+ *
+ * @param {string} elementId input element id
+ * @param {object} value item value
+ */
+ _addItem: function(elementId, value) {
+ var data = _data.get(elementId);
+
+ var listItem = elCreate('li');
+ listItem.className = 'item';
+
+ var content = elCreate('span');
+ content.className = 'content';
+ elData(content, 'object-id', value.objectId);
+ content.textContent = value.value;
+ listItem.appendChild(content);
+
+ if (!data.element.disabled) {
+ var button = elCreate('a');
+ button.className = 'icon icon16 fa-times';
+ button.addEventListener(WCF_CLICK_EVENT, _callbackRemoveItem);
+ listItem.appendChild(button);
+ }
+
+ data.list.insertBefore(listItem, data.listItem);
+ data.element.value = '';
+
+ if (!data.element.disabled) {
+ this._handleLimit(elementId);
+ }
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Removes an item from the list.
+ *
+ * @param {?object} event event object
+ * @param {Element?} item list item
+ * @param {boolean?} noFocus input element will not be focused if true
+ */
+ _removeItem: function(event, item, noFocus) {
+ item = (event === null) ? item : event.currentTarget.parentNode;
+
+ var parent = item.parentNode;
+ //noinspection JSCheckFunctionSignatures
+ var elementId = elData(parent, 'element-id');
+ var data = _data.get(elementId);
+
+ parent.removeChild(item);
+ if (!noFocus) data.element.focus();
+
+ this._handleLimit(elementId);
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Synchronizes the shadow input field with the current list item values.
+ *
+ * @param {object} data element data
+ */
+ _syncShadow: function(data) {
+ if (!data.options.isCSV) return null;
+
+ var value = '', values = this.getValues(data.element.id);
+ for (var i = 0, length = values.length; i < length; i++) {
+ value += (value.length ? ',' : '') + values[i].value;
+ }
+
+ data.shadow.value = value;
+
+ return values;
+ },
+
+ /**
+ * Handles the blur event.
+ *
+ * @param {object} event event object
+ */
+ _blur: function(event) {
+ var data = _data.get(event.currentTarget.id);
+
+ var currentTarget = event.currentTarget;
+ window.setTimeout(function() {
+ var value = currentTarget.value.trim();
+ if (value.length) {
+ this._addItem(currentTarget.id, { objectId: 0, value: value });
+ }
+ }.bind(this), 100);
+ }
+ };
+});
+
+/**
+ * Provides an item list for users and groups.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList/User
+ */
+define('WoltLabSuite/Core/Ui/ItemList/User',['WoltLabSuite/Core/Ui/ItemList'], function(UiItemList) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ getValues: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/ItemList/User
+ */
+ return {
+ _shadowGroups: null,
+
+ /**
+ * Initializes user suggestion support for an element.
+ *
+ * @param {string} elementId input element id
+ * @param {object} options option list
+ */
+ init: function(elementId, options) {
+ this._shadowGroups = null;
+
+ UiItemList.init(elementId, [], {
+ ajax: {
+ className: 'wcf\\data\\user\\UserAction',
+ parameters: {
+ data: {
+ includeUserGroups: ~~options.includeUserGroups,
+ restrictUserGroupIDs: (Array.isArray(options.restrictUserGroupIDs) ? options.restrictUserGroupIDs : [])
+ }
+ }
+ },
+ callbackChange: (typeof options.callbackChange === 'function' ? options.callbackChange : null),
+ callbackSyncShadow: options.csvPerType ? this._syncShadow.bind(this) : null,
+ callbackSetupValues: (typeof options.callbackSetupValues === 'function' ? options.callbackSetupValues : null),
+ excludedSearchValues: (Array.isArray(options.excludedSearchValues) ? options.excludedSearchValues : []),
+ isCSV: true,
+ maxItems: ~~options.maxItems || -1,
+ restricted: true
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Ui/ItemList::getValues()
+ */
+ getValues: function(elementId) {
+ return UiItemList.getValues(elementId);
+ },
+
+ _syncShadow: function(data) {
+ var values = this.getValues(data.element.id);
+ var users = [], groups = [];
+
+ values.forEach(function(value) {
+ if (value.type && value.type === 'group') groups.push(value.objectId);
+ else users.push(value.value);
+ });
+
+ data.shadow.value = users.join(',');
+ if (!this._shadowGroups) {
+ this._shadowGroups = elCreate('input');
+ this._shadowGroups.type = 'hidden';
+ this._shadowGroups.name = data.shadow.name + 'GroupIDs';
+ data.shadow.parentNode.insertBefore(this._shadowGroups, data.shadow);
+ }
+ this._shadowGroups.value = groups.join(',');
+
+ return values;
+ }
+ };
+});
+
+/**
+ * Object-based user list.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/List
+ */
+define('WoltLabSuite/Core/Ui/User/List',['Ajax', 'Core', 'Dictionary', 'Dom/Util', 'Ui/Dialog', 'WoltLabSuite/Core/Ui/Pagination'], function(Ajax, Core, Dictionary, DomUtil, UiDialog, UiPagination) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiUserList(options) { this.init(options); }
+ UiUserList.prototype = {
+ /**
+ * Initializes the user list.
+ *
+ * @param {object} options list of initialization options
+ */
+ init: function(options) {
+ this._cache = new Dictionary();
+ this._pageCount = 0;
+ this._pageNo = 1;
+
+ this._options = Core.extend({
+ className: '',
+ dialogTitle: '',
+ parameters: {}
+ }, options);
+ },
+
+ /**
+ * Opens the user list.
+ */
+ open: function() {
+ this._pageNo = 1;
+ this._showPage();
+ },
+
+ /**
+ * Shows the current or given page.
+ *
+ * @param {int=} pageNo page number
+ */
+ _showPage: function(pageNo) {
+ if (typeof pageNo === 'number') {
+ this._pageNo = ~~pageNo;
+ }
+
+ if (this._pageCount !== 0 && (this._pageNo < 1 || this._pageNo > this._pageCount)) {
+ throw new RangeError("pageNo must be between 1 and " + this._pageCount + " (" + this._pageNo + " given).");
+ }
+
+ if (this._cache.has(this._pageNo)) {
+ var dialog = UiDialog.open(this, this._cache.get(this._pageNo));
+
+ if (this._pageCount > 1) {
+ var element = elBySel('.jsPagination', dialog.content);
+ if (element !== null) {
+ new UiPagination(element, {
+ activePage: this._pageNo,
+ maxPage: this._pageCount,
+
+ callbackSwitch: this._showPage.bind(this)
+ });
+ }
+
+ // scroll to the list start
+ var container = dialog.content.parentNode;
+ if (container.scrollTop > 0) {
+ container.scrollTop = 0;
+ }
+ }
+ }
+ else {
+ this._options.parameters.pageNo = this._pageNo;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.pageCount !== undefined) {
+ this._pageCount = ~~data.returnValues.pageCount;
+ }
+
+ this._cache.set(this._pageNo, data.returnValues.template);
+ this._showPage();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getGroupedUserList',
+ className: this._options.className,
+ interfaceName: 'wcf\\data\\IGroupedUserListAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: DomUtil.getUniqueId(),
+ options: {
+ title: this._options.dialogTitle
+ },
+ source: null
+ };
+ }
+ };
+
+ return UiUserList;
+});
+
+/**
+ * Provides interface elements to use reactions.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Reaction/Handler
+ * @since 5.2
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Reaction/CountButtons',[
+ 'Ajax', 'Core', 'Dictionary', 'Language',
+ 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util',
+ 'Ui/Dialog'
+ ],
+ function(
+ Ajax, Core, Dictionary, Language,
+ ObjectMap, StringUtil, DomChangeListener, DomUtil,
+ UiDialog
+ )
+ {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function CountButtons(objectType, options) { this.init(objectType, options); }
+ CountButtons.prototype = {
+ /**
+ * Initializes the like handler.
+ *
+ * @param {string} objectType object type
+ * @param {object} options initialization options
+ */
+ init: function(objectType, options) {
+ if (options.containerSelector === '') {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/CountButtons] Expected a non-empty string for option 'containerSelector'.");
+ }
+
+ this._containers = new Dictionary();
+ this._objects = new Dictionary();
+ this._objectType = objectType;
+
+ this._options = Core.extend({
+ // selectors
+ summaryListSelector: '.reactionSummaryList',
+ containerSelector: '',
+ isSingleItem: false,
+
+ // optional parameters
+ parameters: {
+ data: {}
+ }
+ }, options);
+
+ this.initContainers(options, objectType);
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Reaction/CountButtons-' + objectType, this.initContainers.bind(this));
+ },
+
+ /**
+ * Initialises the containers.
+ */
+ initContainers: function() {
+ var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false, objectId;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._containers.has(DomUtil.identify(element))) {
+ continue;
+ }
+
+ elementData = {
+ reactButton: null,
+ summary: null,
+
+ objectId: ~~elData(element, 'object-id'),
+ element: element
+ };
+
+ this._containers.set(DomUtil.identify(element), elementData);
+ this._initReactionCountButtons(element, elementData);
+
+ if (!this._objects.has(~~elData(element, 'object-id'))) {
+ var objects = [];
+ }
+ else {
+ var objects = this._objects.get(~~elData(element, 'object-id'));
+ }
+
+ objects.push(elementData);
+
+ this._objects.set(~~elData(element, 'object-id'), objects);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Update the count buttons with the given data.
+ *
+ * @param {int} objectId
+ * @param {object} data
+ */
+ updateCountButtons: function(objectId, data) {
+ var triggerChange = false;
+ this._objects.get(objectId).forEach(function(elementData) {
+ var summaryList = elBySel(this._options.summaryListSelector, elementData.element);
+
+ var sortedElements = {}, elements = elBySelAll('li', summaryList);
+ for (var i = 0, length = elements.length; i < length; i++) {
+ if (data[elData(elements[i], 'reaction-type-id')] !== undefined) {
+ sortedElements[elData(elements[i], 'reaction-type-id')] = elements[i];
+ }
+ else {
+ // reaction has no longer reactions
+ elRemove(elements[i]);
+ }
+ }
+
+ Object.keys(data).forEach(function(key) {
+ if (sortedElements[key] !== undefined) {
+ var reactionCount = elBySel('.reactionCount', sortedElements[key]);
+ reactionCount.innerHTML = StringUtil.shortUnit(data[key]);
+ }
+ else if (REACTION_TYPES[key] !== undefined) {
+ // create element
+ var createdElement = elCreate('li');
+ createdElement.className = 'reactCountButton';
+ elData(createdElement, 'reaction-type-id', key);
+
+ var countSpan = elCreate('span');
+ countSpan.className = 'reactionCount';
+ countSpan.innerHTML = StringUtil.shortUnit(data[key]);
+ createdElement.appendChild(countSpan);
+
+ createdElement.innerHTML = createdElement.innerHTML + REACTION_TYPES[key].renderedIcon;
+
+ summaryList.appendChild(createdElement);
+
+ this._initReactionCountButton(createdElement, objectId);
+
+ triggerChange = true;
+ }
+ }, this);
+ }.bind(this));
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Initialized the reaction count buttons.
+ *
+ * @param {element} element
+ * @param {object} elementData
+ */
+ _initReactionCountButtons: function(element, elementData) {
+ if (this._options.isSingleItem) {
+ var summaryList = elBySel(this._options.summaryListSelector);
+ }
+ else {
+ var summaryList = elBySel(this._options.summaryListSelector, element);
+ }
+
+ if (summaryList !== null) {
+ var elements = elBySelAll('li', summaryList);
+ for (var i = 0, length = elements.length; i < length; i++) {
+ this._initReactionCountButton(elements[i], elementData.objectId);
+ }
+ }
+ },
+
+ /**
+ * Initialized a specific reaction count button for an object.
+ *
+ * @param {element} element
+ * @param {int} objectId
+ */
+ _initReactionCountButton: function(element, objectId) {
+ element.addEventListener(WCF_CLICK_EVENT, this._showReactionOverlay.bind(this, objectId));
+ },
+
+ /**
+ * Shows the reaction overly for a specific object.
+ *
+ * @param {int} objectId
+ */
+ _showReactionOverlay: function(objectId) {
+ this._currentObjectId = objectId;
+ this._showOverlay();
+ },
+
+ /**
+ * Shows a specific page of the current opened reaction overlay.
+ *
+ * @param {int} pageNo
+ */
+ _showOverlay: function() {
+ this._options.parameters.data.containerID = this._objectType + '-' + this._currentObjectId;
+ this._options.parameters.data.objectID = this._currentObjectId;
+ this._options.parameters.data.objectType = this._objectType;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ },
+
+ _ajaxSuccess: function(data) {
+ UiDialog.open(this, data.returnValues.template);
+ UiDialog.setTitle('userReactionOverlay-' + this._objectType, data.returnValues.title);
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getReactionDetails',
+ className: '\\wcf\\data\\reaction\\ReactionAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'userReactionOverlay-' + this._objectType,
+ options: {
+ title: ""
+ },
+ source: null
+ };
+ }
+ };
+
+ return CountButtons;
+ });
+
+/**
+ * Provides interface elements to use reactions.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Reaction/Handler
+ * @since 5.2
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Reaction/Handler',[
+ 'Ajax', 'Core', 'Dictionary', 'Language',
+ 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util',
+ 'Ui/Dialog', 'WoltLabSuite/Core/Ui/User/List', 'User', 'WoltLabSuite/Core/Ui/Reaction/CountButtons',
+ 'Ui/Alignment', 'Ui/CloseOverlay', 'Ui/Screen'
+ ],
+ function(
+ Ajax, Core, Dictionary, Language,
+ ObjectMap, StringUtil, DomChangeListener, DomUtil,
+ UiDialog, UiUserList, User, CountButtons,
+ UiAlignment, UiCloseOverlay, UiScreen
+ )
+ {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiReactionHandler(objectType, options) { this.init(objectType, options); }
+ UiReactionHandler.prototype = {
+ /**
+ * Initializes the reaction handler.
+ *
+ * @param {string} objectType object type
+ * @param {object} options initialization options
+ */
+ init: function(objectType, options) {
+ if (options.containerSelector === '') {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Handler] Expected a non-empty string for option 'containerSelector'.");
+ }
+
+ this._containers = new Dictionary();
+ this._details = new ObjectMap();
+ this._objectType = objectType;
+ this._cache = new Dictionary();
+ this._objects = new Dictionary();
+
+ this._popoverCurrentObjectId = 0;
+
+ this._popover = null;
+
+ this._options = Core.extend({
+ // selectors
+ buttonSelector: '.reactButton',
+ containerSelector: '',
+ isButtonGroupNavigation: false,
+ isSingleItem: false,
+
+ // other stuff
+ parameters: {
+ data: {}
+ }
+ }, options);
+
+ this.initReactButtons(options, objectType);
+
+ this.countButtons = new CountButtons(this._objectType, this._options);
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Reaction/Handler-' + objectType, this.initReactButtons.bind(this));
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Reaction/Handler', this._closePopover.bind(this));
+ },
+
+ /**
+ * Initializes all applicable react buttons with the given selector.
+ */
+ initReactButtons: function() {
+ var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false, objectId;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._containers.has(DomUtil.identify(element))) {
+ continue;
+ }
+
+ elementData = {
+ reactButton: null,
+ objectId: ~~elData(element, 'object-id'),
+ element: element
+ };
+
+ this._containers.set(DomUtil.identify(element), elementData);
+ this._initReactButton(element, elementData);
+
+ if (!this._objects.has(~~elData(element, 'object-id'))) {
+ var objects = [];
+ }
+ else {
+ var objects = this._objects.get(~~elData(element, 'object-id'));
+ }
+
+ objects.push(elementData);
+
+ this._objects.set(~~elData(element, 'object-id'), objects);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+
+ /**
+ * Initializes a specific react button.
+ */
+ _initReactButton: function(element, elementData) {
+ if (this._options.isSingleItem) {
+ elementData.reactButton = elBySel(this._options.buttonSelector);
+ }
+ else {
+ elementData.reactButton = elBySel(this._options.buttonSelector, element);
+ }
+
+ if (elementData.reactButton === null || elementData.reactButton.length === 0) {
+ // the element may have no react button
+ return;
+ }
+
+ if (Object.keys(REACTION_TYPES).length === 1) {
+ var reaction = REACTION_TYPES[Object.keys(REACTION_TYPES)[0]];
+ elementData.reactButton.title = reaction.title;
+ var textSpan = elBySel('.invisible', elementData.reactButton);
+ textSpan.innerText = reaction.title;
+ }
+
+ if (elementData.reactButton.closest('.messageFooterGroup > .jsMobileNavigation')) {
+ UiScreen.on('screen-sm-down', {
+ match: this._enableMobileView.bind(this, elementData.reactButton, elementData.objectId),
+ unmatch: this._disableMobileView.bind(this, elementData.reactButton, elementData.objectId),
+ setup: this._setupMobileView.bind(this, elementData.reactButton, elementData.objectId)
+ });
+ }
+
+ elementData.reactButton.addEventListener(WCF_CLICK_EVENT, this._toggleReactPopover.bind(this, elementData.objectId, elementData.reactButton));
+ },
+
+ /**
+ * Enables the mobile view for the reaction button.
+ *
+ * @param {Element} element
+ */
+ _enableMobileView: function(element) {
+ var messageFooterGroup = element.closest('.messageFooterGroup');
+
+ elShow(elBySel('.mobileReactButton', messageFooterGroup));
+ },
+
+ /**
+ * Disables the mobile view for the reaction button.
+ *
+ * @param {Element} element
+ */
+ _disableMobileView: function(element) {
+ var messageFooterGroup = element.closest('.messageFooterGroup');
+
+ elHide(elBySel('.mobileReactButton', messageFooterGroup));
+ },
+
+ /**
+ * Setup the mobile view for the reaction button.
+ *
+ * @param {Element} element
+ * @param {int} objectID
+ */
+ _setupMobileView: function(element, objectID) {
+ var messageFooterGroup = element.closest('.messageFooterGroup');
+
+ var button = elCreate('button');
+ button.classList = 'mobileReactButton';
+ button.innerHTML = element.innerHTML;
+
+ button.addEventListener(WCF_CLICK_EVENT, this._toggleReactPopover.bind(this, objectID, button));
+
+ messageFooterGroup.appendChild(button);
+ },
+
+ _updateReactButton: function(objectID, reactionTypeID) {
+ this._objects.get(objectID).forEach(function (elementData) {
+ if (reactionTypeID) {
+ elementData.reactButton.classList.add('active');
+ elData(elementData.reactButton, 'reaction-type-id', reactionTypeID);
+ }
+ else {
+ elData(elementData.reactButton, 'reaction-type-id', 0);
+ elementData.reactButton.classList.remove('active');
+ }
+ });
+ },
+
+ _markReactionAsActive: function() {
+ var reactionTypeID = elData(this._objects.get(this._popoverCurrentObjectId)[0].reactButton, 'reaction-type-id');
+
+ // clear old active state
+ var elements = elBySelAll('.reactionTypeButton.active', this._getPopover());
+ for (var i = 0, length = elements.length; i < length; i++) {
+ elements[i].classList.remove('active');
+ }
+
+ if (reactionTypeID != 0) {
+ elBySel('.reactionTypeButton[data-reaction-type-id="'+reactionTypeID+'"]', this._getPopover()).classList.add('active');
+ }
+ },
+
+ /**
+ * Toggle the visibility of the react popover.
+ *
+ * @param {int} objectId
+ * @param {Element} element
+ */
+ _toggleReactPopover: function(objectId, element, event) {
+ if (event !== null) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ if (Object.keys(REACTION_TYPES).length === 1) {
+ var reaction = REACTION_TYPES[Object.keys(REACTION_TYPES)[0]];
+ this._popoverCurrentObjectId = objectId;
+
+ this._react(reaction.reactionTypeID);
+ }
+ else {
+ if (this._popoverCurrentObjectId === 0 || this._popoverCurrentObjectId !== objectId) {
+ this._openReactPopover(objectId, element);
+ }
+ else {
+ this._closePopover(objectId, element);
+ }
+ }
+ },
+
+ /**
+ * Opens the react popover for a specific react button.
+ *
+ * @param {int} objectId objectId of the element
+ * @param {Element} element container element
+ */
+ _openReactPopover: function(objectId, element) {
+ // first close old popover, if exists
+ if (this._popoverCurrentObjectId !== 0) {
+ this._closePopover();
+ }
+
+ this._popoverCurrentObjectId = objectId;
+ this._markReactionAsActive();
+
+ UiAlignment.set(this._getPopover(), element, {
+ pointer: true,
+ horizontal: (this._options.isButtonGroupNavigation) ? 'left' :'center',
+ vertical: 'top'
+ });
+
+ if (this._options.isButtonGroupNavigation) {
+ // find nav element
+ var nav = element.closest('nav');
+ nav.style.opacity = "1";
+ }
+
+ this._getPopover().classList.remove('forceHide');
+ this._getPopover().classList.add('active');
+ },
+
+ /**
+ * Returns the react popover element.
+ *
+ * @returns {Element}
+ */
+ _getPopover: function() {
+ if (this._popover == null) {
+ this._popover = elCreate('div');
+ this._popover.className = 'reactionPopover forceHide';
+
+ var _popoverContent = elCreate('div');
+ _popoverContent.className = 'reactionPopoverContent';
+
+ var popoverContentHTML = elCreate('ul');
+
+ var sortedReactionTypes = this._getSortedReactionTypes();
+
+ for (var key in sortedReactionTypes) {
+ if (!sortedReactionTypes.hasOwnProperty(key)) continue;
+
+ var reactionType = sortedReactionTypes[key];
+
+ var reactionTypeItem = elCreate('li');
+ reactionTypeItem.className = 'reactionTypeButton jsTooltip';
+ elData(reactionTypeItem, 'reaction-type-id', reactionType.reactionTypeID);
+ elData(reactionTypeItem, 'title', reactionType.title);
+ reactionTypeItem.title = reactionType.title;
+
+ var reactionTypeItemSpan = elCreate('span');
+ reactionTypeItemSpan.classList = 'reactionTypeButtonTitle';
+ reactionTypeItemSpan.innerHTML = reactionType.title;
+
+ reactionTypeItem.innerHTML = reactionType.renderedIcon;
+
+ reactionTypeItem.appendChild(reactionTypeItemSpan);
+
+ reactionTypeItem.addEventListener(WCF_CLICK_EVENT, this._react.bind(this, reactionType.reactionTypeID));
+
+ popoverContentHTML.appendChild(reactionTypeItem);
+ }
+
+ _popoverContent.appendChild(popoverContentHTML);
+ this._popover.appendChild(_popoverContent);
+
+ var pointer = elCreate('span');
+ pointer.className = 'elementPointer';
+ pointer.appendChild(elCreate('span'));
+ this._popover.appendChild(pointer);
+
+ document.body.appendChild(this._popover);
+
+ DomChangeListener.trigger();
+ }
+
+ return this._popover;
+ },
+
+ /**
+ * Sort the reaction types by the showOrder field.
+ *
+ * @returns {Array} the reaction types sorted by showOrder
+ */
+ _getSortedReactionTypes: function() {
+ var sortedReactionTypes = [];
+
+ // convert our reaction type object to an array
+ for (var key in REACTION_TYPES) {
+ if (!REACTION_TYPES.hasOwnProperty(key)) continue;
+ sortedReactionTypes.push(REACTION_TYPES[key]);
+ }
+
+ // sort the array
+ sortedReactionTypes.sort(function (a, b) {
+ return a.showOrder - b.showOrder;
+ });
+
+ return sortedReactionTypes;
+ },
+
+ /**
+ * Closes the react popover.
+ */
+ _closePopover: function() {
+ if (this._popoverCurrentObjectId !== 0) {
+ this._getPopover().classList.remove('active');
+
+ if (this._options.isButtonGroupNavigation) {
+ this._objects.get(this._popoverCurrentObjectId).forEach(function (elementData) {
+ elementData.reactButton.closest('nav').style.cssText = "";
+ });
+ }
+
+ this._popoverCurrentObjectId = 0;
+ }
+ },
+
+ /**
+ * React with the given reactionTypeId on an object.
+ *
+ * @param {init} reactionTypeId
+ */
+ _react: function(reactionTypeId) {
+ this._options.parameters.reactionTypeID = reactionTypeId;
+ this._options.parameters.data.containerID = this._currentReactionTypeId;
+ this._options.parameters.data.objectID = this._popoverCurrentObjectId;
+ this._options.parameters.data.objectType = this._objectType;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+
+ this._closePopover();
+ },
+
+ _ajaxSuccess: function(data) {
+ this.countButtons.updateCountButtons(data.returnValues.objectID, data.returnValues.reactions);
+
+ // update react button status
+ this._updateReactButton(data.returnValues.objectID, data.returnValues.reactionTypeID);
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'react',
+ className: '\\wcf\\data\\reaction\\ReactionAction'
+ }
+ };
+ }
+ };
+
+ return UiReactionHandler;
+ });
+
+/**
+ * Provides interface elements to display and review likes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Like/Handler
+ * @deprecated 5.2 use ReactionHandler instead
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Like/Handler',[
+ 'Ajax', 'Core', 'Dictionary', 'Language',
+ 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util',
+ 'Ui/Dialog', 'WoltLabSuite/Core/Ui/User/List', 'User', 'WoltLabSuite/Core/Ui/Reaction/Handler'
+ ],
+ function(
+ Ajax, Core, Dictionary, Language,
+ ObjectMap, StringUtil, DomChangeListener, DomUtil,
+ UiDialog, UiUserList, User, UiReactionHandler
+ )
+{
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiLikeHandler(objectType, options) { this.init(objectType, options); }
+ UiLikeHandler.prototype = {
+ /**
+ * Initializes the like handler.
+ *
+ * @param {string} objectType object type
+ * @param {object} options initialization options
+ */
+ init: function(objectType, options) {
+ if (options.containerSelector === '') {
+ throw new Error("[WoltLabSuite/Core/Ui/Like/Handler] Expected a non-empty string for option 'containerSelector'.");
+ }
+
+ this._containers = new ObjectMap();
+ this._details = new ObjectMap();
+ this._objectType = objectType;
+ this._options = Core.extend({
+ // settings
+ badgeClassNames: '',
+ isSingleItem: false,
+ markListItemAsActive: false,
+ renderAsButton: true,
+ summaryPrepend: true,
+ summaryUseIcon: true,
+
+ // permissions
+ canDislike: false,
+ canLike: false,
+ canLikeOwnContent: false,
+ canViewSummary: false,
+
+ // selectors
+ badgeContainerSelector: '.messageHeader .messageStatus',
+ buttonAppendToSelector: '.messageFooter .messageFooterButtons',
+ buttonBeforeSelector: '',
+ containerSelector: '',
+ summarySelector: '.messageFooterGroup'
+ }, options);
+
+ this.initContainers(options, objectType);
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Like/Handler-' + objectType, this.initContainers.bind(this));
+
+ new UiReactionHandler(this._objectType, {
+ containerSelector: this._options.containerSelector,
+ summaryListSelector: '.reactionSummaryList'
+ });
+ },
+
+ /**
+ * Initializes all applicable containers.
+ */
+ initContainers: function() {
+ var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._containers.has(element)) {
+ continue;
+ }
+
+ elementData = {
+ badge: null,
+ dislikeButton: null,
+ likeButton: null,
+ summary: null,
+
+ dislikes: ~~elData(element, 'like-dislikes'),
+ liked: ~~elData(element, 'like-liked'),
+ likes: ~~elData(element, 'like-likes'),
+ objectId: ~~elData(element, 'object-id'),
+ users: JSON.parse(elData(element, 'like-users'))
+ };
+
+ this._containers.set(element, elementData);
+ this._buildWidget(element, elementData);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Creates the interface elements.
+ *
+ * @param {Element} element container element
+ * @param {object} elementData like data
+ */
+ _buildWidget: function(element, elementData) {
+ // build reaction summary list
+ var summaryList, listItem, badgeContainer, isSummaryPosition = true;
+ badgeContainer = (this._options.isSingleItem) ? elBySel(this._options.summarySelector) : elBySel(this._options.summarySelector, element);
+ if (badgeContainer === null) {
+ badgeContainer = (this._options.isSingleItem) ? elBySel(this._options.badgeContainerSelector) : elBySel(this._options.badgeContainerSelector, element);
+ isSummaryPosition = false;
+ }
+
+ if (badgeContainer !== null) {
+ summaryList = elCreate('ul');
+ summaryList.classList.add('reactionSummaryList');
+ if (isSummaryPosition) {
+ summaryList.classList.add('likesSummary');
+ }
+ else {
+ summaryList.classList.add('reactionSummaryListTiny');
+ }
+
+ for (var key in elementData.users) {
+ if (key === "reactionTypeID") continue;
+ if (!REACTION_TYPES.hasOwnProperty(key)) continue;
+
+ // create element
+ var createdElement = elCreate('li');
+ createdElement.className = 'reactCountButton';
+ elData(createdElement, 'reaction-type-id', key);
+
+ var countSpan = elCreate('span');
+ countSpan.className = 'reactionCount';
+ countSpan.innerHTML = StringUtil.shortUnit(elementData.users[key]);
+ createdElement.appendChild(countSpan);
+
+ createdElement.innerHTML = createdElement.innerHTML + REACTION_TYPES[key].renderedIcon;
+
+ summaryList.appendChild(createdElement);
+
+ }
+
+ if (isSummaryPosition) {
+ if (this._options.summaryPrepend) {
+ DomUtil.prepend(summaryList, badgeContainer);
+ }
+ else {
+ badgeContainer.appendChild(summaryList);
+ }
+ }
+ else {
+ if (badgeContainer.nodeName === 'OL' || badgeContainer.nodeName === 'UL') {
+ listItem = elCreate('li');
+ listItem.appendChild(summaryList);
+ badgeContainer.appendChild(listItem);
+ }
+ else {
+ badgeContainer.appendChild(summaryList);
+ }
+ }
+
+ elementData.badge = summaryList;
+ }
+
+ // build reaction button
+ if (this._options.canLike && (User.userId != elData(element, 'user-id') || this._options.canLikeOwnContent)) {
+ var appendTo = (this._options.buttonAppendToSelector) ? ((this._options.isSingleItem) ? elBySel(this._options.buttonAppendToSelector) : elBySel(this._options.buttonAppendToSelector, element)) : null;
+ var insertPosition = (this._options.buttonBeforeSelector) ? ((this._options.isSingleItem) ? elBySel(this._options.buttonBeforeSelector) : elBySel(this._options.buttonBeforeSelector, element)) : null;
+ if (insertPosition === null && appendTo === null) {
+ throw new Error("Unable to find insert location for like/dislike buttons.");
+ }
+ else {
+ elementData.likeButton = this._createButton(element, elementData.users.reactionTypeID, insertPosition, appendTo);
+ }
+ }
+ },
+
+ /**
+ * Creates a reaction button.
+ *
+ * @param {Element} element container element
+ * @param {int} reactionTypeID the reactionTypeID of the current state
+ * @param {Element?} insertBefore insert button before given element
+ * @param {Element?} appendTo append button to given element
+ * @return {Element} button element
+ */
+ _createButton: function(element, reactionTypeID, insertBefore, appendTo) {
+ var title = Language.get('wcf.reactions.react');
+
+ var listItem = elCreate('li');
+ listItem.className = 'wcfReactButton';
+
+ if (insertBefore) {
+ var jsMobileNavigation = insertBefore.parentElement.contains('jsMobileNavigation');
+ }
+ else {
+ var jsMobileNavigation = appendTo.classList.contains('jsMobileNavigation');
+ }
+
+ var button = elCreate('a');
+ button.className = 'jsTooltip reactButton';
+ if (this._options.renderAsButton) {
+ button.classList.add('button');
+
+ if (jsMobileNavigation) {
+ button.classList.add('ignoreMobileNavigation');
+ }
+ }
+
+ button.href = '#';
+ button.title = title;
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-smile-o';
+
+ if (reactionTypeID === undefined || reactionTypeID == 0) {
+ elData(icon, 'reaction-type-id', 0);
+ }
+ else {
+ elData(button, 'reaction-type-id', reactionTypeID);
+ button.classList.add("active");
+ }
+
+ button.appendChild(icon);
+
+ var invisibleText = elCreate("span");
+ invisibleText.className = "invisible";
+ invisibleText.innerHTML = title;
+
+ button.appendChild(document.createTextNode(" "));
+ button.appendChild(invisibleText);
+
+ listItem.appendChild(button);
+
+ if (insertBefore) {
+ insertBefore.parentNode.insertBefore(listItem, insertBefore);
+ }
+ else {
+ appendTo.appendChild(listItem);
+ }
+
+ return button;
+ }
+ };
+
+ return UiLikeHandler;
+});
+
+/**
+ * Flexible message inline editor.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/InlineEditor
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Message/InlineEditor',[
+ 'Ajax', 'Core', 'Dictionary', 'Environment',
+ 'EventHandler', 'Language', 'ObjectMap', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll'
+ ],
+ function(
+ Ajax, Core, Dictionary, Environment,
+ EventHandler, Language, ObjectMap, DomChangeListener, DomTraverse,
+ DomUtil, UiNotification, UiReusableDropdown, UiScroll
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ _click: function() {},
+ _clickDropdown: function() {},
+ _dropdownBuild: function() {},
+ _dropdownToggle: function() {},
+ _dropdownGetItems: function() {},
+ _dropdownOpen: function() {},
+ _dropdownSelect: function() {},
+ _clickDropdownItem: function() {},
+ _prepare: function() {},
+ _showEditor: function() {},
+ _restoreMessage: function() {},
+ _save: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showMessage: function() {},
+ _hideEditor: function() {},
+ _restoreEditor: function() {},
+ _destroyEditor: function() {},
+ _getHash: function() {},
+ _updateHistory: function() {},
+ _getEditorId: function() {},
+ _getObjectId: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ legacyEdit: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiMessageInlineEditor(options) { this.init(options); }
+ UiMessageInlineEditor.prototype = {
+ /**
+ * Initializes the message inline editor.
+ *
+ * @param {Object} options list of configuration options
+ */
+ init: function(options) {
+ this._activeDropdownElement = null;
+ this._activeElement = null;
+ this._dropdownMenu = null;
+ this._elements = new ObjectMap();
+ this._options = Core.extend({
+ canEditInline: false,
+
+ className: '',
+ containerId: 0,
+ dropdownIdentifier: '',
+ editorPrefix: 'messageEditor',
+
+ messageSelector: '.jsMessage',
+
+ quoteManager: null
+ }, options);
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Message/InlineEdit_' + this._options.className, this.rebuild.bind(this));
+ },
+
+ /**
+ * Initializes each applicable message, should be called whenever new
+ * messages are being displayed.
+ */
+ rebuild: function() {
+ var button, canEdit, element, elements = elBySelAll(this._options.messageSelector);
+
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._elements.has(element)) {
+ continue;
+ }
+
+ button = elBySel('.jsMessageEditButton', element);
+ if (button !== null) {
+ canEdit = elDataBool(element, 'can-edit');
+
+ if (this._options.canEditInline || elDataBool(element, 'can-edit-inline')) {
+ button.addEventListener(WCF_CLICK_EVENT, this._clickDropdown.bind(this, element));
+ button.classList.add('jsDropdownEnabled');
+
+ if (canEdit) {
+ button.addEventListener('dblclick', this._click.bind(this, element));
+ }
+ }
+ else if (canEdit) {
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this, element));
+ }
+ }
+
+ var messageBody = elBySel('.messageBody', element);
+ var messageFooter = elBySel('.messageFooter', element);
+ var messageHeader = elBySel('.messageHeader', element);
+
+ this._elements.set(element, {
+ button: button,
+ messageBody: messageBody,
+ messageBodyEditor: null,
+ messageFooter: messageFooter,
+ messageFooterButtons: elBySel('.messageFooterButtons', messageFooter),
+ messageHeader: messageHeader,
+ messageText: elBySel('.messageText', messageBody)
+ });
+ }
+ },
+
+ /**
+ * Handles clicks on the edit button or the edit dropdown item.
+ *
+ * @param {Element} element message element
+ * @param {?Event} event event object
+ * @protected
+ */
+ _click: function(element, event) {
+ if (element === null) element = this._activeDropdownElement;
+ if (event) event.preventDefault();
+
+ if (this._activeElement === null) {
+ this._activeElement = element;
+
+ this._prepare();
+
+ Ajax.api(this, {
+ actionName: 'beginEdit',
+ parameters: {
+ containerID: this._options.containerId,
+ objectID: this._getObjectId(element)
+ }
+ });
+ }
+ else {
+ UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+ }
+ },
+
+ /**
+ * Creates and opens the dropdown on first usage.
+ *
+ * @param {Element} element message element
+ * @param {Object} event event object
+ * @protected
+ */
+ _clickDropdown: function(element, event) {
+ event.preventDefault();
+
+ var button = event.currentTarget;
+ if (button.classList.contains('dropdownToggle')) {
+ return;
+ }
+
+ button.classList.add('dropdownToggle');
+ button.parentNode.classList.add('dropdown');
+ (function(button, element) {
+ button.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this._activeDropdownElement = element;
+ UiReusableDropdown.toggleDropdown(this._options.dropdownIdentifier, button);
+ }).bind(this));
+ }).bind(this)(button, element);
+
+ // build dropdown
+ if (this._dropdownMenu === null) {
+ this._dropdownMenu = elCreate('ul');
+ this._dropdownMenu.className = 'dropdownMenu';
+
+ var items = this._dropdownGetItems();
+
+ EventHandler.fire('com.woltlab.wcf.inlineEditor', 'dropdownInit_' + this._options.dropdownIdentifier, {
+ items: items
+ });
+
+ this._dropdownBuild(items);
+
+ UiReusableDropdown.init(this._options.dropdownIdentifier, this._dropdownMenu);
+ UiReusableDropdown.registerCallback(this._options.dropdownIdentifier, this._dropdownToggle.bind(this));
+ }
+
+ setTimeout(function() {
+ Core.triggerEvent(button, WCF_CLICK_EVENT);
+ }, 10);
+ },
+
+ /**
+ * Creates the dropdown menu on first usage.
+ *
+ * @param {Object} items list of dropdown items
+ * @protected
+ */
+ _dropdownBuild: function(items) {
+ var item, label, listItem;
+ var callbackClick = this._clickDropdownItem.bind(this);
+
+ for (var i = 0, length = items.length; i < length; i++) {
+ item = items[i];
+ listItem = elCreate('li');
+ elData(listItem, 'item', item.item);
+
+ if (item.item === 'divider') {
+ listItem.className = 'dropdownDivider';
+ }
+ else {
+ label = elCreate('span');
+ label.textContent = Language.get(item.label);
+ listItem.appendChild(label);
+
+ if (item.item === 'editItem') {
+ listItem.addEventListener(WCF_CLICK_EVENT, this._click.bind(this, null));
+ }
+ else {
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ }
+ }
+
+ this._dropdownMenu.appendChild(listItem);
+ }
+ },
+
+ /**
+ * Callback for dropdown toggle.
+ *
+ * @param {int} containerId container id
+ * @param {string} action toggle action, either 'open' or 'close'
+ * @protected
+ */
+ _dropdownToggle: function(containerId, action) {
+ var elementData = this._elements.get(this._activeDropdownElement);
+ elementData.button.parentNode.classList[(action === 'open' ? 'add' : 'remove')]('dropdownOpen');
+ elementData.messageFooterButtons.classList[(action === 'open' ? 'add' : 'remove')]('forceVisible');
+
+ if (action === 'open') {
+ var visibility = this._dropdownOpen();
+
+ EventHandler.fire('com.woltlab.wcf.inlineEditor', 'dropdownOpen_' + this._options.dropdownIdentifier, {
+ element: this._activeDropdownElement,
+ visibility: visibility
+ });
+
+ var item, listItem, visiblePredecessor = false;
+ for (var i = 0; i < this._dropdownMenu.childElementCount; i++) {
+ listItem = this._dropdownMenu.children[i];
+ item = elData(listItem, 'item');
+
+ if (item === 'divider') {
+ if (visiblePredecessor) {
+ elShow(listItem);
+
+ visiblePredecessor = false;
+ }
+ else {
+ elHide(listItem);
+ }
+ }
+ else {
+ if (objOwns(visibility, item) && visibility[item] === false) {
+ elHide(listItem);
+
+ // check if previous item was a divider
+ if (i > 0 && i + 1 === this._dropdownMenu.childElementCount) {
+ if (elData(listItem.previousElementSibling, 'item') === 'divider') {
+ elHide(listItem.previousElementSibling);
+ }
+ }
+ }
+ else {
+ elShow(listItem);
+
+ visiblePredecessor = true;
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Returns the list of dropdown items for this type.
+ *
+ * @return {Array<Object>} list of objects containing the type name and label
+ * @protected
+ */
+ _dropdownGetItems: function() {},
+
+ /**
+ * Invoked once the dropdown for this type is shown, expects a list of type name and a boolean value
+ * to represent the visibility of each item. Items that do not appear in this list will be considered
+ * visible.
+ *
+ * @return {Object<string, boolean>}
+ * @protected
+ */
+ _dropdownOpen: function() {},
+
+ /**
+ * Invoked whenever the user selects an item from the dropdown menu, the selected item is passed as argument.
+ *
+ * @param {string} item selected dropdown item
+ * @protected
+ */
+ _dropdownSelect: function(item) {},
+
+ /**
+ * Handles clicks on a dropdown item.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _clickDropdownItem: function(event) {
+ event.preventDefault();
+
+ //noinspection JSCheckFunctionSignatures
+ var item = elData(event.currentTarget, 'item');
+ var data = {
+ cancel: false,
+ element: this._activeDropdownElement,
+ item: item
+ };
+ EventHandler.fire('com.woltlab.wcf.inlineEditor', 'dropdownItemClick_' + this._options.dropdownIdentifier, data);
+
+ if (data.cancel === true) {
+ event.preventDefault();
+ }
+ else {
+ this._dropdownSelect(item);
+ }
+ },
+
+ /**
+ * Prepares the message for editor display.
+ *
+ * @protected
+ */
+ _prepare: function() {
+ var data = this._elements.get(this._activeElement);
+
+ var messageBodyEditor = elCreate('div');
+ messageBodyEditor.className = 'messageBody editor';
+ data.messageBodyEditor = messageBodyEditor;
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ messageBodyEditor.appendChild(icon);
+
+ DomUtil.insertAfter(messageBodyEditor, data.messageBody);
+
+ elHide(data.messageBody);
+ },
+
+ /**
+ * Shows the message editor.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showEditor: function(data) {
+ var id = this._getEditorId();
+ var elementData = this._elements.get(this._activeElement);
+
+ this._activeElement.classList.add('jsInvalidQuoteTarget');
+ var icon = DomTraverse.childByClass(elementData.messageBodyEditor, 'icon');
+ elRemove(icon);
+
+ var messageBody = elementData.messageBodyEditor;
+ var editor = elCreate('div');
+ editor.className = 'editorContainer';
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(editor, data.returnValues.template);
+ messageBody.appendChild(editor);
+
+ // bind buttons
+ var formSubmit = elBySel('.formSubmit', editor);
+
+ var buttonSave = elBySel('button[data-type="save"]', formSubmit);
+ buttonSave.addEventListener(WCF_CLICK_EVENT, this._save.bind(this));
+
+ var buttonCancel = elBySel('button[data-type="cancel"]', formSubmit);
+ buttonCancel.addEventListener(WCF_CLICK_EVENT, this._restoreMessage.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.redactor', 'submitEditor_' + id, (function(data) {
+ data.cancel = true;
+
+ this._save();
+ }).bind(this));
+
+ // hide message header and footer
+ elHide(elementData.messageHeader);
+ elHide(elementData.messageFooter);
+
+ var editorElement = elById(id);
+ if (Environment.editor() === 'redactor') {
+ window.setTimeout((function() {
+ if (this._options.quoteManager) {
+ this._options.quoteManager.setAlternativeEditor(id);
+ }
+
+ UiScroll.element(this._activeElement);
+ }).bind(this), 250);
+ }
+ else {
+ editorElement.focus();
+ }
+ },
+
+ /**
+ * Restores the message view.
+ *
+ * @protected
+ */
+ _restoreMessage: function() {
+ var elementData = this._elements.get(this._activeElement);
+
+ this._destroyEditor();
+
+ elRemove(elementData.messageBodyEditor);
+ elementData.messageBodyEditor = null;
+
+ elShow(elementData.messageBody);
+ elShow(elementData.messageFooter);
+ elShow(elementData.messageHeader);
+ this._activeElement.classList.remove('jsInvalidQuoteTarget');
+
+ this._activeElement = null;
+
+ if (this._options.quoteManager) {
+ this._options.quoteManager.clearAlternativeEditor();
+ }
+ },
+
+ /**
+ * Saves the editor message.
+ *
+ * @protected
+ */
+ _save: function() {
+ var parameters = {
+ containerID: this._options.containerId,
+ data: {
+ message: ''
+ },
+ objectID: this._getObjectId(this._activeElement),
+ removeQuoteIDs: (this._options.quoteManager) ? this._options.quoteManager.getQuotesMarkedForRemoval() : []
+ };
+
+ var id = this._getEditorId();
+
+ // add any available settings
+ var settingsContainer = elById('settings_' + id);
+ if (settingsContainer) {
+ elBySelAll('input, select, textarea', settingsContainer, function (element) {
+ if (element.nodeName === 'INPUT' && (element.type === 'checkbox' || element.type === 'radio')) {
+ if (!element.checked) {
+ return;
+ }
+ }
+
+ var name = element.name;
+ if (parameters.hasOwnProperty(name)) {
+ throw new Error("Variable overshadowing, key '" + name + "' is already present.");
+ }
+
+ parameters[name] = element.value.trim();
+ });
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'getText_' + id, parameters.data);
+
+ var validateResult = this._validate(parameters);
+
+ if (!(validateResult instanceof Promise)) {
+ if (validateResult === false) {
+ validateResult = Promise.reject();
+ }
+ else {
+ validateResult = Promise.resolve();
+ }
+ }
+
+ validateResult.then(function () {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_' + id, parameters);
+
+ Ajax.api(this, {
+ actionName: 'save',
+ parameters: parameters
+ });
+
+ this._hideEditor();
+ }.bind(this), function(e) {
+ console.log('Validation of post edit failed: '+ e);
+ });
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @param {Object} parameters request parameters
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function(parameters) {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._activeElement, elRemove);
+
+ var data = {
+ api: this,
+ parameters: parameters,
+ valid: true,
+ promises: []
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_' + this._getEditorId(), data);
+
+ data.promises.push(Promise[data.valid ? 'resolve' : 'reject']());
+
+ return Promise.all(data.promises);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, message);
+ },
+
+ /**
+ * Shows the update message.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showMessage: function(data) {
+ var activeElement = this._activeElement;
+ var editorId = this._getEditorId();
+ var elementData = this._elements.get(activeElement);
+ var attachmentLists = elBySelAll('.attachmentThumbnailList, .attachmentFileList', elementData.messageFooter);
+
+ // set new content
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(DomTraverse.childByClass(elementData.messageBody, 'messageText'), data.returnValues.message);
+
+ // handle attachment list
+ //noinspection JSUnresolvedVariable
+ if (typeof data.returnValues.attachmentList === 'string') {
+ for (var i = 0, length = attachmentLists.length; i < length; i++) {
+ elRemove(attachmentLists[i]);
+ }
+
+ var element = elCreate('div');
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(element, data.returnValues.attachmentList);
+
+ var node;
+ while (element.childNodes.length) {
+ node = element.childNodes[element.childNodes.length - 1];
+ elementData.messageFooter.insertBefore(node, elementData.messageFooter.firstChild);
+ }
+ }
+
+ // handle poll
+ //noinspection JSUnresolvedVariable
+ if (typeof data.returnValues.poll === 'string') {
+ // find current poll
+ var poll = elBySel('.pollContainer', elementData.messageBody);
+ if (poll !== null) {
+ // poll contain is wrapped inside `.jsInlineEditorHideContent`
+ elRemove(poll.parentNode);
+ }
+
+ var pollContainer = elCreate('div');
+ pollContainer.className = 'jsInlineEditorHideContent';
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(pollContainer, data.returnValues.poll);
+
+ DomUtil.prepend(pollContainer, elementData.messageBody);
+ }
+
+ this._restoreMessage();
+
+ this._updateHistory(this._getHash(this._getObjectId(activeElement)));
+
+ EventHandler.fire('com.woltlab.wcf.redactor', 'autosaveDestroy_' + editorId);
+
+ UiNotification.show();
+
+ if (this._options.quoteManager) {
+ this._options.quoteManager.clearAlternativeEditor();
+ this._options.quoteManager.countQuotes();
+ }
+ },
+
+ /**
+ * Hides the editor from view.
+ *
+ * @protected
+ */
+ _hideEditor: function() {
+ var elementData = this._elements.get(this._activeElement);
+ elHide(DomTraverse.childByClass(elementData.messageBodyEditor, 'editorContainer'));
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ elementData.messageBodyEditor.appendChild(icon);
+ },
+
+ /**
+ * Restores the previously hidden editor.
+ *
+ * @protected
+ */
+ _restoreEditor: function() {
+ var elementData = this._elements.get(this._activeElement);
+ var icon = elBySel('.fa-spinner', elementData.messageBodyEditor);
+ elRemove(icon);
+
+ var editorContainer = DomTraverse.childByClass(elementData.messageBodyEditor, 'editorContainer');
+ if (editorContainer !== null) elShow(editorContainer);
+ },
+
+ /**
+ * Destroys the editor instance.
+ *
+ * @protected
+ */
+ _destroyEditor: function() {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveDestroy_' + this._getEditorId());
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'destroy_' + this._getEditorId());
+ },
+
+ /**
+ * Returns the hash added to the url after successfully editing a message.
+ *
+ * @param {int} objectId message object id
+ * @return string
+ * @protected
+ */
+ _getHash: function(objectId) {
+ return '#message' + objectId;
+ },
+
+ /**
+ * Updates the history to avoid old content when going back in the browser
+ * history.
+ *
+ * @param {string} hash location hash
+ * @protected
+ */
+ _updateHistory: function(hash) {
+ window.location.hash = hash;
+ },
+
+ /**
+ * Returns the unique editor id.
+ *
+ * @return {string} editor id
+ * @protected
+ */
+ _getEditorId: function() {
+ return this._options.editorPrefix + this._getObjectId(this._activeElement);
+ },
+
+ /**
+ * Returns the element's `data-object-id` value.
+ *
+ * @param {Element} element target element
+ * @return {int}
+ * @protected
+ */
+ _getObjectId: function(element) {
+ return ~~elData(element, 'object-id');
+ },
+
+ _ajaxFailure: function(data) {
+ var elementData = this._elements.get(this._activeElement);
+ var editor = elBySel('.redactor-layer', elementData.messageBodyEditor);
+
+ // handle errors occurring on editor load
+ if (editor === null) {
+ this._restoreMessage();
+
+ return true;
+ }
+
+ this._restoreEditor();
+
+ //noinspection JSUnresolvedVariable
+ if (!data || data.returnValues === undefined || data.returnValues.realErrorMessage === undefined) {
+ return true;
+ }
+
+ //noinspection JSUnresolvedVariable
+ elInnerError(editor, data.returnValues.realErrorMessage);
+
+ return false;
+ },
+
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'beginEdit':
+ this._showEditor(data);
+ break;
+
+ case 'save':
+ this._showMessage(data);
+ break;
+ }
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: this._options.className,
+ interfaceName: 'wcf\\data\\IMessageInlineEditorAction'
+ },
+ silent: true
+ };
+ },
+
+ /** @deprecated 3.0 - used only for backward compatibility with `WCF.Message.InlineEditor` */
+ legacyEdit: function(containerId) {
+ this._click(elById(containerId), null);
+ }
+ };
+
+ return UiMessageInlineEditor;
+});
+
+/**
+ * Provides access and editing of message properties.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Manager
+ */
+define('WoltLabSuite/Core/Ui/Message/Manager',['Ajax', 'Core', 'Dictionary', 'Language', 'Dom/ChangeListener', 'Dom/Util'], function(Ajax, Core, Dictionary, Language, DomChangeListener, DomUtil) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ getPermission: function() {},
+ getPropertyValue: function() {},
+ update: function() {},
+ updateItems: function() {},
+ updateAllItems: function() {},
+ setNote: function() {},
+ _update: function() {},
+ _updateState: function() {},
+ _toggleMessageStatus: function() {},
+ _getAttributeName: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @param {Object} options initialization options
+ * @constructor
+ */
+ function UiMessageManager(options) { this.init(options); }
+ UiMessageManager.prototype = {
+ /**
+ * Initializes a new manager instance.
+ *
+ * @param {Object} options initialization options
+ */
+ init: function(options) {
+ this._elements = null;
+ this._options = Core.extend({
+ className: '',
+ selector: ''
+ }, options);
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Message/Manager' + this._options.className, this.rebuild.bind(this));
+ },
+
+ /**
+ * Rebuilds the list of observed messages. You should call this method whenever a
+ * message has been either added or removed from the document.
+ */
+ rebuild: function() {
+ this._elements = new Dictionary();
+
+ var element, elements = elBySelAll(this._options.selector);
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+
+ this._elements.set(elData(element, 'object-id'), element);
+ }
+ },
+
+ /**
+ * Returns a boolean value for the given permission. The permission should not start
+ * with "can" or "can-" as this is automatically assumed by this method.
+ *
+ * @param {int} objectId message object id
+ * @param {string} permission permission name without a leading "can" or "can-"
+ * @return {boolean} true if permission was set and is either 'true' or '1'
+ */
+ getPermission: function(objectId, permission) {
+ permission = 'can-' + this._getAttributeName(permission);
+ var element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ }
+
+ return elDataBool(element, permission);
+ },
+
+ /**
+ * Returns the given property value from a message, optionally supporting a boolean return value.
+ *
+ * @param {int} objectId message object id
+ * @param {string} propertyName attribute name
+ * @param {boolean} asBool attempt to interpret property value as boolean
+ * @return {(boolean|string)} raw property value or boolean if requested
+ */
+ getPropertyValue: function(objectId, propertyName, asBool) {
+ var element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ }
+
+ return window[(asBool ? 'elDataBool' : 'elData')](element, this._getAttributeName(propertyName));
+ },
+
+ /**
+ * Invokes a method for given message object id in order to alter its state or properties.
+ *
+ * @param {int} objectId message object id
+ * @param {string} actionName action name used for the ajax api
+ * @param {Object=} parameters optional list of parameters included with the ajax request
+ */
+ update: function(objectId, actionName, parameters) {
+ Ajax.api(this, {
+ actionName: actionName,
+ parameters: parameters || {},
+ objectIDs: [objectId]
+ });
+ },
+
+ /**
+ * Updates properties and states for given object ids. Keep in mind that this method does
+ * not support setting individual properties per message, instead all property changes
+ * are applied to all matching message objects.
+ *
+ * @param {Array<int>} objectIds list of message object ids
+ * @param {Object} data list of updated properties
+ */
+ updateItems: function(objectIds, data) {
+ if (!Array.isArray(objectIds)) {
+ objectIds = [objectIds];
+ }
+
+ var element;
+ for (var i = 0, length = objectIds.length; i < length; i++) {
+ element = this._elements.get(objectIds[i]);
+ if (element === undefined) {
+ continue;
+ }
+
+ for (var key in data) {
+ if (data.hasOwnProperty(key)) {
+ this._update(element, key, data[key]);
+ }
+ }
+ }
+ },
+
+ /**
+ * Bulk updates the properties and states for all observed messages at once.
+ *
+ * @param {Object} data list of updated properties
+ */
+ updateAllItems: function(data) {
+ var objectIds = [];
+ this._elements.forEach((function(element, objectId) {
+ objectIds.push(objectId);
+ }).bind(this));
+
+ this.updateItems(objectIds, data);
+ },
+
+ /**
+ * Sets or removes a message note identified by its unique CSS class.
+ *
+ * @param {int} objectId message object id
+ * @param {string} className unique CSS class
+ * @param {string} htmlContent HTML content
+ */
+ setNote: function (objectId, className, htmlContent) {
+ var element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ }
+
+ var messageFooterNotes = elBySel('.messageFooterNotes', element);
+ var note = elBySel('.' + className, messageFooterNotes);
+ if (htmlContent) {
+ if (note === null) {
+ note = elCreate('p');
+ note.className = 'messageFooterNote ' + className;
+
+ messageFooterNotes.appendChild(note);
+ }
+
+ note.innerHTML = htmlContent;
+ }
+ else if (note !== null) {
+ elRemove(note);
+ }
+ },
+
+ /**
+ * Updates a single property of a message element.
+ *
+ * @param {Element} element message element
+ * @param {string} propertyName property name
+ * @param {?} propertyValue property value, will be implicitly converted to string
+ * @protected
+ */
+ _update: function(element, propertyName, propertyValue) {
+ elData(element, this._getAttributeName(propertyName), propertyValue);
+
+ // handle special properties
+ var propertyValueBoolean = (propertyValue == 1 || propertyValue === true || propertyValue === 'true');
+ this._updateState(element, propertyName, propertyValue, propertyValueBoolean);
+ },
+
+ /**
+ * Updates the message element's state based upon a property change.
+ *
+ * @param {Element} element message element
+ * @param {string} propertyName property name
+ * @param {?} propertyValue property value
+ * @param {boolean} propertyValueBoolean true if `propertyValue` equals either 'true' or '1'
+ * @protected
+ */
+ _updateState: function(element, propertyName, propertyValue, propertyValueBoolean) {
+ switch (propertyName) {
+ case 'isDeleted':
+ element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDeleted');
+ this._toggleMessageStatus(element, 'jsIconDeleted', 'wcf.message.status.deleted', 'red', propertyValueBoolean);
+
+ break;
+
+ case 'isDisabled':
+ element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDisabled');
+ this._toggleMessageStatus(element, 'jsIconDisabled', 'wcf.message.status.disabled', 'green', propertyValueBoolean);
+
+ break;
+ }
+ },
+
+ /**
+ * Toggles the message status bade for provided element.
+ *
+ * @param {Element} element message element
+ * @param {string} className badge class name
+ * @param {string} phrase language phrase
+ * @param {string} badgeColor color css class
+ * @param {boolean} addBadge add or remove badge
+ * @protected
+ */
+ _toggleMessageStatus: function(element, className, phrase, badgeColor, addBadge) {
+ var messageStatus = elBySel('.messageStatus', element);
+ if (messageStatus === null) {
+ var messageHeaderMetaData = elBySel('.messageHeaderMetaData', element);
+ if (messageHeaderMetaData === null) {
+ // can't find appropriate location to insert badge
+ return;
+ }
+
+ messageStatus = elCreate('ul');
+ messageStatus.className = 'messageStatus';
+ DomUtil.insertAfter(messageStatus, messageHeaderMetaData);
+ }
+
+ var badge = elBySel('.' + className, messageStatus);
+
+ if (addBadge) {
+ if (badge !== null) {
+ // badge already exists
+ return;
+ }
+
+ badge = elCreate('span');
+ badge.className = 'badge label ' + badgeColor + ' ' + className;
+ badge.textContent = Language.get(phrase);
+
+ var listItem = elCreate('li');
+ listItem.appendChild(badge);
+ messageStatus.appendChild(listItem);
+ }
+ else {
+ if (badge === null) {
+ // badge does not exist
+ return;
+ }
+
+ elRemove(badge.parentNode);
+ }
+ },
+
+ /**
+ * Transforms camel-cased property names into their attribute equivalent.
+ *
+ * @param {string} propertyName camel-cased property name
+ * @return {string} equivalent attribute name
+ * @protected
+ */
+ _getAttributeName: function(propertyName) {
+ if (propertyName.indexOf('-') !== -1) {
+ return propertyName;
+ }
+
+ var attributeName = '';
+ var str, tmp = propertyName.split(/([A-Z][a-z]+)/);
+ for (var i = 0, length = tmp.length; i < length; i++) {
+ str = tmp[i];
+ if (str.length) {
+ if (attributeName.length) attributeName += '-';
+ attributeName += str.toLowerCase();
+ }
+ }
+
+ return attributeName;
+ },
+
+ _ajaxSuccess: function() {
+ throw new Error("Method _ajaxSuccess() must be implemented by deriving functions.");
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: this._options.className
+ }
+ };
+ }
+ };
+
+ return UiMessageManager;
+});
+/**
+ * Handles user interaction with the quick reply feature.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Reply
+ */
+define('WoltLabSuite/Core/Ui/Message/Reply',['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Scroll', 'EventKey', 'User', 'WoltLabSuite/Core/Controller/Captcha'],
+ function(Ajax, Core, EventHandler, Language, DomChangeListener, DomUtil, DomTraverse, UiDialog, UiNotification, UiScroll, EventKey, User, ControllerCaptcha) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _submitGuestDialog: function() {},
+ _submit: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showLoadingOverlay: function() {},
+ _hideLoadingOverlay: function() {},
+ _reset: function() {},
+ _handleError: function() {},
+ _getEditor: function() {},
+ _insertMessage: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiMessageReply(options) { this.init(options); }
+ UiMessageReply.prototype = {
+ /**
+ * Initializes a new quick reply field.
+ *
+ * @param {Object} options configuration options
+ */
+ init: function(options) {
+ this._options = Core.extend({
+ ajax: {
+ className: ''
+ },
+ quoteManager: null,
+ successMessage: 'wcf.global.success.add'
+ }, options);
+
+ this._container = elById('messageQuickReply');
+ this._content = elBySel('.messageContent', this._container);
+ this._textarea = elById('text');
+ this._editor = null;
+ this._guestDialogId = '';
+ this._loadingOverlay = null;
+
+ // prevent marking of text for quoting
+ elBySel('.message', this._container).classList.add('jsInvalidQuoteTarget');
+
+ // handle submit button
+ var submitCallback = this._submit.bind(this);
+ var submitButton = elBySel('button[data-type="save"]', this._container);
+ submitButton.addEventListener(WCF_CLICK_EVENT, submitCallback);
+
+ // bind reply button
+ var replyButtons = elBySelAll('.jsQuickReply');
+ for (var i = 0, length = replyButtons.length; i < length; i++) {
+ replyButtons[i].addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+
+ this._getEditor().WoltLabReply.showEditor();
+
+ UiScroll.element(this._container, (function() {
+ this._getEditor().WoltLabCaret.endOfEditor();
+ }).bind(this));
+ }).bind(this));
+ }
+ },
+
+ /**
+ * Submits the guest dialog.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _submitGuestDialog: function(event) {
+ // only submit when enter key is pressed
+ if (event.type === 'keypress' && !EventKey.Enter(event)) {
+ return;
+ }
+
+ var usernameInput = elBySel('input[name=username]', event.currentTarget.closest('.dialogContent'));
+ if (usernameInput.value === '') {
+ elInnerError(usernameInput, Language.get('wcf.global.form.error.empty'));
+ usernameInput.closest('dl').classList.add('formError');
+
+ return;
+ }
+
+ var parameters = {
+ parameters: {
+ data: {
+ username: usernameInput.value
+ }
+ }
+ };
+
+ //noinspection JSCheckFunctionSignatures
+ var captchaId = elData(event.currentTarget, 'captcha-id');
+ if (ControllerCaptcha.has(captchaId)) {
+ var data = ControllerCaptcha.getData(captchaId);
+ if (data instanceof Promise) {
+ data.then((function (data) {
+ parameters = Core.extend(parameters, data);
+ this._submit(undefined, parameters);
+ }).bind(this));
+ }
+ else {
+ parameters = Core.extend(parameters, ControllerCaptcha.getData(captchaId));
+ this._submit(undefined, parameters);
+ }
+ }
+ else {
+ this._submit(undefined, parameters);
+ }
+ },
+
+ /**
+ * Validates the message and submits it to the server.
+ *
+ * @param {Event?} event event object
+ * @param {Object?} additionalParameters additional parameters sent to the server
+ * @protected
+ */
+ _submit: function(event, additionalParameters) {
+ if (event) {
+ event.preventDefault();
+ }
+
+ // Ignore requests to submit the message while a previous request is still pending.
+ if (this._content.classList.contains('loading')) {
+ if (!this._guestDialogId || !UiDialog.isOpen(this._guestDialogId)) {
+ return;
+ }
+ }
+
+ if (!this._validate()) {
+ // validation failed, bail out
+ return;
+ }
+
+ this._showLoadingOverlay();
+
+ // build parameters
+ var parameters = DomUtil.getDataAttributes(this._container, 'data-', true, true);
+ parameters.data = { message: this._getEditor().code.get() };
+ parameters.removeQuoteIDs = (this._options.quoteManager) ? this._options.quoteManager.getQuotesMarkedForRemoval() : [];
+
+ // add any available settings
+ var settingsContainer = elById('settings_text');
+ if (settingsContainer) {
+ elBySelAll('input, select, textarea', settingsContainer, function (element) {
+ if (element.nodeName === 'INPUT' && (element.type === 'checkbox' || element.type === 'radio')) {
+ if (!element.checked) {
+ return;
+ }
+ }
+
+ var name = element.name;
+ if (parameters.hasOwnProperty(name)) {
+ throw new Error("Variable overshadowing, key '" + name + "' is already present.");
+ }
+
+ parameters[name] = element.value.trim();
+ });
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_text', parameters.data);
+
+ if (!User.userId && !additionalParameters) {
+ parameters.requireGuestDialog = true;
+ }
+
+ Ajax.api(this, Core.extend({
+ parameters: parameters
+ }, additionalParameters));
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function() {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._container, elRemove);
+
+ // check if editor contains actual content
+ if (this._getEditor().utils.isEmpty()) {
+ this.throwError(this._textarea, Language.get('wcf.global.form.error.empty'));
+ return false;
+ }
+
+ var data = {
+ api: this,
+ editor: this._getEditor(),
+ message: this._getEditor().code.get(),
+ valid: true
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_text', data);
+
+ return (data.valid !== false);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, (message === 'empty' ? Language.get('wcf.global.form.error.empty') : message));
+ },
+
+ /**
+ * Displays a loading spinner while the request is processed by the server.
+ *
+ * @protected
+ */
+ _showLoadingOverlay: function() {
+ if (this._loadingOverlay === null) {
+ this._loadingOverlay = elCreate('div');
+ this._loadingOverlay.className = 'messageContentLoadingOverlay';
+ this._loadingOverlay.innerHTML = '<span class="icon icon96 fa-spinner"></span>';
+ }
+
+ this._content.classList.add('loading');
+ this._content.appendChild(this._loadingOverlay);
+ },
+
+ /**
+ * Hides the loading spinner.
+ *
+ * @protected
+ */
+ _hideLoadingOverlay: function() {
+ this._content.classList.remove('loading');
+
+ var loadingOverlay = elBySel('.messageContentLoadingOverlay', this._content);
+ if (loadingOverlay !== null) {
+ loadingOverlay.parentNode.removeChild(loadingOverlay);
+ }
+ },
+
+ /**
+ * Resets the editor contents and notifies event listeners.
+ *
+ * @protected
+ */
+ _reset: function() {
+ this._getEditor().code.set('<p>\u200b</p>');
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'reset_text');
+ },
+
+ /**
+ * Handles errors occurred during server processing.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _handleError: function(data) {
+ var parameters = {
+ api: this,
+ cancel: false,
+ returnValues: data.returnValues
+ };
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'handleError_text', parameters);
+
+ if (parameters.cancel !== true) {
+ //noinspection JSUnresolvedVariable
+ this.throwError(this._textarea, data.returnValues.realErrorMessage);
+ }
+ },
+
+ /**
+ * Returns the current editor instance.
+ *
+ * @return {Object} editor instance
+ * @protected
+ */
+ _getEditor: function() {
+ if (this._editor === null) {
+ if (typeof window.jQuery === 'function') {
+ this._editor = window.jQuery(this._textarea).data('redactor');
+ }
+ else {
+ throw new Error("Unable to access editor, jQuery has not been loaded yet.");
+ }
+ }
+
+ return this._editor;
+ },
+
+ /**
+ * Inserts the rendered message into the post list, unless the post is on the next
+ * page in which case a redirect will be performed instead.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _insertMessage: function(data) {
+ this._getEditor().WoltLabAutosave.reset();
+
+ // redirect to new page
+ //noinspection JSUnresolvedVariable
+ if (data.returnValues.url) {
+ //noinspection JSUnresolvedVariable
+ if (window.location == data.returnValues.url) {
+ window.location.reload();
+ }
+ window.location = data.returnValues.url;
+ }
+ else {
+ //noinspection JSUnresolvedVariable
+ if (data.returnValues.template) {
+ var elementId;
+
+ // insert HTML
+ if (elData(this._container, 'sort-order') === 'DESC') {
+ //noinspection JSUnresolvedVariable
+ DomUtil.insertHtml(data.returnValues.template, this._container, 'after');
+ elementId = DomUtil.identify(this._container.nextElementSibling);
+ }
+ else {
+ var insertBefore = this._container;
+ if (insertBefore.previousElementSibling && insertBefore.previousElementSibling.classList.contains('messageListPagination')) {
+ insertBefore = insertBefore.previousElementSibling;
+ }
+
+ //noinspection JSUnresolvedVariable
+ DomUtil.insertHtml(data.returnValues.template, insertBefore, 'before');
+ elementId = DomUtil.identify(insertBefore.previousElementSibling);
+ }
+
+ // update last post time
+ //noinspection JSUnresolvedVariable
+ elData(this._container, 'last-post-time', data.returnValues.lastPostTime);
+
+ window.history.replaceState(undefined, '', '#' + elementId);
+ UiScroll.element(elById(elementId));
+ }
+
+ UiNotification.show(Language.get(this._options.successMessage));
+
+ if (this._options.quoteManager) {
+ this._options.quoteManager.countQuotes();
+ }
+
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * @param {{returnValues:{guestDialog:string,guestDialogID:string}}} data
+ * @protected
+ */
+ _ajaxSuccess: function(data) {
+ if (!User.userId && !data.returnValues.guestDialogID) {
+ throw new Error("Missing 'guestDialogID' return value for guest.");
+ }
+
+ if (!User.userId && data.returnValues.guestDialog) {
+ UiDialog.openStatic(data.returnValues.guestDialogID, data.returnValues.guestDialog, {
+ closable: false,
+ onClose: function() {
+ if (ControllerCaptcha.has(data.returnValues.guestDialogID)) {
+ ControllerCaptcha.delete(data.returnValues.guestDialogID);
+ }
+ },
+ title: Language.get('wcf.global.confirmation.title')
+ });
+
+ var dialog = UiDialog.getDialog(data.returnValues.guestDialogID);
+ elBySel('input[type=submit]', dialog.content).addEventListener(WCF_CLICK_EVENT, this._submitGuestDialog.bind(this));
+ elBySel('input[type=text]', dialog.content).addEventListener('keypress', this._submitGuestDialog.bind(this));
+
+ this._guestDialogId = data.returnValues.guestDialogID;
+ }
+ else {
+ this._insertMessage(data);
+
+ if (!User.userId) {
+ UiDialog.close(data.returnValues.guestDialogID);
+ }
+
+ this._reset();
+
+ this._hideLoadingOverlay();
+ }
+ },
+
+ _ajaxFailure: function(data) {
+ this._hideLoadingOverlay();
+
+ //noinspection JSUnresolvedVariable
+ if (data === null || data.returnValues === undefined || data.returnValues.realErrorMessage === undefined) {
+ return true;
+ }
+
+ this._handleError(data);
+
+ return false;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'quickReply',
+ className: this._options.ajax.className,
+ interfaceName: 'wcf\\data\\IMessageQuickReplyAction'
+ },
+ silent: true
+ };
+ }
+ };
+
+ return UiMessageReply;
+});
+
+/**
+ * Provides buttons to share a page through multiple social community sites.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Share
+ */
+define('WoltLabSuite/Core/Ui/Message/Share',['EventHandler', 'StringUtil'], function(EventHandler, StringUtil) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Message/Share
+ */
+ return {
+ _pageDescription: '',
+ _pageUrl: '',
+
+ init: function() {
+ var title = elBySel('meta[property="og:title"]');
+ if (title !== null) this._pageDescription = encodeURIComponent(title.content);
+ var url = elBySel('meta[property="og:url"]');
+ if (url !== null) this._pageUrl = encodeURIComponent(url.content);
+
+ elBySelAll('.jsMessageShareButtons', null, (function(container) {
+ container.classList.remove('jsMessageShareButtons');
+
+ var pageUrl = encodeURIComponent(StringUtil.unescapeHTML(elData(container, 'url') || ''));
+ if (!pageUrl) {
+ pageUrl = this._pageUrl;
+ }
+
+ var providers = {
+ facebook: {
+ link: elBySel('.jsShareFacebook', container),
+ share: (function(event) { this._share('facebook', 'https://www.facebook.com/sharer.php?u={pageURL}&t={text}', true, pageUrl); }).bind(this)
+ },
+ google: {
+ link: elBySel('.jsShareGoogle', container),
+ share: (function(event) { this._share('google', 'https://plus.google.com/share?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ reddit: {
+ link: elBySel('.jsShareReddit', container),
+ share: (function(event) { this._share('reddit', 'https://ssl.reddit.com/submit?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ twitter: {
+ link: elBySel('.jsShareTwitter', container),
+ share: (function(event) { this._share('twitter', 'https://twitter.com/share?url={pageURL}&text={text}', false, pageUrl); }).bind(this)
+ },
+ linkedIn: {
+ link: elBySel('.jsShareLinkedIn', container),
+ share: (function(event) { this._share('linkedIn', 'https://www.linkedin.com/cws/share?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ pinterest: {
+ link: elBySel('.jsSharePinterest', container),
+ share: (function(event) { this._share('pinterest', 'https://www.pinterest.com/pin/create/link/?url={pageURL}&description={text}', false, pageUrl); }).bind(this)
+ },
+ xing: {
+ link: elBySel('.jsShareXing', container),
+ share: (function(event) { this._share('xing', 'https://www.xing.com/social_plugins/share?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ whatsApp: {
+ link: elBySel('.jsShareWhatsApp', container),
+ share: (function() {
+ window.location.href = 'whatsapp://send?text=' + this._pageDescription + '%20' + pageUrl;
+ }).bind(this)
+ }
+ };
+
+ EventHandler.fire('com.woltlab.wcf.message.share', 'shareProvider', {
+ container: container,
+ providers: providers,
+ pageDescription: this._pageDescription,
+ pageUrl: this._pageUrl
+ });
+
+ for (var provider in providers) {
+ if (providers.hasOwnProperty(provider)) {
+ if (providers[provider].link !== null) {
+ providers[provider].link.addEventListener(WCF_CLICK_EVENT, providers[provider].share);
+ }
+ }
+ }
+ }).bind(this));
+ },
+
+ _share: function(objectName, url, appendUrl, pageUrl) {
+ // fallback for plugins
+ if (!pageUrl) {
+ pageUrl = this._pageUrl;
+ }
+
+ window.open(
+ url.replace(/\{pageURL}/, pageUrl).replace(/\{text}/, this._pageDescription + (appendUrl ? "%20" + pageUrl : "")),
+ objectName,
+ 'height=600,width=600'
+ );
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Page/Search',['Ajax', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function(Ajax, EventKey, Language, StringUtil, DomUtil, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ open: function() {},
+ _search: function() {},
+ _click: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callbackSelect, _resultContainer, _resultList, _searchInput = null;
+
+ return {
+ open: function(callbackSelect) {
+ _callbackSelect = callbackSelect;
+
+ UiDialog.open(this);
+ },
+
+ _search: function (event) {
+ event.preventDefault();
+
+ var inputContainer = _searchInput.parentNode;
+
+ var value = _searchInput.value.trim();
+ if (value.length < 3) {
+ elInnerError(inputContainer, Language.get('wcf.page.search.error.tooShort'));
+ return;
+ }
+ else {
+ elInnerError(inputContainer, false);
+ }
+
+ Ajax.api(this, {
+ parameters: {
+ searchString: value
+ }
+ });
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ var page = event.currentTarget;
+ var pageTitle = elBySel('h3', page).textContent.replace(/['"]/g, '');
+
+ _callbackSelect(elData(page, 'page-id') + '#' + pageTitle);
+
+ UiDialog.close(this);
+ },
+
+ _ajaxSuccess: function(data) {
+ var html = '', page;
+ //noinspection JSUnresolvedVariable
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ page = data.returnValues[i];
+
+ html += '<li>'
+ + '<div class="containerHeadline pointer" data-page-id="' + page.pageID + '">'
+ + '<h3>' + StringUtil.escapeHTML(page.name) + '</h3>'
+ + '<small>' + StringUtil.escapeHTML(page.displayLink) + '</small>'
+ + '</div>'
+ + '</li>';
+ }
+
+ _resultList.innerHTML = html;
+
+ window[html ? 'elShow' : 'elHide'](_resultContainer);
+
+ if (html) {
+ elBySelAll('.containerHeadline', _resultList, (function(item) {
+ item.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }).bind(this));
+ }
+ else {
+ elInnerError(_searchInput.parentNode, Language.get('wcf.page.search.error.noResults'));
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ actionName: 'search',
+ className: 'wcf\\data\\page\\PageAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiPageSearch',
+ options: {
+ onSetup: (function() {
+ var callbackSearch = this._search.bind(this);
+
+ _searchInput = elById('wcfUiPageSearchInput');
+ _searchInput.addEventListener('keydown', function(event) {
+ if (EventKey.Enter(event)) {
+ callbackSearch(event);
+ }
+ });
+
+ _searchInput.nextElementSibling.addEventListener(WCF_CLICK_EVENT, callbackSearch);
+
+ _resultContainer = elById('wcfUiPageSearchResultContainer');
+ _resultList = elById('wcfUiPageSearchResultList');
+ }).bind(this),
+ onShow: function() {
+ _searchInput.focus();
+ },
+ title: Language.get('wcf.page.search')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiPageSearchInput">' + Language.get('wcf.page.search.name') + '</label></dt>'
+ + '<dd>'
+ + '<div class="inputAddon">'
+ + '<input type="text" id="wcfUiPageSearchInput" class="long">'
+ + '<a href="#" class="inputSuffix"><span class="icon icon16 fa-search"></span></a>'
+ + '</div>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<section id="wcfUiPageSearchResultContainer" class="section" style="display: none;">'
+ + '<header class="sectionHeader">'
+ + '<h2 class="sectionTitle">' + Language.get('wcf.page.search.results') + '</h2>'
+ + '</header>'
+ + '<ol id="wcfUiPageSearchResultList" class="containerList"></ol>'
+ + '</section>'
+ };
+ }
+ };
+});
+
+/**
+ * Sortable lists with optimized handling per device sizes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Sortable/List
+ */
+define('WoltLabSuite/Core/Ui/Sortable/List',['Core', 'Ui/Screen'], function (Core, UiScreen) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _enable: function() {},
+ _disable: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiSortableList(options) { this.init(options); }
+ UiSortableList.prototype = {
+ /**
+ * Initializes the sortable list controller.
+ *
+ * @param {Object} options initialization options for `WCF.Sortable.List`
+ */
+ init: function (options) {
+ this._options = Core.extend({
+ containerId: '',
+ className: '',
+ offset: 0,
+ options: {},
+ isSimpleSorting: false,
+ additionalParameters: {}
+ }, options);
+
+ UiScreen.on('screen-sm-md', {
+ match: this._enable.bind(this, true),
+ unmatch: this._disable.bind(this),
+ setup: this._enable.bind(this, true)
+ });
+
+ UiScreen.on('screen-lg', {
+ match: this._enable.bind(this, false),
+ unmatch: this._disable.bind(this),
+ setup: this._enable.bind(this, false)
+ });
+ },
+
+ /**
+ * Enables sorting with an optional sort handle.
+ *
+ * @param {boolean} hasHandle true if sort can only be started with the sort handle
+ * @protected
+ */
+ _enable: function (hasHandle) {
+ var options = this._options.options;
+ if (hasHandle) options.handle = '.sortableNodeHandle';
+
+ new window.WCF.Sortable.List(
+ this._options.containerId,
+ this._options.className,
+ this._options.offset,
+ options,
+ this._options.isSimpleSorting,
+ this._options.additionalParameters
+ );
+ },
+
+ /**
+ * Disables sorting for registered containers.
+ *
+ * @protected
+ */
+ _disable: function () {
+ window.jQuery('#' + this._options.containerId + ' .sortableList')[(this._options.isSimpleSorting ? 'sortable' : 'nestedSortable')]('destroy');
+ }
+ };
+
+ return UiSortableList;
+});
+/**
+ * Handles the data to create and edit a poll in a form created via form builder.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Poll/Editor
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/Poll/Editor',[
+ 'Core',
+ 'Dom/Util',
+ 'EventHandler',
+ 'EventKey',
+ 'Language',
+ 'WoltLabSuite/Core/Date/Picker',
+ 'WoltLabSuite/Core/Ui/Sortable/List'
+], function(
+ Core,
+ DomUtil,
+ EventHandler,
+ EventKey,
+ Language,
+ DatePicker,
+ UiSortableList
+) {
+ "use strict";
+
+ function UiPollEditor(containerId, pollOptions, wysiwygId, options) {
+ this.init(containerId, pollOptions, wysiwygId, options);
+ }
+ UiPollEditor.prototype = {
+ /**
+ * Initializes the poll editor.
+ *
+ * @param {string} containerId id of the poll options container
+ * @param {object[]} pollOptions existing poll options
+ * @param {string} wysiwygId id of the related wysiwyg editor
+ * @param {object} options additional poll options
+ */
+ init: function(containerId, pollOptions, wysiwygId, options) {
+ this._container = elById(containerId);
+ if (this._container === null) {
+ throw new Error("Unknown poll editor container with id '" + containerId + "'.");
+ }
+
+ this._wysiwygId = wysiwygId;
+ if (wysiwygId !== '' && elById(wysiwygId) === null) {
+ throw new Error("Unknown wysiwyg field with id '" + wysiwygId + "'.");
+ }
+
+ this.questionField = elById(this._wysiwygId + 'Poll_question');
+
+ var optionLists = elByClass('sortableList', this._container);
+ if (optionLists.length === 0) {
+ throw new Error("Cannot find poll options list for container with id '" + containerId + "'.");
+ }
+ this.optionList = optionLists[0];
+
+ this.endTimeField = elById(this._wysiwygId + 'Poll_endTime');
+ this.maxVotesField = elById(this._wysiwygId + 'Poll_maxVotes');
+ this.isChangeableYesField = elById(this._wysiwygId + 'Poll_isChangeable');
+ this.isChangeableNoField = elById(this._wysiwygId + 'Poll_isChangeable_no');
+ this.isPublicYesField = elById(this._wysiwygId + 'Poll_isPublic');
+ this.isPublicNoField = elById(this._wysiwygId + 'Poll_isPublic_no');
+ this.resultsRequireVoteYesField = elById(this._wysiwygId + 'Poll_resultsRequireVote');
+ this.resultsRequireVoteNoField = elById(this._wysiwygId + 'Poll_resultsRequireVote_no');
+ this.sortByVotesYesField = elById(this._wysiwygId + 'Poll_sortByVotes');
+ this.sortByVotesNoField = elById(this._wysiwygId + 'Poll_sortByVotes_no');
+
+ this._optionCount = 0;
+ this._options = Core.extend({
+ isAjax: false,
+ maxOptions: 20
+ }, options);
+
+ this._createOptionList(pollOptions || []);
+
+ new UiSortableList({
+ containerId: containerId,
+ options: {
+ toleranceElement: '> div'
+ }
+ });
+
+ if (this._options.isAjax) {
+ var events = ['handleError', 'reset', 'submit', 'validate'];
+ for (var i = 0, length = events.length; i < length; i++) {
+ var event = events[i];
+
+ EventHandler.add(
+ 'com.woltlab.wcf.redactor2',
+ event + '_' + this._wysiwygId,
+ this['_' + event].bind(this)
+ );
+ }
+ }
+ else {
+ var form = this._container.closest('form');
+ if (form === null) {
+ throw new Error("Cannot find form for container with id '" + containerId + "'.");
+ }
+
+ form.addEventListener('submit', this._submit.bind(this));
+ }
+ },
+
+ /**
+ * Adds an option based on below the option for which the `Add Option` button has
+ * been clicked.
+ *
+ * @param {Event} event icon click event
+ */
+ _addOption: function(event) {
+ event.preventDefault();
+
+ if (this._optionCount === this._options.maxOptions) {
+ return false;
+ }
+
+ this._createOption(
+ undefined,
+ undefined,
+ event.currentTarget.closest('li')
+ );
+ },
+
+ /**
+ * Creates a new option based on the given data or an empty option if no option data
+ * is given.
+ *
+ * @param {string} optionValue value of the option
+ * @param {integer} optionId id of the option
+ * @param {Element?} insertAfter optional element after which the new option is added
+ * @private
+ */
+ _createOption: function(optionValue, optionId, insertAfter) {
+ optionValue = optionValue || '';
+ optionId = ~~optionId || 0;
+
+ var listItem = elCreate('LI');
+ listItem.className = 'sortableNode';
+ elData(listItem, 'option-id', optionId);
+
+ if (insertAfter) {
+ DomUtil.insertAfter(listItem, insertAfter);
+ }
+ else {
+ this.optionList.appendChild(listItem);
+ }
+
+ var pollOptionInput = elCreate('div');
+ pollOptionInput.className = 'pollOptionInput';
+ listItem.appendChild(pollOptionInput);
+
+ var sortHandle = elCreate('span');
+ sortHandle.className = 'icon icon16 fa-arrows sortableNodeHandle';
+ pollOptionInput.appendChild(sortHandle);
+
+ // buttons
+ var addButton = elCreate('a');
+ elAttr(addButton, 'role', 'button');
+ elAttr(addButton, 'href', '#');
+ addButton.className = 'icon icon16 fa-plus jsTooltip jsAddOption pointer';
+ elAttr(addButton, 'title', Language.get('wcf.poll.button.addOption'));
+ addButton.addEventListener('click', this._addOption.bind(this));
+ pollOptionInput.appendChild(addButton);
+
+ var deleteButton = elCreate('a');
+ elAttr(deleteButton, 'role', 'button');
+ elAttr(deleteButton, 'href', '#');
+ deleteButton.className = 'icon icon16 fa-times jsTooltip jsDeleteOption pointer';
+ elAttr(deleteButton, 'title', Language.get('wcf.poll.button.removeOption'));
+ deleteButton.addEventListener('click', this._removeOption.bind(this));
+ pollOptionInput.appendChild(deleteButton);
+
+ // input field
+ var optionInput = elCreate('input');
+ elAttr(optionInput, 'type', 'text');
+ optionInput.value = optionValue;
+ elAttr(optionInput, 'maxlength', 255);
+ optionInput.addEventListener('keydown', this._optionInputKeyDown.bind(this));
+ optionInput.addEventListener('click', function() {
+ // work-around for some weird focus issue on iOS/Android
+ if (document.activeElement !== this) {
+ this.focus();
+ }
+ });
+ pollOptionInput.appendChild(optionInput);
+
+ if (insertAfter !== null) {
+ optionInput.focus();
+ }
+
+ this._optionCount++;
+ if (this._optionCount === this._options.maxOptions) {
+ elBySelAll('span.jsAddOption', this.optionList, function(icon) {
+ icon.classList.remove('pointer');
+ icon.classList.add('disabled');
+ });
+ }
+ },
+
+ /**
+ * Adds the given poll option to the option list.
+ *
+ * @param {object[]} pollOptions data of the added options
+ */
+ _createOptionList: function(pollOptions) {
+ for (var i = 0, length = pollOptions.length; i < length; i++) {
+ var option = pollOptions[i];
+ this._createOption(option.optionValue, option.optionID);
+ }
+
+ // add empty option field to add new options
+ if (this._optionCount < this._options.maxOptions) {
+ this._createOption();
+ }
+ },
+
+ /**
+ * Handles errors when the data is saved via AJAX.
+ *
+ * @param {object} data request response data
+ */
+ _handleError: function (data) {
+ switch (data.returnValues.fieldName) {
+ case this._wysiwygId + 'Poll_endTime':
+ case this._wysiwygId + 'Poll_maxVotes':
+ var fieldName = data.returnValues.fieldName.replace(this._wysiwygId + 'Poll_', '');
+
+ var small = elCreate('small');
+ small.className = 'innerError';
+ small.innerHTML = Language.get('wcf.poll.' + fieldName + '.error.' + data.returnValues.errorType);
+
+ var element = elById(data.returnValues.fieldName);
+ var errorParent = element.closest('dd');
+
+ DomUtil.prepend(small, element.nextSibling);
+
+ data.cancel = true;
+ break;
+ }
+ },
+
+ /**
+ * Adds an empty poll option after the current option when clicking enter.
+ *
+ * @param {Event} event key event
+ */
+ _optionInputKeyDown: function(event) {
+ // ignore every key except for [Enter]
+ if (!EventKey.Enter(event)) {
+ return;
+ }
+
+ Core.triggerEvent(elByClass('jsAddOption', event.currentTarget.parentNode)[0], 'click');
+
+ event.preventDefault();
+ },
+
+ /**
+ * Removes a poll option after clicking on the `Remove Option` button.
+ *
+ * @param {Event} event click event
+ */
+ _removeOption: function (event) {
+ event.preventDefault();
+
+ elRemove(event.currentTarget.closest('li'));
+
+ this._optionCount--;
+
+ elBySelAll('span.jsAddOption', this.optionList, function(icon) {
+ icon.classList.add('pointer');
+ icon.classList.remove('disabled');
+ });
+
+ if (this.optionList.length === 0) {
+ this._createOption();
+ }
+ },
+
+ /**
+ * Resets all poll-related form fields.
+ */
+ _reset: function() {
+ this.questionField.value = '';
+
+ this._optionCount = 0;
+ this.optionList.innerHtml = '';
+ this._createOption();
+
+ DatePicker.clear(this.endTimeField);
+
+ this.maxVotesField.value = 1;
+ this.isChangeableYesField.checked = false;
+ this.isChangeableNoField.checked = true;
+ this.isPublicYesField = false;
+ this.isPublicNoField = true;
+ this.resultsRequireVoteYesField = false;
+ this.resultsRequireVoteNoField = true;
+ this.sortByVotesYesField = false;
+ this.sortByVotesNoField = true;
+
+ EventHandler.fire(
+ 'com.woltlab.wcf.poll.editor',
+ 'reset',
+ {
+ pollEditor: this
+ }
+ );
+ },
+
+ /**
+ * Is called if the form is submitted or before the AJAX request is sent.
+ *
+ * @param {Event?} event form submit event
+ */
+ _submit: function(event) {
+ var options = [];
+ for (var i = 0, length = this.optionList.children.length; i < length; i++) {
+ var listItem = this.optionList.children[i];
+ var optionValue = elBySel('input[type=text]', listItem).value.trim();
+
+ if (optionValue !== '') {
+ options.push(elData(listItem, 'option-id') + '_' + optionValue);
+ }
+ }
+
+ if (this._options.isAjax) {
+ event.poll = {};
+
+ event.poll[this.questionField.id] = this.questionField.value;
+ event.poll[this._wysiwygId + 'Poll_options'] = options;
+ event.poll[this.endTimeField.id] = this.endTimeField.value;
+ event.poll[this.maxVotesField.id] = this.maxVotesField.value;
+ event.poll[this.isChangeableYesField.id] = !!this.isChangeableYesField.checked;
+ event.poll[this.isPublicYesField.id] = !!this.isPublicYesField.checked;
+ event.poll[this.resultsRequireVoteYesField.id] = !!this.resultsRequireVoteYesField.checked;
+ event.poll[this.sortByVotesYesField.id] = !!this.sortByVotesYesField.checked;
+
+ EventHandler.fire(
+ 'com.woltlab.wcf.poll.editor',
+ 'submit',
+ {
+ event: event,
+ pollEditor: this
+ }
+ );
+ }
+ else {
+ var form = this._container.closest('form');
+
+ for (var i = 0, length = options.length; i < length; i++) {
+ var input = elCreate('input');
+ elAttr(input, 'type', 'hidden');
+ elAttr(input, 'name', this._wysiwygId + 'Poll_options[' + i + ']');
+ input.value = options[i];
+ form.appendChild(input);
+ }
+ }
+ },
+
+ /**
+ * Is called to validate the poll data.
+ *
+ * @param {object} data event data
+ */
+ _validate: function(data) {
+ if (this.questionField.value.trim() === '') {
+ return;
+ }
+
+ var nonEmptyOptionCount = 0;
+ for (var i = 0, length = this.optionList.children.length; i < length; i++) {
+ var optionInput = elBySel('input[type=text]', this.optionList.children[i]);
+ if (optionInput.value.trim() !== '') {
+ nonEmptyOptionCount++;
+ }
+ }
+
+ if (nonEmptyOptionCount === 0) {
+ data.api.throwError(this._container, Language.get('wcf.global.form.error.empty'));
+ data.valid = false;
+ }
+ else {
+ var maxVotes = ~~this.maxVotesField.value;
+
+ if (maxVotes && maxVotes > nonEmptyOptionCount) {
+ data.api.throwError(this.maxVotesField.parentNode, Language.get('wcf.poll.maxVotes.error.invalid'));
+ data.valid = false;
+ }
+ else {
+ EventHandler.fire(
+ 'com.woltlab.wcf.poll.editor',
+ 'validate',
+ {
+ data: data,
+ pollEditor: this
+ }
+ );
+ }
+ }
+ }
+ };
+
+ return UiPollEditor;
+});
+
+/**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Article
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Article',['WoltLabSuite/Core/Ui/Article/Search'], function(UiArticleSearch) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _click: function() {},
+ _insert: function() {}
+ };
+ return Fake;
+ }
+
+ function UiRedactorArticle(editor, button) { this.init(editor, button); }
+ UiRedactorArticle.prototype = {
+ init: function (editor, button) {
+ this._editor = editor;
+
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ UiArticleSearch.open(this._insert.bind(this));
+ },
+
+ _insert: function (articleId) {
+ this._editor.buffer.set();
+
+ this._editor.insert.text("[wsa='" + articleId + "'][/wsa]");
+ }
+ };
+
+ return UiRedactorArticle;
+});
+
+/**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Metacode
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Metacode',['EventHandler', 'Dom/Util'], function(EventHandler, DomUtil) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ convert: function() {},
+ convertFromHtml: function() {},
+ _getOpeningTag: function() {},
+ _getClosingTag: function() {},
+ _getFirstParagraph: function() {},
+ _getLastParagraph: function() {},
+ _parseAttributes: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Redactor/Metacode
+ */
+ return {
+ /**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @param {Element} element textarea element
+ */
+ convert: function(element) {
+ element.textContent = this.convertFromHtml(element.textContent);
+ },
+
+ convertFromHtml: function (editorId, html) {
+ var div = elCreate('div');
+ div.innerHTML = html;
+
+ var attributes, data, metacode, metacodes = elByTag('woltlab-metacode', div), name, tagClose, tagOpen;
+ while (metacodes.length) {
+ metacode = metacodes[0];
+ name = elData(metacode, 'name');
+ attributes = this._parseAttributes(elData(metacode, 'attributes'));
+
+ data = {
+ attributes: attributes,
+ cancel: false,
+ metacode: metacode
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'metacode_' + name + '_' + editorId, data);
+ if (data.cancel === true) {
+ continue;
+ }
+
+ tagOpen = this._getOpeningTag(name, attributes);
+ tagClose = this._getClosingTag(name);
+
+ if (metacode.parentNode === div) {
+ DomUtil.prepend(tagOpen, this._getFirstParagraph(metacode));
+ this._getLastParagraph(metacode).appendChild(tagClose);
+ }
+ else {
+ DomUtil.prepend(tagOpen, metacode);
+ metacode.appendChild(tagClose);
+ }
+
+ DomUtil.unwrapChildNodes(metacode);
+ }
+
+ // convert `<kbd>…</kbd>` to `[tt]…[/tt]`
+ var inlineCode, inlineCodes = elByTag('kbd', div);
+ while (inlineCodes.length) {
+ inlineCode = inlineCodes[0];
+
+ inlineCode.insertBefore(document.createTextNode('[tt]'), inlineCode.firstChild);
+ inlineCode.appendChild(document.createTextNode('[/tt]'));
+
+ DomUtil.unwrapChildNodes(inlineCode);
+ }
+
+ return div.innerHTML;
+ },
+
+ /**
+ * Returns a text node representing the opening bbcode tag.
+ *
+ * @param {string} name bbcode tag
+ * @param {Array} attributes list of attributes
+ * @returns {Text} text node containing the opening bbcode tag
+ * @protected
+ */
+ _getOpeningTag: function(name, attributes) {
+ var buffer = '[' + name;
+ if (attributes.length) {
+ buffer += '=';
+
+ for (var i = 0, length = attributes.length; i < length; i++) {
+ if (i > 0) buffer += ",";
+ buffer += "'" + attributes[i] + "'";
+ }
+ }
+
+ return document.createTextNode(buffer + ']');
+ },
+
+ /**
+ * Returns a text node representing the closing bbcode tag.
+ *
+ * @param {string} name bbcode tag
+ * @returns {Text} text node containing the closing bbcode tag
+ * @protected
+ */
+ _getClosingTag: function(name) {
+ return document.createTextNode('[/' + name + ']');
+ },
+
+ /**
+ * Returns the first paragraph of provided element. If there are no children or
+ * the first child is not a paragraph, a new paragraph is created and inserted
+ * as first child.
+ *
+ * @param {Element} element metacode element
+ * @returns {Element} paragraph that is the first child of provided element
+ * @protected
+ */
+ _getFirstParagraph: function (element) {
+ var firstChild, paragraph;
+
+ if (element.childElementCount === 0) {
+ paragraph = elCreate('p');
+ element.appendChild(paragraph);
+ }
+ else {
+ firstChild = element.children[0];
+
+ if (firstChild.nodeName === 'P') {
+ paragraph = firstChild;
+ }
+ else {
+ paragraph = elCreate('p');
+ element.insertBefore(paragraph, firstChild);
+ }
+ }
+
+ return paragraph;
+ },
+
+ /**
+ * Returns the last paragraph of provided element. If there are no children or
+ * the last child is not a paragraph, a new paragraph is created and inserted
+ * as last child.
+ *
+ * @param {Element} element metacode element
+ * @returns {Element} paragraph that is the last child of provided element
+ * @protected
+ */
+ _getLastParagraph: function (element) {
+ var count = element.childElementCount, lastChild, paragraph;
+
+ if (count === 0) {
+ paragraph = elCreate('p');
+ element.appendChild(paragraph);
+ }
+ else {
+ lastChild = element.children[count - 1];
+
+ if (lastChild.nodeName === 'P') {
+ paragraph = lastChild;
+ }
+ else {
+ paragraph = elCreate('p');
+ element.appendChild(paragraph);
+ }
+ }
+
+ return paragraph;
+ },
+
+ /**
+ * Parses the attributes string.
+ *
+ * @param {string} attributes base64- and JSON-encoded attributes
+ * @return {Array} list of parsed attributes
+ * @protected
+ */
+ _parseAttributes: function(attributes) {
+ try {
+ attributes = JSON.parse(atob(attributes));
+ }
+ catch (e) { /* invalid base64 data or invalid json */ }
+
+ if (!Array.isArray(attributes)) {
+ return [];
+ }
+
+ var attribute, parsedAttributes = [];
+ for (var i = 0, length = attributes.length; i < length; i++) {
+ attribute = attributes[i];
+
+ if (typeof attribute === 'string') {
+ attribute = attribute.replace(/^'(.*)'$/, '$1');
+ }
+
+ parsedAttributes.push(attribute);
+ }
+
+ return parsedAttributes;
+ }
+ };
+});
+
+/**
+ * Manages the autosave process storing the current editor message in the local
+ * storage to recover it on browser crash or accidental navigation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Autosave
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Autosave',['Core', 'Devtools', 'EventHandler', 'Language', 'Dom/Traverse', './Metacode'], function(Core, Devtools, EventHandler, Language, DomTraverse, UiRedactorMetacode) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ getInitialValue: function() {},
+ getMetaData: function () {},
+ watch: function() {},
+ destroy: function() {},
+ clear: function() {},
+ createOverlay: function() {},
+ hideOverlay: function() {},
+ _saveToStorage: function() {},
+ _cleanup: function() {}
+ };
+ return Fake;
+ }
+
+ // time between save requests in seconds
+ var _frequency = 15;
+
+ /**
+ * @param {Element} element textarea element
+ * @constructor
+ */
+ function UiRedactorAutosave(element) { this.init(element); }
+ UiRedactorAutosave.prototype = {
+ /**
+ * Initializes the autosave handler and removes outdated messages from storage.
+ *
+ * @param {Element} element textarea element
+ */
+ init: function (element) {
+ this._container = null;
+ this._metaData = {};
+ this._editor = null;
+ this._element = element;
+ this._isActive = true;
+ this._isPending = false;
+ this._key = Core.getStoragePrefix() + elData(this._element, 'autosave');
+ this._lastMessage = '';
+ this._originalMessage = '';
+ this._overlay = null;
+ this._restored = false;
+ this._timer = null;
+
+ this._cleanup();
+
+ // remove attribute to prevent Redactor's built-in autosave to kick in
+ this._element.removeAttribute('data-autosave');
+
+ var form = DomTraverse.parentByTag(this._element, 'FORM');
+ if (form !== null) {
+ form.addEventListener('submit', this.destroy.bind(this));
+ }
+
+ // export meta data
+ EventHandler.add('com.woltlab.wcf.redactor2', 'getMetaData_' + this._element.id, (function (data) {
+ for (var key in this._metaData) {
+ if (this._metaData.hasOwnProperty(key)) {
+ data[key] = this._metaData[key];
+ }
+ }
+ }).bind(this));
+
+ // clear editor content on reset
+ EventHandler.add('com.woltlab.wcf.redactor2', 'reset_' + this._element.id, this.hideOverlay.bind(this));
+
+ document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
+ },
+
+ _onVisibilityChange: function () {
+ if (document.hidden) {
+ this._isActive = false;
+ this._isPending = true;
+ }
+ else {
+ this._isActive = true;
+ this._isPending = false;
+ }
+ },
+
+ /**
+ * Returns the initial value for the textarea, used to inject message
+ * from storage into the editor before initialization.
+ *
+ * @return {string} message content
+ */
+ getInitialValue: function() {
+ //noinspection JSUnresolvedVariable
+ if (window.ENABLE_DEVELOPER_TOOLS && Devtools._internal_.editorAutosave() === false) {
+ //noinspection JSUnresolvedVariable
+ return this._element.value;
+ }
+
+ var value = '';
+ try {
+ value = window.localStorage.getItem(this._key);
+ }
+ catch (e) {
+ window.console.warn("Unable to access local storage: " + e.message);
+ }
+
+ try {
+ value = JSON.parse(value);
+ }
+ catch (e) {
+ value = '';
+ }
+
+ // Check if the storage is outdated.
+ if (value !== null && typeof value === 'object' && value.content) {
+ var lastEditTime = ~~elData(this._element, 'autosave-last-edit-time');
+ if (lastEditTime * 1000 <= value.timestamp) {
+ // Compare the stored version with the editor content, but only use the `innerText` property
+ // in order to ignore differences in whitespace, e. g. caused by indentation of HTML tags.
+ var div1 = elCreate('div');
+ div1.innerHTML = this._element.value;
+ var div2 = elCreate('div');
+ div2.innerHTML = value.content;
+
+ if (div1.innerText.trim() !== div2.innerText.trim()) {
+ //noinspection JSUnresolvedVariable
+ this._originalMessage = this._element.value;
+ this._restored = true;
+
+ this._metaData = value.meta || {};
+
+ return value.content;
+ }
+ }
+ }
+
+ //noinspection JSUnresolvedVariable
+ return this._element.value;
+ },
+
+ /**
+ * Returns the stored meta data.
+ *
+ * @return {Object}
+ */
+ getMetaData: function () {
+ return this._metaData;
+ },
+
+ /**
+ * Enables periodical save of editor contents to local storage.
+ *
+ * @param {$.Redactor} editor redactor instance
+ */
+ watch: function(editor) {
+ this._editor = editor;
+
+ if (this._timer !== null) {
+ throw new Error("Autosave timer is already active.");
+ }
+
+ this._timer = window.setInterval(this._saveToStorage.bind(this), _frequency * 1000);
+
+ this._saveToStorage();
+
+ this._isPending = false;
+ },
+
+ /**
+ * Disables autosave handler, for use on editor destruction.
+ */
+ destroy: function () {
+ this.clear();
+
+ this._editor = null;
+
+ window.clearInterval(this._timer);
+ this._timer = null;
+ this._isPending = false;
+ },
+
+ /**
+ * Removed the stored message, for use after a message has been submitted.
+ */
+ clear: function () {
+ this._metaData = {};
+ this._lastMessage = '';
+
+ try {
+ window.localStorage.removeItem(this._key);
+ }
+ catch (e) {
+ window.console.warn("Unable to remove from local storage: " + e.message);
+ }
+ },
+
+ /**
+ * Creates the autosave controls, used to keep or discard the restored draft.
+ */
+ createOverlay: function () {
+ if (!this._restored) {
+ return;
+ }
+
+ var container = elCreate('div');
+ container.className = 'redactorAutosaveRestored active';
+
+ var title = elCreate('span');
+ title.textContent = Language.get('wcf.editor.autosave.restored');
+ container.appendChild(title);
+
+ var button = elCreate('a');
+ button.className = 'jsTooltip';
+ button.href = '#';
+ button.title = Language.get('wcf.editor.autosave.keep');
+ button.innerHTML = '<span class="icon icon16 fa-check green"></span>';
+ button.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ this.hideOverlay();
+ }).bind(this));
+ container.appendChild(button);
+
+ button = elCreate('a');
+ button.className = 'jsTooltip';
+ button.href = '#';
+ button.title = Language.get('wcf.editor.autosave.discard');
+ button.innerHTML = '<span class="icon icon16 fa-times red"></span>';
+ button.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ // remove from storage
+ this.clear();
+
+ // set code
+ var content = UiRedactorMetacode.convertFromHtml(this._editor.core.element()[0].id, this._originalMessage);
+ this._editor.code.start(content);
+
+ // set value
+ this._editor.core.textarea().val(this._editor.clean.onSync(this._editor.$editor.html()));
+
+ this.hideOverlay();
+ }).bind(this));
+ container.appendChild(button);
+
+ this._editor.core.box()[0].appendChild(container);
+
+ var callback = (function () {
+ this._editor.core.editor()[0].removeEventListener(WCF_CLICK_EVENT, callback);
+
+ this.hideOverlay();
+ }).bind(this);
+ this._editor.core.editor()[0].addEventListener(WCF_CLICK_EVENT, callback);
+
+ this._container = container;
+ },
+
+ /**
+ * Hides the autosave controls.
+ */
+ hideOverlay: function () {
+ if (this._container !== null) {
+ this._container.classList.remove('active');
+
+ window.setTimeout((function () {
+ if (this._container !== null) {
+ elRemove(this._container);
+ }
+
+ this._container = null;
+ this._originalMessage = '';
+ }).bind(this), 1000);
+ }
+ },
+
+ /**
+ * Saves the current message to storage unless there was no change.
+ *
+ * @protected
+ */
+ _saveToStorage: function() {
+ if (!this._isActive) {
+ if (!this._isPending) return;
+
+ // save one last time before suspending
+ this._isPending = false;
+ }
+
+ //noinspection JSUnresolvedVariable
+ if (window.ENABLE_DEVELOPER_TOOLS && Devtools._internal_.editorAutosave() === false) {
+ //noinspection JSUnresolvedVariable
+ return;
+ }
+
+ var content = this._editor.code.get();
+ if (this._editor.utils.isEmpty(content)) {
+ content = '';
+ }
+
+ if (this._lastMessage === content) {
+ // break if content hasn't changed
+ return;
+ }
+
+ if (content === '') {
+ return this.clear();
+ }
+
+ try {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveMetaData_' + this._element.id, this._metaData);
+
+ window.localStorage.setItem(this._key, JSON.stringify({
+ content: content,
+ meta: this._metaData,
+ timestamp: Date.now()
+ }));
+
+ this._lastMessage = content;
+ }
+ catch (e) {
+ window.console.warn("Unable to write to local storage: " + e.message);
+ }
+ },
+
+ /**
+ * Removes stored messages older than one week.
+ *
+ * @protected
+ */
+ _cleanup: function () {
+ var oneWeekAgo = Date.now() - (7 * 24 * 3600 * 1000), removeKeys = [];
+ var i, key, length, value;
+ for (i = 0, length = window.localStorage.length; i < length; i++) {
+ key = window.localStorage.key(i);
+
+ // check if key matches our prefix
+ if (key.indexOf(Core.getStoragePrefix()) !== 0) {
+ continue;
+ }
+
+ try {
+ value = window.localStorage.getItem(key);
+ }
+ catch (e) {
+ window.console.warn("Unable to access local storage: " + e.message);
+ }
+
+ try {
+ value = JSON.parse(value);
+ }
+ catch (e) {
+ value = { timestamp: 0 };
+ }
+
+ if (!value || value.timestamp < oneWeekAgo) {
+ removeKeys.push(key);
+ }
+ }
+
+ for (i = 0, length = removeKeys.length; i < length; i++) {
+ try {
+ window.localStorage.removeItem(removeKeys[i]);
+ }
+ catch (e) {
+ window.console.warn("Unable to remove from local storage: " + e.message);
+ }
+ }
+ }
+ };
+
+ return UiRedactorAutosave;
+});
+
+/**
+ * Helper class to deal with clickable block headers using the pseudo
+ * `::before` element.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/PseudoHeader
+ */
+define('WoltLabSuite/Core/Ui/Redactor/PseudoHeader',[], function() {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ getHeight: function() {}
+ };
+ return Fake;
+ }
+
+ return {
+ /**
+ * Returns the height within a click should be treated as a click
+ * within the block element's title. This method expects that the
+ * `::before` element is used and that removing the attribute
+ * `data-title` does cause the title to collapse.
+ *
+ * @param {Element} element block element
+ * @return {int} clickable height spanning from the top border down to the bottom of the title
+ */
+ getHeight: function (element) {
+ var height = ~~window.getComputedStyle(element).paddingTop.replace(/px$/, '');
+
+ var styles = window.getComputedStyle(element, '::before');
+ height += ~~styles.paddingTop.replace(/px$/, '');
+ height += ~~styles.paddingBottom.replace(/px$/, '');
+
+ var titleHeight = ~~styles.height.replace(/px$/, '');
+ if (titleHeight === 0) {
+ // firefox returns garbage for pseudo element height
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=925694
+
+ titleHeight = element.scrollHeight;
+ element.classList.add('redactorCalcHeight');
+ titleHeight -= element.scrollHeight;
+ element.classList.remove('redactorCalcHeight');
+ }
+
+ height += titleHeight;
+
+ return height;
+ }
+ }
+});
+
+/**
+ * Manages code blocks.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Code
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Code',['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './PseudoHeader', 'prism/prism-meta'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorPseudoHeader, PrismMeta) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _bbcodeCode: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {},
+ _dialogSubmit: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @constructor
+ */
+ function UiRedactorCode(editor) { this.init(editor); }
+ UiRedactorCode.prototype = {
+ /**
+ * Initializes the source code management.
+ *
+ * @param {Object} editor editor instance
+ */
+ init: function(editor) {
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+ this._pre = null;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'bbcode_code_' + this._elementId, this._bbcodeCode.bind(this));
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ // support for active button marking
+ this._editor.opts.activeButtonsStates.pre = 'code';
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+ },
+
+ /**
+ * Intercepts the insertion of `[code]` tags and uses a native `<pre>` instead.
+ *
+ * @param {Object} data event data
+ * @protected
+ */
+ _bbcodeCode: function(data) {
+ data.cancel = true;
+
+ var pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE' && pre.classList.contains('woltlabHtml')) {
+ return;
+ }
+
+ this._editor.button.toggle({}, 'pre', 'func', 'block.format');
+
+ pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE' && !pre.classList.contains('woltlabHtml')) {
+ if (pre.childElementCount === 1 && pre.children[0].nodeName === 'BR') {
+ // drop superfluous linebreak
+ pre.removeChild(pre.children[0]);
+ }
+
+ this._setTitle(pre);
+
+ pre.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(pre);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ elBySelAll('pre:not(.woltlabHtml)', this._editor.$editor[0], (function(pre) {
+ pre.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(pre);
+ }).bind(this));
+ },
+
+ /**
+ * Opens the dialog overlay to edit the code's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var pre = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(pre);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(pre);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._pre = pre;
+
+ UiDialog.open(this);
+ }
+ },
+
+ /**
+ * Saves the changes to the code's properties.
+ *
+ * @protected
+ */
+ _dialogSubmit: function() {
+ var id = 'redactor-code-' + this._elementId;
+
+ ['file', 'highlighter', 'line'].forEach((function (attr) {
+ elData(this._pre, attr, elById(id + '-' + attr).value);
+ }).bind(this));
+
+ this._setTitle(this._pre);
+ this._editor.caret.after(this._pre);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * Sets or updates the code's header title.
+ *
+ * @param {Element} pre code element
+ * @protected
+ */
+ _setTitle: function(pre) {
+ var file = elData(pre, 'file'),
+ highlighter = elData(pre, 'highlighter');
+
+ //noinspection JSUnresolvedVariable
+ highlighter = (this._editor.opts.woltlab.highlighters.hasOwnProperty(highlighter)) ? this._editor.opts.woltlab.highlighters[highlighter] : '';
+
+ var title = Language.get('wcf.editor.code.title', {
+ file: file,
+ highlighter: highlighter
+ });
+
+ if (elData(pre, 'title') !== title) {
+ elData(pre, 'title', title);
+ }
+ },
+
+ _delete: function (event) {
+ event.preventDefault();
+
+ var caretEnd = this._pre.nextElementSibling || this._pre.previousElementSibling;
+ if (caretEnd === null && this._pre.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._pre.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._pre);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var id = 'redactor-code-' + this._elementId,
+ idButtonDelete = id + '-button-delete',
+ idButtonSave = id + '-button-save',
+ idFile = id + '-file',
+ idHighlighter = id + '-highlighter',
+ idLine = id + '-line';
+
+ return {
+ id: id,
+ options: {
+ onClose: (function () {
+ this._editor.selection.restore();
+
+ UiDialog.destroy(this);
+ }).bind(this),
+
+ onSetup: (function() {
+ elById(idButtonDelete).addEventListener(WCF_CLICK_EVENT, this._delete.bind(this));
+
+ // set highlighters
+ var highlighters = '<option value="">' + Language.get('wcf.editor.code.highlighter.detect') + '</option>';
+ highlighters += '<option value="plain">' + Language.get('wcf.editor.code.highlighter.plain') + '</option>';
+
+ //noinspection JSUnresolvedVariable
+ var values = this._editor.opts.woltlab.highlighters.map(function (highlighter) {
+ return [highlighter, PrismMeta[highlighter].title];
+ });
+
+ // sort by label
+ values.sort(function(a, b) {
+ if (a[1] < b[1]) {
+ return -1;
+ }
+ else if (a[1] > b[1]) {
+ return 1;
+ }
+
+ return 0;
+ });
+
+ values.forEach((function(value) {
+ highlighters += '<option value="' + value[0] + '">' + StringUtil.escapeHTML(value[1]) + '</option>';
+ }).bind(this));
+
+ elById(idHighlighter).innerHTML = highlighters;
+ }).bind(this),
+
+ onShow: (function() {
+ elById(idHighlighter).value = elData(this._pre, 'highlighter');
+ var line = elData(this._pre, 'line');
+ elById(idLine).value = (line === '') ? 1 : ~~line;
+ elById(idFile).value = elData(this._pre, 'file');
+ }).bind(this),
+
+ title: Language.get('wcf.editor.code.edit')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="' + idHighlighter + '">' + Language.get('wcf.editor.code.highlighter') + '</label></dt>'
+ + '<dd>'
+ + '<select id="' + idHighlighter + '"></select>'
+ + '<small>' + Language.get('wcf.editor.code.highlighter.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="' + idLine + '">' + Language.get('wcf.editor.code.line') + '</label></dt>'
+ + '<dd>'
+ + '<input type="number" id="' + idLine + '" min="0" value="1" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.code.line.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="' + idFile + '">' + Language.get('wcf.editor.code.file') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idFile + '" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.code.file.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button id="' + idButtonSave + '" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.save') + '</button>'
+ + '<button id="' + idButtonDelete + '">' + Language.get('wcf.global.button.delete') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+
+ return UiRedactorCode;
+});
+
+/**
+ * Provides helper methods to add and remove format elements. These methods should in
+ * theory work with non-editor elements but has not been tested and any usage outside
+ * the editor is not recommended.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Format
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Format',['Dom/Util'], function(DomUtil) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ format: function() {},
+ removeFormat: function() {},
+ _handleParentNodes: function() {},
+ _getLastMatchingParent: function() {},
+ _isBoundaryElement: function() {},
+ _getSelectionMarker: function() {}
+ };
+ return Fake;
+ }
+
+ var _isValidSelection = function(editorElement) {
+ var element = window.getSelection().anchorNode;
+ while (element) {
+ if (element === editorElement) {
+ return true;
+ }
+
+ element = element.parentNode;
+ }
+
+ return false;
+ };
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Redactor/Format
+ */
+ return {
+ /**
+ * Applies format elements to the selected text.
+ *
+ * @param {Element} editorElement editor element
+ * @param {string} property CSS property name
+ * @param {string} value CSS property value
+ */
+ format: function(editorElement, property, value) {
+ var selection = window.getSelection();
+ if (!selection.rangeCount) {
+ // no active selection
+ return;
+ }
+
+ if (!_isValidSelection(editorElement)) {
+ console.error("Invalid selection, range exists outside of the editor:", selection.anchorNode);
+ return;
+ }
+
+ var range = selection.getRangeAt(0);
+ var markerStart = null, markerEnd = null, tmpElement = null;
+ if (range.collapsed) {
+ tmpElement = elCreate('strike');
+ tmpElement.textContent = '\u200B';
+ range.insertNode(tmpElement);
+
+ range = document.createRange();
+ range.selectNodeContents(tmpElement);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ else {
+ // removing existing format causes the selection to vanish,
+ // these markers are used to restore it afterwards
+ markerStart = elCreate('mark');
+ markerEnd = elCreate('mark');
+
+ var tmpRange = range.cloneRange();
+ tmpRange.collapse(true);
+ tmpRange.insertNode(markerStart);
+
+ tmpRange = range.cloneRange();
+ tmpRange.collapse(false);
+ tmpRange.insertNode(markerEnd);
+
+ range = document.createRange();
+ range.setStartAfter(markerStart);
+ range.setEndBefore(markerEnd);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ // remove existing format before applying new one
+ this.removeFormat(editorElement, property);
+
+ range = document.createRange();
+ range.setStartAfter(markerStart);
+ range.setEndBefore(markerEnd);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ var selectionMarker = ['strike', 'strikethrough'];
+ if (tmpElement === null) {
+ selectionMarker = this._getSelectionMarker(editorElement, selection);
+
+ document.execCommand(selectionMarker[1]);
+ }
+
+ var elements = elBySelAll(selectionMarker[0], editorElement), formatElement, selectElements = [], strike;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ strike = elements[i];
+
+ formatElement = elCreate('span');
+ // we're bypassing `style.setPropertyValue()` on purpose here,
+ // as it prevents browsers from mangling the value
+ elAttr(formatElement, 'style', property + ': ' + value);
+
+ DomUtil.replaceElement(strike, formatElement);
+ selectElements.push(formatElement);
+ }
+
+ var count = selectElements.length;
+ if (count) {
+ var firstSelectedElement = selectElements[0];
+ var lastSelectedElement = selectElements[count - 1];
+
+ // check if parent is of the same format
+ // and contains only the selected nodes
+ if (tmpElement === null && (firstSelectedElement.parentNode === lastSelectedElement.parentNode)) {
+ var parent = firstSelectedElement.parentNode;
+ if (parent.nodeName === 'SPAN' && parent.style.getPropertyValue(property) !== '') {
+ if (this._isBoundaryElement(firstSelectedElement, parent, 'previous') && this._isBoundaryElement(lastSelectedElement, parent, 'next')) {
+ DomUtil.unwrapChildNodes(parent);
+ }
+ }
+ }
+
+ range = document.createRange();
+ range.setStart(firstSelectedElement, 0);
+ range.setEnd(lastSelectedElement, lastSelectedElement.childNodes.length);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ if (markerStart !== null) {
+ elRemove(markerStart);
+ elRemove(markerEnd);
+ }
+ },
+
+ /**
+ * Removes a format element from the current selection.
+ *
+ * The removal uses a few techniques to remove the target element(s) without harming
+ * nesting nor any other formatting present. The steps taken are described below:
+ *
+ * 1. The browser will wrap all parts of the selection into <strike> tags
+ *
+ * This isn't the most efficient way to isolate each selected node, but is the
+ * most reliable way to accomplish this because the browser will insert them
+ * exactly where the range spans without harming the node nesting.
+ *
+ * Basically it is a trade-off between efficiency and reliability, the performance
+ * is still excellent but could be better at the expense of an increased complexity,
+ * which simply doesn't exactly pay off.
+ *
+ * 2. Iterate over each inserted <strike> and isolate all relevant ancestors
+ *
+ * Format tags can appear both as a child of the <strike> as well as once or multiple
+ * times as an ancestor.
+ *
+ * It uses ranges to select the contents before the <strike> element up to the start
+ * of the last matching ancestor and cuts out the nodes. The browser will ensure that
+ * the resulting fragment will include all relevant ancestors that were present before.
+ *
+ * The example below will use the fictional <bar> elements as the tag to remove, the
+ * pipe ("|") is used to denote the outer node boundaries.
+ *
+ * Before:
+ * |<bar>This is <foo>a <strike>simple <bar>example</bar></strike></foo></bar>|
+ * After:
+ * |<bar>This is <foo>a </foo></bar>|<bar><foo>simple <bar>example</bar></strike></foo></bar>|
+ *
+ * As a result we can now remove <bar> both inside the <strike> element as well as
+ * the outer <bar> without harming the effect of <bar> for the preceding siblings.
+ *
+ * This process is repeated for siblings appearing after the <strike> element too, it
+ * works as described above but flipped. This is an expensive operation and will only
+ * take place if there are any matching ancestors that need to be considered.
+ *
+ * Inspired by http://stackoverflow.com/a/12899461
+ *
+ * 3. Remove all matching ancestors, child elements and last the <strike> element itself
+ *
+ * Depending on the amount of nested matching nodes, this process will move a lot of
+ * nodes around. Removing the <bar> element will require all its child nodes to be moved
+ * in front of <bar>, they will actually become a sibling of <bar>. Afterwards the
+ * (now empty) <bar> element can be safely removed without losing any nodes.
+ *
+ *
+ * One last hint: This method will not check if the selection at some point contains at
+ * least one target element, it assumes that the user will not take any action that invokes
+ * this method for no reason (unless they want to waste CPU cycles, in that case they're
+ * welcome).
+ *
+ * This is especially important for developers as this method shouldn't be called for
+ * no good reason. Even though it is super fast, it still comes with expensive DOM operations
+ * and especially low-end devices (such as cheap smartphones) might not exactly like executing
+ * this method on large documents.
+ *
+ * If you fell the need to invoke this method anyway, go ahead. I'm a comment, not a cop.
+ *
+ * @param {Element} editorElement editor element
+ * @param {string} property CSS property that should be removed
+ */
+ removeFormat: function(editorElement, property) {
+ var selection = window.getSelection();
+ if (!selection.rangeCount) {
+ return;
+ }
+ else if (!_isValidSelection(editorElement)) {
+ console.error("Invalid selection, range exists outside of the editor:", selection.anchorNode);
+ return;
+ }
+
+ // Removing a span from an empty selection in an empty line containing a `<br>` causes a selection
+ // shift where the caret is moved into the span again. Unlike inline changes to the formatting, any
+ // removal of the format in an empty line should remove it from its entirely, instead of just around
+ // the caret position.
+ var range = selection.getRangeAt(0);
+ var helperTextNode = null;
+ if (range.collapsed) {
+ var container = range.startContainer;
+ var tree = [container];
+ while (true) {
+ var parent = container.parentNode;
+ if (parent === editorElement || parent.nodeName === 'TD') {
+ break;
+ }
+
+ container = parent;
+ tree.push(container);
+ }
+
+ if (this._isEmpty(container.innerHTML)) {
+ var marker = document.createElement('woltlab-format-marker');
+ range.insertNode(marker);
+
+ // Find the offending span and remove it entirely.
+ tree.forEach(function (element) {
+ if (element.nodeName === 'SPAN') {
+ if (element.style.getPropertyValue(property)) {
+ DomUtil.unwrapChildNodes(element);
+ }
+ }
+ });
+
+ // Firefox messes up the selection if the ancestor element was removed and there is
+ // an adjacent `<br>` present. Instead of keeping the caret in front of the <br>, it
+ // is implicitly moved behind it.
+ range = document.createRange();
+ range.selectNode(marker);
+ range.collapse(true);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ elRemove(marker);
+
+ return;
+ }
+
+ // Fill up the range with a zero length whitespace to give the browser
+ // something to strike through. If the range is completely empty, the
+ // "strike" is remembered by the browser, but not actually inserted into
+ // the DOM, causing the next keystroke to magically insert it.
+ helperTextNode = document.createTextNode('\u200B');
+ range.insertNode(helperTextNode);
+ }
+
+ var strikeElements = elByTag('strike', editorElement);
+
+ // remove any <strike> element first, all though there shouldn't be any at all
+ while (strikeElements.length) {
+ DomUtil.unwrapChildNodes(strikeElements[0]);
+ }
+
+ var selectionMarker = this._getSelectionMarker(editorElement, window.getSelection());
+
+ document.execCommand(selectionMarker[1]);
+ if (selectionMarker[0] !== 'strike') {
+ strikeElements = elByTag(selectionMarker[0], editorElement);
+ }
+
+ var lastMatchingParent, strikeElement;
+ while (strikeElements.length) {
+ strikeElement = strikeElements[0];
+ lastMatchingParent = this._getLastMatchingParent(strikeElement, editorElement, property);
+
+ if (lastMatchingParent !== null) {
+ this._handleParentNodes(strikeElement, lastMatchingParent, property);
+ }
+
+ // remove offending elements from child nodes
+ elBySelAll('span', strikeElement, function (span) {
+ if (span.style.getPropertyValue(property)) {
+ DomUtil.unwrapChildNodes(span);
+ }
+ });
+
+ // remove strike element itself
+ DomUtil.unwrapChildNodes(strikeElement);
+ }
+
+ // search for tags that are still floating around, but are completely empty
+ elBySelAll('span', editorElement, function (element) {
+ if (element.parentNode && !element.textContent.length && element.style.getPropertyValue(property) !== '') {
+ if (element.childElementCount === 1 && element.children[0].nodeName === 'MARK') {
+ element.parentNode.insertBefore(element.children[0], element);
+ }
+
+ if (element.childElementCount === 0) {
+ elRemove(element);
+ }
+ }
+ });
+
+ if (helperTextNode !== null) {
+ window.jQuery(editorElement).redactor('caret.after', range.parentNode);
+ elRemove(helperTextNode);
+ }
+ },
+
+ /**
+ * Slices relevant parent nodes and removes matching ancestors.
+ *
+ * @param {Element} strikeElement strike element representing the text selection
+ * @param {Element} lastMatchingParent last matching ancestor element
+ * @param {string} property CSS property that should be removed
+ * @protected
+ */
+ _handleParentNodes: function(strikeElement, lastMatchingParent, property) {
+ var range;
+
+ // selection does not begin at parent node start, slice all relevant parent
+ // nodes to ensure that selection is then at the beginning while preserving
+ // all proper ancestor elements
+ //
+ // before: (the pipe represents the node boundary)
+ // |otherContent <-- selection -->
+ // after:
+ // |otherContent| |<-- selection -->
+ if (!DomUtil.isAtNodeStart(strikeElement, lastMatchingParent)) {
+ range = document.createRange();
+ range.setStartBefore(lastMatchingParent);
+ range.setEndBefore(strikeElement);
+
+ var fragment = range.extractContents();
+ lastMatchingParent.parentNode.insertBefore(fragment, lastMatchingParent);
+ }
+
+ // selection does not end at parent node end, slice all relevant parent nodes
+ // to ensure that selection is then at the end while preserving all proper
+ // ancestor elements
+ //
+ // before: (the pipe represents the node boundary)
+ // <-- selection --> otherContent|
+ // after:
+ // <-- selection -->| |otherContent|
+ if (!DomUtil.isAtNodeEnd(strikeElement, lastMatchingParent)) {
+ range = document.createRange();
+ range.setStartAfter(strikeElement);
+ range.setEndAfter(lastMatchingParent);
+
+ fragment = range.extractContents();
+ lastMatchingParent.parentNode.insertBefore(fragment, lastMatchingParent.nextSibling);
+ }
+
+ // the strike element is now some kind of isolated, meaning we can now safely
+ // remove all offending parent nodes without influencing formatting of any content
+ // before or after the element
+ elBySelAll('span', lastMatchingParent, function (span) {
+ if (span.style.getPropertyValue(property)) {
+ DomUtil.unwrapChildNodes(span);
+ }
+ });
+
+ // finally remove the parent itself
+ DomUtil.unwrapChildNodes(lastMatchingParent);
+ },
+
+ /**
+ * Finds the last matching ancestor until it reaches the editor element.
+ *
+ * @param {Element} strikeElement strike element representing the text selection
+ * @param {Element} editorElement editor element
+ * @param {string} property CSS property that should be removed
+ * @returns {(Element|null)} last matching ancestor element or null if there is none
+ * @protected
+ */
+ _getLastMatchingParent: function(strikeElement, editorElement, property) {
+ var parent = strikeElement.parentNode, match = null;
+ while (parent !== editorElement) {
+ if (parent.nodeName === 'SPAN' && parent.style.getPropertyValue(property) !== '') {
+ match = parent;
+ }
+
+ parent = parent.parentNode;
+ }
+
+ return match;
+ },
+
+ /**
+ * Returns true if provided element is the first or last element
+ * of its parent, ignoring empty text nodes appearing between the
+ * element and the boundary.
+ *
+ * @param {Element} element target element
+ * @param {Element} parent parent element
+ * @param {string} type traversal direction, can be either `next` or `previous`
+ * @return {boolean} true if element is the non-empty boundary element
+ * @protected
+ */
+ _isBoundaryElement: function (element, parent, type) {
+ var node = element;
+ while (node = node[type + 'Sibling']) {
+ if (node.nodeType !== Node.TEXT_NODE || node.textContent.replace(/\u200B/, '') !== '') {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Returns a custom selection marker element, can be either `strike`, `sub` or `sup`. Using other kind
+ * of formattings is not possible due to the inconsistent behavior across browsers.
+ *
+ * @param {Element} editorElement editor element
+ * @param {Selection} selection selection object
+ * @return {string[]} tag name and command name
+ * @protected
+ */
+ _getSelectionMarker: function (editorElement, selection) {
+ var hasNode, node, tag, tags = ['DEL', 'SUB', 'SUP'];
+ for (var i = 0, length = tags.length; i < length; i++) {
+ tag = tags[i];
+
+ node = elClosest(selection.anchorNode);
+ hasNode = (elBySel(tag.toLowerCase(), node) !== null);
+
+ if (!hasNode) {
+ while (node && node !== editorElement) {
+ if (node.nodeName === tag) {
+ hasNode = true;
+ break;
+ }
+
+ node = node.parentNode;
+ }
+ }
+
+ if (hasNode) {
+ tag = undefined;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (tag === 'DEL' || tag === undefined) {
+ return ['strike', 'strikethrough'];
+ }
+
+ return [tag.toLowerCase(), tag.toLowerCase() + 'script'];
+ },
+
+ /**
+ * Slightly modified version of Redactor's `utils.isEmpty()`.
+ *
+ * @param {string} html
+ * @returns {boolean}
+ * @protected
+ */
+ _isEmpty: function(html) {
+ html = html.replace(/[\u200B-\u200D\uFEFF]/g, '');
+ html = html.replace(/ /gi, '');
+ html = html.replace(/<\/?br\s?\/?>/g, '');
+ html = html.replace(/\s/g, '');
+ html = html.replace(/^<p>[^\W\w\D\d]*?<\/p>$/i, '');
+ html = html.replace(/<iframe(.*?[^>])>$/i, 'iframe');
+ html = html.replace(/<source(.*?[^>])>$/i, 'source');
+
+ // remove empty tags
+ html = html.replace(/<[^\/>][^>]*><\/[^>]+>/gi, '');
+ html = html.replace(/<[^\/>][^>]*><\/[^>]+>/gi, '');
+
+ return html.trim() === '';
+ }
+ };
+});
+
+/**
+ * Manages html code blocks.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Html
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Html',['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './PseudoHeader'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorPseudoHeader) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _bbcodeCode: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _save: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @constructor
+ */
+ function UiRedactorHtml(editor) { this.init(editor); }
+ UiRedactorHtml.prototype = {
+ /**
+ * Initializes the source code management.
+ *
+ * @param {Object} editor editor instance
+ */
+ init: function(editor) {
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+ this._pre = null;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'bbcode_woltlabHtml_' + this._elementId, this._bbcodeCode.bind(this));
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ // support for active button marking
+ this._editor.opts.activeButtonsStates['woltlab-html'] = 'woltlabHtml';
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+ },
+
+ /**
+ * Intercepts the insertion of `[woltlabHtml]` tags and uses a native `<pre>` instead.
+ *
+ * @param {Object} data event data
+ * @protected
+ */
+ _bbcodeCode: function(data) {
+ data.cancel = true;
+
+ var pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE' && !pre.classList.contains('woltlabHtml')) {
+ return;
+ }
+
+ this._editor.button.toggle({}, 'pre', 'func', 'block.format');
+
+ pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE') {
+ pre.classList.add('woltlabHtml');
+
+ if (pre.childElementCount === 1 && pre.children[0].nodeName === 'BR') {
+ // drop superfluous linebreak
+ pre.removeChild(pre.children[0]);
+ }
+
+ this._setTitle(pre);
+
+ pre.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(pre);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ elBySelAll('pre.woltlabHtml', this._editor.$editor[0], (function(pre) {
+ pre.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(pre);
+ }).bind(this));
+ },
+
+ /**
+ * Opens the dialog overlay to edit the code's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var pre = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(pre);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(pre);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._pre = pre;
+
+ console.warn("should edit");
+ }
+ },
+
+ /**
+ * Sets or updates the code's header title.
+ *
+ * @param {Element} pre code element
+ * @protected
+ */
+ _setTitle: function(pre) {
+ ['title', 'description'].forEach(function(title) {
+ var phrase = Language.get('wcf.editor.html.' + title);
+
+ if (elData(pre, title) !== phrase) {
+ elData(pre, title, phrase);
+ }
+ });
+ },
+
+ _delete: function (event) {
+ console.warn("should delete");
+ event.preventDefault();
+
+ var caretEnd = this._pre.nextElementSibling || this._pre.previousElementSibling;
+ if (caretEnd === null && this._pre.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._pre.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._pre);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ }
+ };
+
+ return UiRedactorHtml;
+});
+define('WoltLabSuite/Core/Ui/Redactor/Link',['Core', 'EventKey', 'Language', 'Ui/Dialog'], function(Core, EventKey, Language, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ showDialog: function() {},
+ _submit: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _boundListener = false;
+ var _callback = null;
+
+ return {
+ showDialog: function(options) {
+ UiDialog.open(this);
+
+ UiDialog.setTitle(this, Language.get('wcf.editor.link.' + (options.insert ? 'add' : 'edit')));
+
+ var submitButton = elById('redactor-modal-button-action');
+ submitButton.textContent = Language.get('wcf.global.button.' + (options.insert ? 'insert' : 'save'));
+
+ _callback = options.submitCallback;
+
+ if (!_boundListener) {
+ _boundListener = true;
+
+ submitButton.addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ }
+ },
+
+ _submit: function() {
+ if (_callback()) {
+ UiDialog.close(this);
+ }
+ else {
+ var url = elById('redactor-link-url');
+ elInnerError(url, Language.get((url.value.trim() === '' ? 'wcf.global.form.error.empty' : 'wcf.editor.link.error.invalid')));
+ }
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'redactorDialogLink',
+ options: {
+ onClose: function() {
+ var url = elById('redactor-link-url');
+ var small = (url.nextElementSibling && url.nextElementSibling.nodeName === 'SMALL') ? url.nextElementSibling : null;
+ if (small !== null) {
+ elRemove(small);
+ }
+ },
+ onSetup: function (content) {
+ var submitButton = elBySel('.formSubmit > .buttonPrimary', content);
+
+ if (submitButton !== null) {
+ elBySelAll('input[type="url"], input[type="text"]', content, function (input) {
+ input.addEventListener('keyup', function (event) {
+ if (EventKey.Enter(event)) {
+ Core.triggerEvent(submitButton, 'click');
+ }
+ });
+ });
+ }
+ },
+ onShow: function () {
+ elById('redactor-link-url').focus();
+ }
+ },
+ source: '<dl>'
+ + '<dt><label for="redactor-link-url">' + Language.get('wcf.editor.link.url') + '</label></dt>'
+ + '<dd><input type="url" id="redactor-link-url" class="long"></dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="redactor-link-url-text">' + Language.get('wcf.editor.link.text') + '</label></dt>'
+ + '<dd><input type="text" id="redactor-link-url-text" class="long"></dd>'
+ + '</dl>'
+ + '<div class="formSubmit">'
+ + '<button id="redactor-modal-button-action" class="buttonPrimary"></button>'
+ + '</div>'
+ };
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Redactor/Mention',['Ajax', 'Environment', 'StringUtil', 'Ui/CloseOverlay'], function(Ajax, Environment, StringUtil, UiCloseOverlay) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _keyDown: function() {},
+ _keyUp: function() {},
+ _getTextLineInFrontOfCaret: function() {},
+ _getDropdownMenuPosition: function() {},
+ _setUsername: function() {},
+ _selectMention: function() {},
+ _updateDropdownPosition: function() {},
+ _selectItem: function() {},
+ _hideDropdown: function() {},
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {}
+ };
+ return Fake;
+ }
+
+ var _dropdownContainer = null;
+
+ function UiRedactorMention(redactor) { this.init(redactor); }
+ UiRedactorMention.prototype = {
+ init: function(redactor) {
+ this._active = false;
+ this._dropdownActive = false;
+ this._dropdownMenu = null;
+ this._itemIndex = 0;
+ this._lineHeight = null;
+ this._mentionStart = '';
+ this._redactor = redactor;
+ this._timer = null;
+
+ redactor.WoltLabEvent.register('keydown', this._keyDown.bind(this));
+ redactor.WoltLabEvent.register('keyup', this._keyUp.bind(this));
+
+ UiCloseOverlay.add('UiRedactorMention-' + redactor.core.element()[0].id, this._hideDropdown.bind(this));
+ },
+
+ _keyDown: function(data) {
+ if (!this._dropdownActive) {
+ return;
+ }
+
+ /** @var Event event */
+ var event = data.event;
+
+ switch (event.which) {
+ // enter
+ case 13:
+ this._setUsername(null, this._dropdownMenu.children[this._itemIndex].children[0]);
+ break;
+
+ // arrow up
+ case 38:
+ this._selectItem(-1);
+ break;
+
+ // arrow down
+ case 40:
+ this._selectItem(1);
+ break;
+
+ default:
+ this._hideDropdown();
+ return;
+ break;
+ }
+
+ event.preventDefault();
+ data.cancel = true;
+ },
+
+ _keyUp: function(data) {
+ /** @var Event event */
+ var event = data.event;
+
+ // ignore return key
+ if (event.which === 13) {
+ this._active = false;
+
+ return;
+ }
+
+ if (this._dropdownActive) {
+ data.cancel = true;
+
+ // ignore arrow up/down
+ if (event.which === 38 || event.which === 40) {
+ return;
+ }
+ }
+
+ var text = this._getTextLineInFrontOfCaret();
+ if (text.length > 0 && text.length < 25) {
+ var match = text.match(/@([^,]{3,})$/);
+ if (match) {
+ // if mentioning is at text begin or there's a whitespace character
+ // before the '@', everything is fine
+ if (!match.index || text[match.index - 1].match(/\s/)) {
+ this._mentionStart = match[1];
+
+ if (this._timer !== null) {
+ window.clearTimeout(this._timer);
+ this._timer = null;
+ }
+
+ this._timer = window.setTimeout((function() {
+ Ajax.api(this, {
+ parameters: {
+ data: {
+ searchString: this._mentionStart
+ }
+ }
+ });
+
+ this._timer = null;
+ }).bind(this), 500);
+ }
+ }
+ else {
+ this._hideDropdown();
+ }
+ }
+ else {
+ this._hideDropdown();
+ }
+ },
+
+ _getTextLineInFrontOfCaret: function() {
+ var data = this._selectMention(false);
+ if (data !== null) {
+ return data.range.cloneContents().textContent.replace(/\u200B/g, '').replace(/\u00A0/g, ' ').trim();
+ }
+
+ return '';
+ },
+
+ _getDropdownMenuPosition: function() {
+ var data = this._selectMention();
+ if (data === null) {
+ return null;
+ }
+
+ this._redactor.selection.save();
+
+ data.selection.removeAllRanges();
+ data.selection.addRange(data.range);
+
+ // get the offsets of the bounding box of current text selection
+ var rect = data.selection.getRangeAt(0).getBoundingClientRect();
+ var offsets = {
+ top: Math.round(rect.bottom) + (window.scrollY || window.pageYOffset),
+ left: Math.round(rect.left) + document.body.scrollLeft
+ };
+
+ if (this._lineHeight === null) {
+ this._lineHeight = Math.round(rect.bottom - rect.top);
+ }
+
+ // restore caret position
+ this._redactor.selection.restore();
+
+ return offsets;
+ },
+
+ _setUsername: function(event, item) {
+ if (event) {
+ event.preventDefault();
+ item = event.currentTarget;
+ }
+
+ var data = this._selectMention();
+ if (data === null) {
+ this._hideDropdown();
+
+ return;
+ }
+
+ // allow redactor to undo this
+ this._redactor.buffer.set();
+
+ data.selection.removeAllRanges();
+ data.selection.addRange(data.range);
+
+ var range = getSelection().getRangeAt(0);
+ range.deleteContents();
+ range.collapse(true);
+
+ var text = document.createTextNode('@' + elData(item, 'username') + '\u00A0');
+ range.insertNode(text);
+
+ range = document.createRange();
+ range.selectNode(text);
+ range.collapse(false);
+
+ data.selection.removeAllRanges();
+ data.selection.addRange(range);
+
+ this._hideDropdown();
+ },
+
+ _selectMention: function (skipCheck) {
+ var selection = window.getSelection();
+ if (!selection.rangeCount || !selection.isCollapsed) {
+ return null;
+ }
+
+ var container = selection.anchorNode;
+ if (container.nodeType === Node.TEXT_NODE) {
+ // work-around for Firefox after suggestions have been presented
+ container = container.parentNode;
+ }
+
+ // check if there is an '@' within the current range
+ if (container.textContent.indexOf('@') === -1) {
+ return null;
+ }
+
+ // check if we're inside code or quote blocks
+ var editor = this._redactor.core.editor()[0];
+ while (container && container !== editor) {
+ if (['PRE', 'WOLTLAB-QUOTE'].indexOf(container.nodeName) !== -1) {
+ return null;
+ }
+
+ container = container.parentNode;
+ }
+
+ var range = selection.getRangeAt(0);
+ var endContainer = range.startContainer;
+ var endOffset = range.startOffset;
+
+ // find the appropriate end location
+ while (endContainer.nodeType === Node.ELEMENT_NODE) {
+ if (endOffset === 0 && endContainer.childNodes.length === 0) {
+ // invalid start location
+ return null;
+ }
+
+ // startOffset for elements will always be after a node index
+ // or at the very start, which means if there is only text node
+ // and the caret is after it, startOffset will equal `1`
+ endContainer = endContainer.childNodes[(endOffset ? endOffset - 1 : 0)];
+ if (endOffset > 0) {
+ if (endContainer.nodeType === Node.TEXT_NODE) {
+ endOffset = endContainer.textContent.length;
+ }
+ else {
+ endOffset = endContainer.childNodes.length;
+ }
+ }
+ }
+
+ var startContainer = endContainer;
+ var startOffset = -1;
+ while (startContainer !== null) {
+ if (startContainer.nodeType !== Node.TEXT_NODE) {
+ return null;
+ }
+
+ if (startContainer.textContent.indexOf('@') !== -1) {
+ startOffset = startContainer.textContent.lastIndexOf('@');
+
+ break;
+ }
+
+ startContainer = startContainer.previousSibling;
+ }
+
+ if (startOffset === -1) {
+ // there was a non-text node that was in our way
+ return null;
+ }
+
+ try {
+ // mark the entire text, starting from the '@' to the current cursor position
+ range = document.createRange();
+ range.setStart(startContainer, startOffset);
+ range.setEnd(endContainer, endOffset);
+ }
+ catch (e) {
+ window.console.debug(e);
+ return null;
+ }
+
+ if (skipCheck === false) {
+ // check if the `@` occurs at the very start of the container
+ // or at least has a whitespace in front of it
+ var text = '';
+ if (startOffset) {
+ text = startContainer.textContent.substr(0, startOffset);
+ }
+
+ while (startContainer = startContainer.previousSibling) {
+ if (startContainer.nodeType === Node.TEXT_NODE) {
+ text = startContainer.textContent + text;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (text.replace(/\u200B/g, '').match(/\S$/)) {
+ return null;
+ }
+ }
+ else {
+ // check if new range includes the mention text
+ if (range.cloneContents().textContent.replace(/\u200B/g, '').replace(/\u00A0/g, '').trim().replace(/^@/, '') !== this._mentionStart) {
+ // string mismatch
+ return null;
+ }
+ }
+
+ return {
+ range: range,
+ selection: selection
+ };
+ },
+
+ _updateDropdownPosition: function() {
+ var offset = this._getDropdownMenuPosition();
+ if (offset === null) {
+ this._hideDropdown();
+
+ return;
+ }
+ offset.top += 7; // add a little vertical gap
+
+ this._dropdownMenu.style.setProperty('left', offset.left + 'px', '');
+ this._dropdownMenu.style.setProperty('top', offset.top + 'px', '');
+
+ this._selectItem(0);
+
+ if (offset.top + this._dropdownMenu.offsetHeight + 10 > window.innerHeight + (window.scrollY || window.pageYOffset)) {
+ this._dropdownMenu.style.setProperty('top', offset.top - this._dropdownMenu.offsetHeight - 2 * this._lineHeight + 7 + 'px', '');
+ }
+ },
+
+ _selectItem: function(step) {
+ // find currently active item
+ var item = elBySel('.active', this._dropdownMenu);
+ if (item !== null) {
+ item.classList.remove('active');
+ }
+
+ this._itemIndex += step;
+ if (this._itemIndex < 0) {
+ this._itemIndex = this._dropdownMenu.childElementCount - 1;
+ }
+ else if (this._itemIndex >= this._dropdownMenu.childElementCount) {
+ this._itemIndex = 0;
+ }
+
+ this._dropdownMenu.children[this._itemIndex].classList.add('active');
+ },
+
+ _hideDropdown: function() {
+ if (this._dropdownMenu !== null) this._dropdownMenu.classList.remove('dropdownOpen');
+ this._dropdownActive = false;
+ this._itemIndex = 0;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getSearchResultList',
+ className: 'wcf\\data\\user\\UserAction',
+ interfaceName: 'wcf\\data\\ISearchAction',
+ parameters: {
+ data: {
+ includeUserGroups: true,
+ scope: 'mention'
+ }
+ }
+ },
+ silent: true
+ };
+ },
+
+ _ajaxSuccess: function(data) {
+ if (!Array.isArray(data.returnValues) || !data.returnValues.length) {
+ this._hideDropdown();
+
+ return;
+ }
+
+ if (this._dropdownMenu === null) {
+ this._dropdownMenu = elCreate('ol');
+ this._dropdownMenu.className = 'dropdownMenu';
+
+ if (_dropdownContainer === null) {
+ _dropdownContainer = elCreate('div');
+ _dropdownContainer.className = 'dropdownMenuContainer';
+ document.body.appendChild(_dropdownContainer);
+ }
+
+ _dropdownContainer.appendChild(this._dropdownMenu);
+ }
+
+ this._dropdownMenu.innerHTML = '';
+
+ var callbackClick = this._setUsername.bind(this), link, listItem, user;
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ user = data.returnValues[i];
+
+ listItem = elCreate('li');
+ link = elCreate('a');
+ link.addEventListener('mousedown', callbackClick);
+ link.className = 'box16';
+ link.innerHTML = '<span>' + user.icon + '</span> <span>' + StringUtil.escapeHTML(user.label) + '</span>';
+ elData(link, 'user-id', user.objectID);
+ elData(link, 'username', user.label);
+
+ listItem.appendChild(link);
+ this._dropdownMenu.appendChild(listItem);
+ }
+
+ this._dropdownMenu.classList.add('dropdownOpen');
+ this._dropdownActive = true;
+
+ this._updateDropdownPosition();
+ }
+ };
+
+ return UiRedactorMention;
+});
+
+/**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Page
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Page',['WoltLabSuite/Core/Ui/Page/Search'], function(UiPageSearch) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _click: function() {},
+ _insert: function() {}
+ };
+ return Fake;
+ }
+
+ function UiRedactorPage(editor, button) { this.init(editor, button); }
+ UiRedactorPage.prototype = {
+ init: function (editor, button) {
+ this._editor = editor;
+
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ UiPageSearch.open(this._insert.bind(this));
+ },
+
+ _insert: function (pageID) {
+ this._editor.buffer.set();
+
+ this._editor.insert.text("[wsp='" + pageID + "'][/wsp]");
+ }
+ };
+
+ return UiRedactorPage;
+});
+
+/**
+ * Manages quotes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Quote
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Quote',['Core', 'EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './Metacode', './PseudoHeader'], function (Core, EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorMetacode, UiRedactorPseudoHeader) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _insertQuote: function() {},
+ _click: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _save: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {},
+ _dialogSubmit: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @param {jQuery} button toolbar button
+ * @constructor
+ */
+ function UiRedactorQuote(editor, button) { this.init(editor, button); }
+ UiRedactorQuote.prototype = {
+ /**
+ * Initializes the quote management.
+ *
+ * @param {Object} editor editor instance
+ * @param {jQuery} button toolbar button
+ */
+ init: function(editor, button) {
+ this._quote = null;
+ this._quotes = elByTag('woltlab-quote', editor.$editor[0]);
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ this._editor.button.addCallback(button, this._click.bind(this));
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+
+ // quote manager
+ EventHandler.add('com.woltlab.wcf.redactor2', 'insertQuote_' + this._elementId, this._insertQuote.bind(this));
+ },
+
+ /**
+ * Inserts a quote.
+ *
+ * @param {Object} data quote data
+ * @protected
+ */
+ _insertQuote: function (data) {
+ if (this._editor.WoltLabSource.isActive()) {
+ return;
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'showEditor');
+
+ var editor = this._editor.core.editor()[0];
+ this._editor.selection.restore();
+
+ this._editor.buffer.set();
+
+ // caret must be within a `<p>`, if it is not: move it
+ var block = this._editor.selection.block();
+ if (block === false) {
+ this._editor.focus.end();
+ block = this._editor.selection.block();
+ }
+
+ while (block && block.parentNode !== editor) {
+ block = block.parentNode;
+ }
+
+ var quote = elCreate('woltlab-quote');
+ elData(quote, 'author', data.author);
+ elData(quote, 'link', data.link);
+
+ var content = data.content;
+ if (data.isText) {
+ content = StringUtil.escapeHTML(content);
+ content = '<p>' + content + '</p>';
+ content = content.replace(/\n\n/g, '</p><p>');
+ content = content.replace(/\n/g, '<br>');
+ }
+ else {
+ //noinspection JSUnresolvedFunction
+ content = UiRedactorMetacode.convertFromHtml(this._editor.$element[0].id, content);
+ }
+
+ // bypass the editor as `insert.html()` doesn't like us
+ quote.innerHTML = content;
+
+ block.parentNode.insertBefore(quote, block.nextSibling);
+
+ if (block.nodeName === 'P' && (block.innerHTML === '<br>' || block.innerHTML.replace(/\u200B/g, '') === '')) {
+ block.parentNode.removeChild(block);
+ }
+
+ // avoid adjacent blocks that are not paragraphs
+ var sibling = quote.previousElementSibling;
+ if (sibling && sibling.nodeName !== 'P') {
+ sibling = elCreate('p');
+ sibling.textContent = '\u200B';
+ quote.parentNode.insertBefore(sibling, quote);
+ }
+
+ this._editor.WoltLabCaret.paragraphAfterBlock(quote);
+
+ this._editor.buffer.set();
+ },
+
+ /**
+ * Toggles the quote block on button click.
+ *
+ * @protected
+ */
+ _click: function() {
+ this._editor.button.toggle({}, 'woltlab-quote', 'func', 'block.format');
+
+ var quote = this._editor.selection.block();
+ if (quote && quote.nodeName === 'WOLTLAB-QUOTE') {
+ this._setTitle(quote);
+
+ quote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(quote);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ var quote;
+ for (var i = 0, length = this._quotes.length; i < length; i++) {
+ quote = this._quotes[i];
+
+ quote.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(quote);
+ }
+ },
+
+ /**
+ * Opens the dialog overlay to edit the quote's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var quote = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(quote);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(quote);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._quote = quote;
+
+ UiDialog.open(this);
+ }
+ },
+
+ /**
+ * Saves the changes to the quote's properties.
+ *
+ * @protected
+ */
+ _dialogSubmit: function() {
+ var id = 'redactor-quote-' + this._elementId;
+ var urlInput = elById(id + '-url');
+
+ var url = urlInput.value.replace(/\u200B/g, '').trim();
+ // simple test to check if it at least looks like it could be a valid url
+ if (url.length && !/^https?:\/\/[^\/]+/.test(url)) {
+ elInnerError(urlInput, Language.get('wcf.editor.quote.url.error.invalid'));
+ return;
+ }
+ else {
+ elInnerError(urlInput, false);
+ }
+
+ // set author
+ elData(this._quote, 'author', elById(id + '-author').value);
+
+ // set url
+ elData(this._quote, 'link', url);
+
+ this._setTitle(this._quote);
+ this._editor.caret.after(this._quote);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * Sets or updates the quote's header title.
+ *
+ * @param {Element} quote quote element
+ * @protected
+ */
+ _setTitle: function(quote) {
+ var title = Language.get('wcf.editor.quote.title', {
+ author: elData(quote, 'author'),
+ url: elData(quote, 'url')
+ });
+
+ if (elData(quote, 'title') !== title) {
+ elData(quote, 'title', title);
+ }
+ },
+
+ _delete: function (event) {
+ event.preventDefault();
+
+ var caretEnd = this._quote.nextElementSibling || this._quote.previousElementSibling;
+ if (caretEnd === null && this._quote.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._quote.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._quote);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var id = 'redactor-quote-' + this._elementId,
+ idAuthor = id + '-author',
+ idButtonDelete = id + '-button-delete',
+ idButtonSave = id + '-button-save',
+ idUrl = id + '-url';
+
+ return {
+ id: id,
+ options: {
+ onClose: (function () {
+ this._editor.selection.restore();
+
+ UiDialog.destroy(this);
+ }).bind(this),
+
+ onSetup: (function() {
+ elById(idButtonDelete).addEventListener(WCF_CLICK_EVENT, this._delete.bind(this));
+ }).bind(this),
+
+ onShow: (function() {
+ elById(idAuthor).value = elData(this._quote, 'author');
+ elById(idUrl).value = elData(this._quote, 'link');
+ }).bind(this),
+
+ title: Language.get('wcf.editor.quote.edit')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="' + idAuthor + '">' + Language.get('wcf.editor.quote.author') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idAuthor + '" class="long" data-dialog-submit-on-enter="true">'
+ + '</dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="' + idUrl + '">' + Language.get('wcf.editor.quote.url') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idUrl + '" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.quote.url.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button id="' + idButtonSave + '" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.save') + '</button>'
+ + '<button id="' + idButtonDelete + '">' + Language.get('wcf.global.button.delete') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+
+ return UiRedactorQuote;
+});
+/**
+ * Manages spoilers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Spoiler
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Spoiler',['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './PseudoHeader'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorPseudoHeader) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _bbcodeSpoiler: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {},
+ _dialogSubmit: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @constructor
+ */
+ function UiRedactorSpoiler(editor) { this.init(editor); }
+ UiRedactorSpoiler.prototype = {
+ /**
+ * Initializes the spoiler management.
+ *
+ * @param {Object} editor editor instance
+ */
+ init: function(editor) {
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+ this._spoiler = null;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'bbcode_spoiler_' + this._elementId, this._bbcodeSpoiler.bind(this));
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+ },
+
+ /**
+ * Intercepts the insertion of `[spoiler]` tags and uses
+ * the custom `<woltlab-spoiler>` element instead.
+ *
+ * @param {Object} data event data
+ * @protected
+ */
+ _bbcodeSpoiler: function(data) {
+ data.cancel = true;
+
+ this._editor.button.toggle({}, 'woltlab-spoiler', 'func', 'block.format');
+
+ var spoiler = this._editor.selection.block();
+ if (spoiler && spoiler.nodeName === 'WOLTLAB-SPOILER') {
+ this._setTitle(spoiler);
+
+ spoiler.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(spoiler);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ elBySelAll('woltlab-spoiler', this._editor.$editor[0], (function(spoiler) {
+ spoiler.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(spoiler);
+ }).bind(this));
+ },
+
+ /**
+ * Opens the dialog overlay to edit the spoiler's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var spoiler = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(spoiler);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(spoiler);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._spoiler = spoiler;
+
+ UiDialog.open(this);
+ }
+ },
+
+ /**
+ * Saves the changes to the spoiler's properties.
+ *
+ * @protected
+ */
+ _dialogSubmit: function() {
+ elData(this._spoiler, 'label', elById('redactor-spoiler-' + this._elementId + '-label').value);
+
+ this._setTitle(this._spoiler);
+ this._editor.caret.after(this._spoiler);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * Sets or updates the spoiler's header title.
+ *
+ * @param {Element} spoiler spoiler element
+ * @protected
+ */
+ _setTitle: function(spoiler) {
+ var title = Language.get('wcf.editor.spoiler.title', { label: elData(spoiler, 'label') });
+
+ if (elData(spoiler, 'title') !== title) {
+ elData(spoiler, 'title', title);
+ }
+ },
+
+ _delete: function (event) {
+ event.preventDefault();
+
+ var caretEnd = this._spoiler.nextElementSibling || this._spoiler.previousElementSibling;
+ if (caretEnd === null && this._spoiler.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._spoiler.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._spoiler);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var id = 'redactor-spoiler-' + this._elementId,
+ idButtonDelete = id + '-button-delete',
+ idButtonSave = id + '-button-save',
+ idLabel = id + '-label';
+
+ return {
+ id: id,
+ options: {
+ onClose: (function () {
+ this._editor.selection.restore();
+
+ UiDialog.destroy(this);
+ }).bind(this),
+
+ onSetup: (function() {
+ elById(idButtonDelete).addEventListener(WCF_CLICK_EVENT, this._delete.bind(this));
+ }).bind(this),
+
+ onShow: (function() {
+ elById(idLabel).value = elData(this._spoiler, 'label');
+ }).bind(this),
+
+ title: Language.get('wcf.editor.spoiler.edit')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="' + idLabel + '">' + Language.get('wcf.editor.spoiler.label') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idLabel + '" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.spoiler.label.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button id="' + idButtonSave + '" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.save') + '</button>'
+ + '<button id="' + idButtonDelete + '">' + Language.get('wcf.global.button.delete') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+
+ return UiRedactorSpoiler;
+});
+define('WoltLabSuite/Core/Ui/Redactor/Table',['Language', 'Ui/Dialog'], function(Language, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ showDialog: function() {},
+ _submit: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callback = null;
+
+ return {
+ showDialog: function(options) {
+ UiDialog.open(this);
+
+ _callback = options.submitCallback;
+ },
+
+ _dialogSubmit: function() {
+ // check if rows and cols are within the boundaries
+ var isValid = true;
+ ['rows', 'cols'].forEach(function(type) {
+ var input = elById('redactor-table-' + type);
+ if (input.value < 1 || input.value > 100) {
+ isValid = false;
+ }
+ });
+
+ if (!isValid) return;
+
+ _callback();
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'redactorDialogTable',
+ options: {
+ onShow: function () {
+ elById('redactor-table-rows').value = 2;
+ elById('redactor-table-cols').value = 3;
+ },
+ title: Language.get('wcf.editor.table.insertTable')
+ },
+ source: '<dl>'
+ + '<dt><label for="redactor-table-rows">' + Language.get('wcf.editor.table.rows') + '</label></dt>'
+ + '<dd><input type="number" id="redactor-table-rows" class="small" min="1" max="100" value="2" data-dialog-submit-on-enter="true"></dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="redactor-table-cols">' + Language.get('wcf.editor.table.cols') + '</label></dt>'
+ + '<dd><input type="number" id="redactor-table-cols" class="small" min="1" max="100" value="3" data-dialog-submit-on-enter="true"></dd>'
+ + '</dl>'
+ + '<div class="formSubmit">'
+ + '<button id="redactor-modal-button-action" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.insert') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Search/Page',['Core', 'Dom/Traverse', 'Dom/Util', 'Ui/Screen', 'Ui/SimpleDropdown', './Input'], function(Core, DomTraverse, DomUtil, UiScreen, UiSimpleDropdown, UiSearchInput) {
+ "use strict";
+
+ return {
+ init: function (objectType) {
+ var searchInput = elById('pageHeaderSearchInput');
+
+ new UiSearchInput(searchInput, {
+ ajax: {
+ className: 'wcf\\data\\search\\keyword\\SearchKeywordAction'
+ },
+ callbackDropdownInit: function(dropdownMenu) {
+ dropdownMenu.classList.add('dropdownMenuPageSearch');
+
+ if (UiScreen.is('screen-lg')) {
+ elData(dropdownMenu, 'dropdown-alignment-horizontal', 'right');
+
+ var minWidth = searchInput.clientWidth;
+ dropdownMenu.style.setProperty('min-width', minWidth + 'px', '');
+
+ // calculate offset to ignore the width caused by the submit button
+ var parent = searchInput.parentNode;
+ var offsetRight = (DomUtil.offset(parent).left + parent.clientWidth) - (DomUtil.offset(searchInput).left + minWidth);
+ var offsetTop = DomUtil.styleAsInt(window.getComputedStyle(parent), 'padding-bottom');
+ dropdownMenu.style.setProperty('transform', 'translateX(-' + Math.ceil(offsetRight) + 'px) translateY(-' + offsetTop + 'px)', '');
+ }
+ },
+ callbackSelect: function() {
+ setTimeout(function() {
+ DomTraverse.parentByTag(searchInput, 'FORM').submit();
+ }, 1);
+
+ return true;
+ }
+ });
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(DomUtil.identify(elBySel('.pageHeaderSearchType')));
+ var callback = this._click.bind(this);
+ elBySelAll('a[data-object-type]', dropdownMenu, function(link) {
+ link.addEventListener(WCF_CLICK_EVENT, callback);
+ });
+
+ // trigger click on init
+ var link = elBySel('a[data-object-type="' + objectType + '"]', dropdownMenu);
+ Core.triggerEvent(link, WCF_CLICK_EVENT);
+ },
+
+ _click: function(event) {
+ event.preventDefault();
+
+ var pageHeader = elById('pageHeader');
+ pageHeader.classList.add('searchBarForceOpen');
+ window.setTimeout(function() {
+ pageHeader.classList.remove('searchBarForceOpen');
+ }, 10);
+
+ var objectType = elData(event.currentTarget, 'object-type');
+
+ var container = elById('pageHeaderSearchParameters');
+ container.innerHTML = '';
+
+ var extendedLink = elData(event.currentTarget, 'extended-link');
+ if (extendedLink) {
+ elBySel('.pageHeaderSearchExtendedLink').href = extendedLink;
+ }
+
+ var parameters = elData(event.currentTarget, 'parameters');
+ if (parameters) {
+ parameters = JSON.parse(parameters);
+ }
+ else {
+ parameters = {};
+ }
+
+ if (objectType) parameters['types[]'] = objectType;
+
+ for (var key in parameters) {
+ if (parameters.hasOwnProperty(key)) {
+ var input = elCreate('input');
+ input.type = 'hidden';
+ input.name = key;
+ input.value = parameters[key];
+ container.appendChild(input);
+ }
+ }
+
+ // update label
+ var button = elBySel('.pageHeaderSearchType > .button > .pageHeaderSearchTypeLabel', elById('pageHeaderSearchInputContainer'));
+ button.textContent = event.currentTarget.textContent;
+ }
+ };
+});
+
+/**
+ * Inserts smilies into a WYSIWYG editor instance, with WAI-ARIA keyboard support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Smiley/Insert
+ */
+define('WoltLabSuite/Core/Ui/Smiley/Insert',['EventHandler', 'EventKey'], function (EventHandler, EventKey) {
+ 'use strict';
+
+ function UiSmileyInsert(editorId) { this.init(editorId); }
+
+ UiSmileyInsert.prototype = {
+ _editorId: '',
+
+ /**
+ * @param {string} editorId
+ */
+ init: function (editorId) {
+ this._editorId = editorId;
+
+ var container = elById('smilies-' + this._editorId);
+ if (!container) {
+ // form builder
+ container = elById(this._editorId + 'SmiliesTabContainer');
+ if (!container) {
+ throw new Error('Unable to find the message tab menu container containing the smilies.');
+ }
+ }
+
+ container.addEventListener('keydown', this._keydown.bind(this));
+ container.addEventListener('mousedown', this._mousedown.bind(this));
+ },
+
+ /**
+ * @param {KeyboardEvent} event
+ * @protected
+ */
+ _keydown: function(event) {
+ var activeButton = document.activeElement;
+ if (!activeButton.classList.contains('jsSmiley')) {
+ return;
+ }
+
+ if (EventKey.ArrowLeft(event) || EventKey.ArrowRight(event) || EventKey.Home(event) || EventKey.End(event)) {
+ event.preventDefault();
+
+ var smilies = Array.prototype.slice.call(elBySelAll('.jsSmiley', event.currentTarget));
+ if (EventKey.ArrowLeft(event)) {
+ smilies.reverse();
+ }
+
+ var index = smilies.indexOf(activeButton);
+ if (EventKey.Home(event)) {
+ index = 0;
+ }
+ else if (EventKey.End(event)) {
+ index = smilies.length - 1;
+ }
+ else {
+ index = index + 1;
+ if (index === smilies.length) {
+ index = 0;
+ }
+ }
+
+ smilies[index].focus();
+ }
+ else if (EventKey.Enter(event) || EventKey.Space(event)) {
+ event.preventDefault();
+
+ this._insert(elBySel('img', activeButton));
+ }
+ },
+
+ /**
+ * @param {MouseEvent} event
+ * @protected
+ */
+ _mousedown: function (event) {
+ event.preventDefault();
+
+ // Clicks may occur on a few different elements, but we are only looking for the image.
+ var listItem = event.target.closest('li');
+ var img = elBySel('img', listItem);
+ if (img) this._insert(img);
+ },
+
+ /**
+ * @param {Element} img
+ * @protected
+ */
+ _insert: function(img) {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'insertSmiley_' + this._editorId, {
+ img: img
+ });
+ }
+ };
+ return UiSmileyInsert;
+});
+
+/**
+ * Provides a selection dialog for FontAwesome icons with filter capabilities.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Style/FontAwesome
+ */
+define('WoltLabSuite/Core/Ui/Style/FontAwesome',['Language', 'Ui/Dialog', 'WoltLabSuite/Core/Ui/ItemList/Filter'], function (Language, UiDialog, UiItemListFilter) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ setup: function() {},
+ open: function() {},
+ _click: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callback, _iconList, _itemListFilter;
+ var _icons = [];
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Style/FontAwesome
+ */
+ return {
+ /**
+ * Sets the list of available icons, must be invoked prior to any call
+ * to the `open()` method.
+ *
+ * @param {string[]} icons list of icon names excluding the `fa-` prefix
+ */
+ setup: function (icons) {
+ _icons = icons;
+ },
+
+ /**
+ * Shows the FontAwesome selection dialog, supplied callback will be
+ * invoked with the selection icon's name as the only argument.
+ *
+ * @param {Function<string>} callback callback on icon selection, receives icon name only
+ */
+ open: function(callback) {
+ if (_icons.length === 0) {
+ throw new Error("Missing icon data, please include the template before calling this method using `{include file='fontAwesomeJavaScript'}`.");
+ }
+
+ _callback = callback;
+
+ UiDialog.open(this);
+ },
+
+ /**
+ * Selects an icon, notifies the callback and closes the dialog.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ var item = event.target.closest('li');
+ var icon = elBySel('small', item).textContent.trim();
+
+ UiDialog.close(this);
+
+ _callback(icon);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'fontAwesomeSelection',
+ options: {
+ onSetup: (function() {
+ _iconList = elById('fontAwesomeIcons');
+
+ // build icons
+ var icon, html = '';
+ for (var i = 0, length = _icons.length; i < length; i++) {
+ icon = _icons[i];
+
+ html += '<li><span class="icon icon48 fa-' + icon + '"></span><small>' + icon + '</small></li>';
+ }
+
+ _iconList.innerHTML = html;
+ _iconList.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+
+ _itemListFilter = new UiItemListFilter('fontAwesomeIcons', {
+ callbackPrepareItem: function (item) {
+ var small = elBySel('small', item);
+ var text = small.textContent.trim();
+
+ return {
+ item: item,
+ span: small,
+ text: text
+ };
+ },
+ enableVisibilityFilter: false
+ });
+ }).bind(this),
+ onShow: function () {
+ _itemListFilter.reset();
+ },
+ title: Language.get('wcf.global.fontAwesome.selectIcon')
+ },
+ source: '<ul class="fontAwesomeIcons" id="fontAwesomeIcons"></ul>'
+ };
+ }
+ }
+});
+/**
+ * Provides a simple toggle to show or hide certain elements when the
+ * target element is checked.
+ *
+ * Be aware that the list of elements to show or hide accepts selectors
+ * which will be passed to `elBySel()`, causing only the first matched
+ * element to be used. If you require a whole list of elements identified
+ * by a single selector to be handled, please provide the actual list of
+ * elements instead.
+ *
+ * Usage:
+ *
+ * new UiToggleInput('input[name="foo"][value="bar"]', {
+ * show: ['#showThisContainer', '.makeThisVisibleToo'],
+ * hide: ['.notRelevantStuff', elById('fooBar')]
+ * });
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Toggle/Input
+ */
+define('WoltLabSuite/Core/Ui/Toggle/Input',['Core'], function(Core) {
+ "use strict";
+
+ /**
+ * @param {string} elementSelector element selector used with `elBySel()`
+ * @param {Object} options toggle options
+ * @constructor
+ */
+ function UiToggleInput(elementSelector, options) { this.init(elementSelector, options); }
+ UiToggleInput.prototype = {
+ /**
+ * Initializes a new input toggle.
+ *
+ * @param {string} elementSelector element selector used with `elBySel()`
+ * @param {Object} options toggle options
+ */
+ init: function(elementSelector, options) {
+ this._element = elBySel(elementSelector);
+ if (this._element === null) {
+ throw new Error("Unable to find element by selector '" + elementSelector + "'.");
+ }
+
+ var type = (this._element.nodeName === 'INPUT') ? elAttr(this._element, 'type') : '';
+ if (type !== 'checkbox' && type !== 'radio') {
+ throw new Error("Illegal element, expected input[type='checkbox'] or input[type='radio'].");
+ }
+
+ this._options = Core.extend({
+ hide: [],
+ show: []
+ }, options);
+
+ ['hide', 'show'].forEach((function(type) {
+ var element, i, length;
+ for (i = 0, length = this._options[type].length; i < length; i++) {
+ element = this._options[type][i];
+
+ if (typeof element !== 'string' && !(element instanceof Element)) {
+ throw new TypeError("The array '" + type + "' may only contain string selectors or DOM elements.");
+ }
+ }
+ }).bind(this));
+
+ this._element.addEventListener('change', this._change.bind(this));
+
+ this._handleElements(this._options.show, this._element.checked);
+ this._handleElements(this._options.hide, !this._element.checked);
+ },
+
+ /**
+ * Triggered when element is checked / unchecked.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _change: function(event) {
+ var showElements = event.currentTarget.checked;
+
+ this._handleElements(this._options.show, showElements);
+ this._handleElements(this._options.hide, !showElements);
+ },
+
+ /**
+ * Loops through the target elements and shows / hides them.
+ *
+ * @param {Array} elements list of elements or selectors
+ * @param {boolean} showElement true if elements should be shown
+ * @protected
+ */
+ _handleElements: function(elements, showElement) {
+ var element, tmp;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (typeof element === 'string') {
+ tmp = elBySel(element);
+ if (tmp === null) {
+ throw new Error("Unable to find element by selector '" + element + "'.");
+ }
+
+ elements[i] = element = tmp;
+ }
+
+ window[(showElement ? 'elShow' : 'elHide')](element);
+ }
+ }
+ };
+
+ return UiToggleInput;
+});
+
+/**
+ * Simple notification overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Editor
+ */
+define('WoltLabSuite/Core/Ui/User/Editor',['Ajax', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', 'Ui/Notification'], function(Ajax, Language, StringUtil, DomUtil, UiDialog, UiNotification) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _click: function() {},
+ _submit: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _actionName = '';
+ var _userHeader = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/User/Editor
+ */
+ return {
+ /**
+ * Initializes the user editor.
+ */
+ init: function() {
+ _userHeader = elBySel('.userProfileUser');
+
+ // init buttons
+ ['ban', 'disableAvatar', 'disableCoverPhoto', 'disableSignature', 'enable'].forEach((function(action) {
+ var button = elBySel('.userProfileButtonMenu .jsButtonUser' + StringUtil.ucfirst(action));
+
+ // button is missing if users lacks the permission
+ if (button) {
+ elData(button, 'action', action);
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Handles clicks on action buttons.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ //noinspection JSCheckFunctionSignatures
+ var action = elData(event.currentTarget, 'action');
+ var actionName = '';
+ switch (action) {
+ case 'ban':
+ if (elDataBool(_userHeader, 'banned')) {
+ actionName = 'unban';
+ }
+ break;
+
+ case 'disableAvatar':
+ if (elDataBool(_userHeader, 'disable-avatar')) {
+ actionName = 'enableAvatar';
+ }
+ break;
+
+ case 'disableCoverPhoto':
+ if (elDataBool(_userHeader, 'disable-cover-photo')) {
+ actionName = 'enableCoverPhoto';
+ }
+ break;
+
+ case 'disableSignature':
+ if (elDataBool(_userHeader, 'disable-signature')) {
+ actionName = 'enableSignature';
+ }
+ break;
+
+ case 'enable':
+ actionName = (elDataBool(_userHeader, 'is-disabled')) ? 'enable' : 'disable';
+ break;
+ }
+
+ if (actionName === '') {
+ _actionName = action;
+
+ UiDialog.open(this);
+ }
+ else {
+ Ajax.api(this, {
+ actionName: actionName
+ });
+ }
+ },
+
+ /**
+ * Handles form submit and input validation.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _submit: function(event) {
+ event.preventDefault();
+
+ var label = elById('wcfUiUserEditorExpiresLabel');
+
+ var expires = '';
+ var errorMessage = '';
+ if (!elById('wcfUiUserEditorNeverExpires').checked) {
+ expires = elById('wcfUiUserEditorExpiresDatePicker').value;
+ if (expires === '') {
+ errorMessage = Language.get('wcf.global.form.error.empty');
+ }
+ }
+
+ elInnerError(label, errorMessage);
+
+ var parameters = {};
+ parameters[_actionName + 'Expires'] = expires;
+ parameters[_actionName + 'Reason'] = elById('wcfUiUserEditorReason').value.trim();
+
+ Ajax.api(this, {
+ actionName: _actionName,
+ parameters: parameters
+ });
+ },
+
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'ban':
+ case 'unban':
+ elData(_userHeader, 'banned', (data.actionName === 'ban'));
+ elBySel('.userProfileButtonMenu .jsButtonUserBan').textContent = Language.get('wcf.user.' + (data.actionName === 'ban' ? 'unban' : 'ban'));
+
+ var contentTitle = elBySel('.contentTitle', _userHeader);
+ var banIcon = elBySel('.jsUserBanned', contentTitle);
+ if (data.actionName === 'ban') {
+ banIcon = elCreate('span');
+ banIcon.className = 'icon icon24 fa-lock jsUserBanned jsTooltip';
+ banIcon.title = data.returnValues;
+ contentTitle.appendChild(banIcon);
+ }
+ else if (banIcon) {
+ elRemove(banIcon);
+ }
+
+ break;
+
+ case 'disableAvatar':
+ case 'enableAvatar':
+ elData(_userHeader, 'disable-avatar', (data.actionName === 'disableAvatar'));
+ elBySel('.userProfileButtonMenu .jsButtonUserDisableAvatar').textContent = Language.get('wcf.user.' + (data.actionName === 'disableAvatar' ? 'enable' : 'disable') + 'Avatar');
+
+ break;
+
+ case 'disableCoverPhoto':
+ case 'enableCoverPhoto':
+ elData(_userHeader, 'disable-cover-photo', (data.actionName === 'disableCoverPhoto'));
+ elBySel('.userProfileButtonMenu .jsButtonUserDisableCoverPhoto').textContent = Language.get('wcf.user.' + (data.actionName === 'disableCoverPhoto' ? 'enable' : 'disable') + 'CoverPhoto');
+
+ break;
+
+ case 'disableSignature':
+ case 'enableSignature':
+ elData(_userHeader, 'disable-signature', (data.actionName === 'disableSignature'));
+ elBySel('.userProfileButtonMenu .jsButtonUserDisableSignature').textContent = Language.get('wcf.user.' + (data.actionName === 'disableSignature' ? 'enable' : 'disable') + 'Signature');
+
+ break;
+
+ case 'enable':
+ case 'disable':
+ elData(_userHeader, 'is-disabled', (data.actionName === 'disable'));
+ elBySel('.userProfileButtonMenu .jsButtonUserEnable').textContent = Language.get('wcf.acp.user.' + (data.actionName === 'enable' ? 'disable' : 'enable'));
+
+ break;
+ }
+
+ if (data.actionName === 'ban' || data.actionName === 'disableAvatar' || data.actionName === 'disableCoverPhoto' || data.actionName === 'disableSignature') {
+ UiDialog.close(this);
+ }
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\UserAction',
+ objectIDs: [ elData(_userHeader, 'object-id') ]
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiUserEditor',
+ options: {
+ onSetup: (function (content) {
+ elById('wcfUiUserEditorNeverExpires').addEventListener('change', function () {
+ window[(this.checked) ? 'elHide' : 'elShow'](elById('wcfUiUserEditorExpiresSettings'));
+ });
+
+ elBySel('button.buttonPrimary', content).addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ }).bind(this),
+ onShow: function(content) {
+ UiDialog.setTitle('wcfUiUserEditor', Language.get('wcf.user.' + _actionName + '.confirmMessage'));
+
+ var label = elById('wcfUiUserEditorReason').nextElementSibling;
+ var phrase = 'wcf.user.' + _actionName + '.reason.description';
+ label.textContent = Language.get(phrase);
+ window[(label.textContent === phrase) ? 'elHide' : 'elShow'](label);
+
+ label = elById('wcfUiUserEditorNeverExpires').nextElementSibling;
+ label.textContent = Language.get('wcf.user.' + _actionName + '.neverExpires');
+
+ label = elBySel('label[for="wcfUiUserEditorExpires"]', content);
+ label.textContent = Language.get('wcf.user.' + _actionName + '.expires');
+
+ label = elById('wcfUiUserEditorExpiresLabel');
+ label.textContent = Language.get('wcf.user.' + _actionName + '.expires.description');
+ }
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiUserEditorReason">' + Language.get('wcf.global.reason') + '</label></dt>'
+ + '<dd><textarea id="wcfUiUserEditorReason" cols="40" rows="3"></textarea><small></small></dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt></dt>'
+ + '<dd><label><input type="checkbox" id="wcfUiUserEditorNeverExpires" checked> <span></span></label></dd>'
+ + '</dl>'
+ + '<dl id="wcfUiUserEditorExpiresSettings" style="display: none">'
+ + '<dt><label for="wcfUiUserEditorExpires"></label></dt>'
+ + '<dd>'
+ + '<input type="date" name="wcfUiUserEditorExpires" id="wcfUiUserEditorExpires" class="medium" min="' + new Date(TIME_NOW * 1000).toISOString() + '" data-ignore-timezone="true">'
+ + '<small id="wcfUiUserEditorExpiresLabel"></small>'
+ + '</dd>'
+ +'</dl>'
+ + '</div>'
+ + '<div class="formSubmit"><button class="buttonPrimary">' + Language.get('wcf.global.button.submit') + '</button></div>'
+ };
+ }
+ };
+});
+
+/**
+ * Shows and hides an element that depends on certain selected pages when setting up conditions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Condition/Page/Dependence
+ */
+define('WoltLabSuite/Core/Controller/Condition/Page/Dependence',['Dom/ChangeListener', 'Dom/Traverse', 'EventHandler', 'ObjectMap'], function(DomChangeListener, DomTraverse, EventHandler, ObjectMap) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ register: function() {},
+ _checkVisibility: function() {},
+ _hideDependentElement: function() {},
+ _showDependentElement: function() {}
+ };
+ return Fake;
+ }
+
+ var _pages = elBySelAll('input[name="pageIDs[]"]');
+ var _dependentElements = [];
+ var _pageIds = new ObjectMap();
+ var _hiddenElements = new ObjectMap();
+
+ var _didInit = false;
+
+ return {
+ register: function(dependentElement, pageIds) {
+ _dependentElements.push(dependentElement);
+ _pageIds.set(dependentElement, pageIds);
+ _hiddenElements.set(dependentElement, []);
+
+ if (!_didInit) {
+ for (var i = 0, length = _pages.length; i < length; i++) {
+ _pages[i].addEventListener('change', this._checkVisibility.bind(this));
+ }
+
+ _didInit = true;
+ }
+
+ // remove the dependent element before submit if it is hidden
+ DomTraverse.parentByTag(dependentElement, 'FORM').addEventListener('submit', function() {
+ if (dependentElement.style.getPropertyValue('display') === 'none') {
+ dependentElement.remove();
+ }
+ });
+
+ this._checkVisibility();
+ },
+
+ /**
+ * Checks if only relevant pages are selected. If that is the case, the dependent
+ * element is shown, otherwise it is hidden.
+ *
+ * @private
+ */
+ _checkVisibility: function() {
+ var dependentElement, page, pageIds, checkedPageIds, irrelevantPageIds;
+
+ depenentElementLoop: for (var i = 0, length = _dependentElements.length; i < length; i++) {
+ dependentElement = _dependentElements[i];
+ pageIds = _pageIds.get(dependentElement);
+
+ checkedPageIds = [];
+ for (var j = 0, length2 = _pages.length; j < length2; j++) {
+ page = _pages[j];
+
+ if (page.checked) {
+ checkedPageIds.push(~~page.value);
+ }
+ }
+
+ irrelevantPageIds = checkedPageIds.filter(function(pageId) {
+ return pageIds.indexOf(pageId) === -1;
+ });
+
+ if (!checkedPageIds.length || irrelevantPageIds.length) {
+ this._hideDependentElement(dependentElement);
+ }
+ else {
+ this._showDependentElement(dependentElement);
+ }
+ }
+
+ EventHandler.fire('com.woltlab.wcf.pageConditionDependence', 'checkVisivility');
+ },
+
+ /**
+ * Hides all elements that depend on the given element.
+ *
+ * @param {HTMLElement} dependentElement
+ */
+ _hideDependentElement: function(dependentElement) {
+ elHide(dependentElement);
+
+ var hiddenElements = _hiddenElements.get(dependentElement);
+ for (var i = 0, length = hiddenElements.length; i < length; i++) {
+ elHide(hiddenElements[i]);
+ }
+
+ _hiddenElements.set(dependentElement, []);
+ },
+
+ /**
+ * Shows all elements that depend on the given element.
+ *
+ * @param {HTMLElement} dependentElement
+ */
+ _showDependentElement: function(dependentElement) {
+ elShow(dependentElement);
+
+ // make sure that all parent elements are also visible
+ var parentNode = dependentElement;
+ while ((parentNode = parentNode.parentNode) && parentNode instanceof Element) {
+ if (parentNode.style.getPropertyValue('display') === 'none') {
+ _hiddenElements.get(dependentElement).push(parentNode);
+ }
+
+ elShow(parentNode);
+ }
+ }
+ };
+});
+
+/**
+ * Map route planner based on Google Maps.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Map/Route/Planner
+ */
+define('WoltLabSuite/Core/Controller/Map/Route/Planner',[
+ 'Dom/Traverse',
+ 'Dom/Util',
+ 'Language',
+ 'Ui/Dialog',
+ 'WoltLabSuite/Core/Ajax/Status'
+], function(
+ DomTraverse,
+ DomUtil,
+ Language,
+ UiDialog,
+ AjaxStatus
+) {
+ /**
+ * @constructor
+ */
+ function Planner(buttonId, destination) {
+ this._button = elById(buttonId);
+ if (this._button === null) {
+ throw new Error("Unknown button with id '" + buttonId + "'");
+ }
+
+ this._button.addEventListener('click', this._openDialog.bind(this));
+
+ this._destination = destination;
+ }
+ Planner.prototype = {
+ /**
+ * Sets up the route planner dialog.
+ */
+ _dialogSetup: function() {
+ return {
+ id: this._button.id + 'Dialog',
+ options: {
+ onShow: this._initDialog.bind(this),
+ title: Language.get('wcf.map.route.planner')
+ },
+ source: '<div class="googleMapsDirectionsContainer" style="display: none;">' +
+ '<div class="googleMap"></div>' +
+ '<div class="googleMapsDirections"></div>' +
+ '</div>' +
+ '<small class="googleMapsDirectionsGoogleLinkContainer"><a href="' + this._getGoogleMapsLink() + '" class="googleMapsDirectionsGoogleLink" target="_blank" style="display: none;">' + Language.get('wcf.map.route.viewOnGoogleMaps') + '</a></small>' +
+ '<dl>' +
+ '<dt>' + Language.get('wcf.map.route.origin') + '</dt>' +
+ '<dd><input type="text" name="origin" class="long" autofocus /></dd>' +
+ '</dl>' +
+ '<dl style="display: none;">' +
+ '<dt>' + Language.get('wcf.map.route.travelMode') + '</dt>' +
+ '<dd>' +
+ '<select name="travelMode">' +
+ '<option value="driving">' + Language.get('wcf.map.route.travelMode.driving') + '</option>' +
+ '<option value="walking">' + Language.get('wcf.map.route.travelMode.walking') + '</option>' +
+ '<option value="bicycling">' + Language.get('wcf.map.route.travelMode.bicycling') + '</option>' +
+ '<option value="transit">' + Language.get('wcf.map.route.travelMode.transit') + '</option>' +
+ '</select>' +
+ '</dd>' +
+ '</dl>'
+ }
+ },
+
+ /**
+ * Calculates the route based on the given result of a location search.
+ *
+ * @param {object} data
+ */
+ _calculateRoute: function(data) {
+ var dialog = UiDialog.getDialog(this).dialog;
+
+ if (data.label) {
+ this._originInput.value = data.label;
+ }
+
+ if (this._map === undefined) {
+ this._map = new google.maps.Map(elByClass('googleMap', dialog)[0], {
+ disableDoubleClickZoom: WCF.Location.GoogleMaps.Settings.get('disableDoubleClickZoom'),
+ draggable: WCF.Location.GoogleMaps.Settings.get('draggable'),
+ mapTypeId: google.maps.MapTypeId.ROADMAP,
+ scaleControl: WCF.Location.GoogleMaps.Settings.get('scaleControl'),
+ scrollwheel: WCF.Location.GoogleMaps.Settings.get('scrollwheel')
+ });
+
+ this._directionsService = new google.maps.DirectionsService();
+ this._directionsRenderer = new google.maps.DirectionsRenderer();
+
+ this._directionsRenderer.setMap(this._map);
+ this._directionsRenderer.setPanel(elByClass('googleMapsDirections', dialog)[0]);
+
+ this._googleLink = elByClass('googleMapsDirectionsGoogleLink', dialog)[0];
+ }
+
+ var request = {
+ destination: this._destination,
+ origin: data.location,
+ provideRouteAlternatives: true,
+ travelMode: google.maps.TravelMode[this._travelMode.value.toUpperCase()]
+ };
+
+ AjaxStatus.show();
+ this._directionsService.route(request, this._setRoute.bind(this));
+
+ elAttr(this._googleLink, 'href', this._getGoogleMapsLink(data.location, this._travelMode.value));
+
+ this._lastOrigin = data.location;
+ },
+
+ /**
+ * Returns the Google Maps link based on the given optional directions origin
+ * and optional travel mode.
+ *
+ * @param {google.maps.LatLng} origin
+ * @param {string} travelMode
+ * @return {string}
+ */
+ _getGoogleMapsLink: function(origin, travelMode) {
+ if (origin) {
+ var link = 'https://www.google.com/maps/dir/?api=1' +
+ '&origin=' + origin.lat() + ',' + origin.lng() + '' +
+ '&destination=' + this._destination.lat() + ',' + this._destination.lng();
+
+ if (travelMode) {
+ link += '&travelmode=' + travelMode;
+ }
+
+ return link;
+ }
+
+ return 'https://www.google.com/maps/search/?api=1&query=' + this._destination.lat() + ',' + this._destination.lng();
+ },
+
+ /**
+ * Initializes the route planning dialog.
+ */
+ _initDialog: function() {
+ if (!this._didInitDialog) {
+ var dialog = UiDialog.getDialog(this).dialog;
+
+ // make input element a location search
+ this._originInput = elBySel('input[name="origin"]', dialog);
+ new WCF.Location.GoogleMaps.LocationSearch(this._originInput, this._calculateRoute.bind(this));
+
+ this._travelMode = elBySel('select[name="travelMode"]', dialog);
+ this._travelMode.addEventListener('change', this._updateRoute.bind(this));
+
+ this._didInitDialog = true;
+ }
+ },
+
+ /**
+ * Opens the route planning dialog.
+ */
+ _openDialog: function() {
+ UiDialog.open(this);
+ },
+
+ /**
+ * Handles the response of the direction service.
+ *
+ * @param {object} result
+ * @param {string} status
+ */
+ _setRoute: function(result, status) {
+ AjaxStatus.hide();
+
+ if (status === 'OK') {
+ elShow(this._map.getDiv().parentNode);
+
+ google.maps.event.trigger(this._map, 'resize');
+
+ this._directionsRenderer.setDirections(result);
+
+ elShow(DomTraverse.parentByTag(this._travelMode, 'DL'));
+ elShow(this._googleLink);
+
+ elInnerError(this._originInput, false);
+ }
+ else {
+ // map irrelevant errors to not found error
+ if (status !== 'OVER_QUERY_LIMIT' && status !== 'REQUEST_DENIED') {
+ status = 'NOT_FOUND';
+ }
+
+ elInnerError(this._originInput, Language.get('wcf.map.route.error.' + status.toLowerCase()));
+ }
+ },
+
+ /**
+ * Updates the route after the travel mode has been changed.
+ */
+ _updateRoute: function() {
+ this._calculateRoute({
+ location: this._lastOrigin
+ });
+ }
+ };
+
+ return Planner;
+});
+
+/**
+ * Handles email notification type for user notification settings.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/User/Notification/Settings
+ */
+define('WoltLabSuite/Core/Controller/User/Notification/Settings',['Dictionary', 'Language', 'Dom/Traverse', 'Ui/SimpleDropdown'], function(Dictionary, Language, DomTraverse, UiSimpleDropdown) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ setup: function() {},
+ _initGroup: function() {},
+ _click: function() {},
+ _createDropdown: function() {},
+ _selectType: function() {}
+ };
+ return Fake;
+ }
+
+ var _data = new Dictionary();
+
+ var _callbackClick = null;
+ var _callbackSelectType = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/User/Notification/Settings
+ */
+ var ControllerUserNotificationSettings = {
+ /**
+ * Binds event listeners for all notifications supporting emails.
+ */
+ setup: function() {
+ _callbackClick = this._click.bind(this);
+ _callbackSelectType = this._selectType.bind(this);
+
+ var group, mailSetting, groups = elBySelAll('#notificationSettings .flexibleButtonGroup');
+ for (var i = 0, length = groups.length; i < length; i++) {
+ group = groups[i];
+
+ mailSetting = elBySel('.notificationSettingsEmail', group);
+ if (mailSetting === null) {
+ continue;
+ }
+
+ this._initGroup(group, mailSetting);
+ }
+ },
+
+ /**
+ * Initializes a setting.
+ *
+ * @param {Element} group button group element
+ * @param {Element} mailSetting mail settings element
+ */
+ _initGroup: function(group, mailSetting) {
+ var groupId = ~~elData(group, 'object-id');
+
+ var disabledNotification = elById('settings_' + groupId + '_disabled');
+ disabledNotification.addEventListener(WCF_CLICK_EVENT, function() { mailSetting.classList.remove('active'); });
+ var enabledNotification = elById('settings_' + groupId + '_enabled');
+ enabledNotification.addEventListener(WCF_CLICK_EVENT, function() { mailSetting.classList.add('active'); });
+
+ var mailValue = DomTraverse.childByTag(mailSetting, 'INPUT');
+
+ var button = DomTraverse.childByTag(mailSetting, 'A');
+ elData(button, 'object-id', groupId);
+ button.addEventListener(WCF_CLICK_EVENT, _callbackClick);
+
+ _data.set(groupId, {
+ button: button,
+ dropdownMenu: null,
+ mailSetting: mailSetting,
+ mailValue: mailValue
+ });
+ },
+
+ /**
+ * Creates and displays the email type dropdown.
+ *
+ * @param {Object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ var button = event.currentTarget;
+ var objectId = ~~elData(button, 'object-id');
+ var data = _data.get(objectId);
+ if (data.dropdownMenu === null) {
+ data.dropdownMenu = this._createDropdown(objectId, data.mailValue.value);
+
+ button.parentNode.classList.add('dropdown');
+ button.parentNode.appendChild(data.dropdownMenu);
+
+ UiSimpleDropdown.init(button, event);
+ }
+ else {
+ var items = DomTraverse.childrenByTag(data.dropdownMenu, 'LI'), value = data.mailValue.value;
+ for (var i = 0; i < 4; i++) {
+ items[i].classList[(elData(items[i], 'value') === value) ? 'add' : 'remove']('active');
+ }
+ }
+ },
+
+ /**
+ * Creates the email type dropdown.
+ *
+ * @param {int} objectId notification event id
+ * @param {string} initialValue initial email type
+ * @returns {Element} dropdown menu object
+ */
+ _createDropdown: function(objectId, initialValue) {
+ var dropdownMenu = elCreate('ul');
+ dropdownMenu.className = 'dropdownMenu';
+ elData(dropdownMenu, 'object-id', objectId);
+
+ var link, listItem, value, items = ['instant', 'daily', 'divider', 'none'];
+ for (var i = 0; i < 4; i++) {
+ value = items[i];
+
+ listItem = elCreate('li');
+ if (value === 'divider') {
+ listItem.className = 'dropdownDivider';
+ }
+ else {
+ link = elCreate('a');
+ link.textContent = Language.get('wcf.user.notification.mailNotificationType.' + value);
+ listItem.appendChild(link);
+ elData(listItem, 'value', value);
+ listItem.addEventListener(WCF_CLICK_EVENT, _callbackSelectType);
+
+ if (initialValue === value) {
+ listItem.className = 'active';
+ }
+ }
+
+ dropdownMenu.appendChild(listItem);
+ }
+
+ return dropdownMenu;
+ },
+
+ /**
+ * Sets the selected email notification type.
+ *
+ * @param {Object} event event object
+ */
+ _selectType: function(event) {
+ var value = elData(event.currentTarget, 'value');
+ var groupId = ~~elData(event.currentTarget.parentNode, 'object-id');
+
+ var data = _data.get(groupId);
+ data.mailValue.value = value;
+ elBySel('span.title', data.mailSetting).textContent = Language.get('wcf.user.notification.mailNotificationType.' + value);
+
+ data.button.classList[(value === 'none') ? 'remove' : 'add']('yellow');
+ data.button.classList[(value === 'none') ? 'remove' : 'add']('active');
+ }
+ };
+
+ return ControllerUserNotificationSettings;
+});
+
+/**
+ * Data handler for a captcha form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Captcha
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Captcha',['Core', './Field', 'WoltLabSuite/Core/Controller/Captcha'], function(Core, FormBuilderField, Captcha) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldCaptcha(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldCaptcha, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#getData
+ */
+ _getData: function() {
+ if (Captcha.has(this._fieldId)) {
+ return Captcha.getData(this._fieldId);
+ }
+
+ return {};
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ // does nothing
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#destroy
+ */
+ destroy: function() {
+ if (Captcha.has(this._fieldId)) {
+ Captcha.delete(this._fieldId);
+ }
+ }
+ });
+
+ return FormBuilderFieldCaptcha;
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form represented by checkboxes.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Checkboxes
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Checkboxes',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldCheckboxes(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldCheckboxes, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = [];
+
+ for (var i = 0, length = this._fields.length; i < length; i++) {
+ if (this._fields[i].checked) {
+ data[this._fieldId].push(this._fields[i].value);
+ }
+ }
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ this._fields = elBySelAll('input[name="' + this._fieldId + '[]"]');
+ }
+ });
+
+ return FormBuilderFieldCheckboxes;
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form that stores its value via a checkbox being
+ * checked or not.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Checked
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Checked',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldInput(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldInput, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = ~~this._field.checked;
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldInput;
+});
+
+/**
+ * Data handler for a date form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Date
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Date',['Core', 'WoltLabSuite/Core/Date/Picker', './Field'], function(Core, DatePicker, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldDate(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldDate, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = DatePicker.getValue(this._field);
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldDate;
+});
+
+/**
+ * Data handler for an item list form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/ItemList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/ItemList',['Core', './Field', 'WoltLabSuite/Core/Ui/ItemList/Static'], function(Core, FormBuilderField, UiItemListStatic) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldItemList(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldItemList, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+ data[this._fieldId] = [];
+
+ var values = UiItemListStatic.getValues(this._fieldId);
+ for (var i = 0, length = values.length; i < length; i++) {
+ // TODO: data[this._fieldId] is an array but if code assumes object
+ if (values[i].objectId) {
+ data[this._fieldId][values[i].objectId] = values[i].value;
+ }
+ else {
+ data[this._fieldId].push(values[i].value);
+ }
+ }
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldItemList;
+});
+
+/**
+ * Data handler for a radio button form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/RadioButton
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/RadioButton',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldRadioButton(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldRadioButton, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#getData
+ */
+ _getData: function() {
+ var data = {};
+
+ for (var i = 0, length = this._fields.length; i < length; i++) {
+ if (this._fields[i].checked) {
+ data[this._fieldId] = this._fields[i].value;
+ break;
+ }
+ }
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ this._fields = elBySelAll('input[name=' + this._fieldId + ']');
+ },
+ });
+
+ return FormBuilderFieldRadioButton;
+});
+
+/**
+ * Data handler for a simple acl form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/SimpleAcl
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/SimpleAcl',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldSimpleAcl(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldSimpleAcl, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var groupIds = [];
+ elBySelAll('input[name="' + this._fieldId + '[group][]"]', undefined, function(input) {
+ groupIds.push(~~input.value);
+ });
+
+ var usersIds = [];
+ elBySelAll('input[name="' + this._fieldId + '[user][]"]', undefined, function(input) {
+ usersIds.push(~~input.value);
+ });
+
+ var data = {};
+
+ data[this._fieldId] = {
+ group: groupIds,
+ user: usersIds
+ };
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ // does nothing
+ }
+ });
+
+ return FormBuilderFieldSimpleAcl;
+});
+
+/**
+ * Data handler for a tag form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Tag
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Tag',['Core', './Field', 'WoltLabSuite/Core/Ui/ItemList'], function(Core, FormBuilderField, UiItemList) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldTag(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldTag, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+ data[this._fieldId] = [];
+
+ var values = UiItemList.getValues(this._fieldId);
+ for (var i = 0, length = values.length; i < length; i++) {
+ data[this._fieldId].push(values[i].value);
+ }
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldTag;
+});
+
+/**
+ * Data handler for a user form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/User
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/User',['Core', './Field', 'WoltLabSuite/Core/Ui/ItemList'], function(Core, FormBuilderField, UiItemList) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldUser(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldUser, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var values = UiItemList.getValues(this._fieldId);
+ var usernames = [];
+ for (var i = 0, length = values.length; i < length; i++) {
+ if (values[i].objectId) {
+ usernames.push(values[i].value);
+ }
+ }
+
+ var data = {};
+ data[this._fieldId] = usernames.join(',');
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldUser;
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form that stores its value in an input's value
+ * attribute.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Value
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Value',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldValue(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldValue, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = this._field.value;
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldValue;
+});
+
+/**
+ * Data handler for an i18n form builder field in an Ajax form that stores its value in an input's
+ * value attribute.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/ValueI18n
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/ValueI18n',['Core', './Field', 'WoltLabSuite/Core/Language/Input'], function(Core, FormBuilderField, LanguageInput) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldValueI18n(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldValueI18n, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ var values = LanguageInput.getValues(this._fieldId);
+ if (values.size > 1) {
+ data[this._fieldId + '_i18n'] = values.toObject();
+ }
+ else {
+ data[this._fieldId] = values.get(0);
+ }
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#destroy
+ */
+ destroy: function() {
+ LanguageInput.unregister(this._fieldId);
+ }
+ });
+
+ return FormBuilderFieldValueI18n;
+});
+
+/**
+ * Handles the comment response add feature.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Add
+ */
+define('WoltLabSuite/Core/Ui/Comment/Response/Add',[
+ 'Core', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Comment/Add'
+],
+function(
+ Core, Language, DomChangeListener, DomUtil, DomTraverse, UiNotification, UiCommentAdd
+) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ getContainer: function() {},
+ getContent: function() {},
+ setContent: function() {},
+ _submitGuestDialog: function() {},
+ _submit: function() {},
+ _getParameters: function () {},
+ _validate: function() {},
+ throwError: function() {},
+ _showLoadingOverlay: function() {},
+ _hideLoadingOverlay: function() {},
+ _reset: function() {},
+ _handleError: function() {},
+ _getEditor: function() {},
+ _insertMessage: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentResponseAdd(container, options) { this.init(container, options); }
+ Core.inherit(UiCommentResponseAdd, UiCommentAdd, {
+ init: function (container, options) {
+ UiCommentResponseAdd._super.prototype.init.call(this, container);
+
+ this._options = Core.extend({
+ callbackInsert: null
+ }, options);
+ },
+
+ /**
+ * Returns the editor container for placement or `null` if the editor is busy.
+ *
+ * @return {(Element|null)}
+ */
+ getContainer: function() {
+ return (this._isBusy) ? null : this._container;
+ },
+
+ /**
+ * Retrieves the current content from the editor.
+ *
+ * @return {string}
+ */
+ getContent: function () {
+ return window.jQuery(this._textarea).redactor('code.get');
+ },
+
+ /**
+ * Sets the content and places the caret at the end of the editor.
+ *
+ * @param {string} html
+ */
+ setContent: function (html) {
+ window.jQuery(this._textarea).redactor('code.set', html);
+ window.jQuery(this._textarea).redactor('WoltLabCaret.endOfEditor');
+
+ // the error message can appear anywhere in the container, not exclusively after the textarea
+ var innerError = elBySel('.innerError', this._textarea.parentNode);
+ if (innerError !== null) elRemove(innerError);
+
+ this._content.classList.remove('collapsed');
+ this._focusEditor();
+ },
+
+ _getParameters: function () {
+ var parameters = UiCommentResponseAdd._super.prototype._getParameters.call(this);
+ parameters.data.commentID = ~~elData(this._container.closest('.comment'), 'object-id');
+
+ return parameters;
+ },
+
+ _insertMessage: function(data) {
+ var commentContent = DomTraverse.childByClass(this._container.parentNode, 'commentContent');
+ var responseList = commentContent.nextElementSibling;
+ if (responseList === null || !responseList.classList.contains('commentResponseList')) {
+ responseList = elCreate('ul');
+ responseList.className = 'containerList commentResponseList';
+ elData(responseList, 'responses', 0);
+
+ commentContent.parentNode.insertBefore(responseList, commentContent.nextSibling);
+ }
+
+ // insert HTML
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.insertHtml(data.returnValues.template, responseList, 'append');
+
+ UiNotification.show(Language.get('wcf.global.success.add'));
+
+ DomChangeListener.trigger();
+
+ // reset editor
+ window.jQuery(this._textarea).redactor('code.set', '');
+
+ if (this._options.callbackInsert !== null) this._options.callbackInsert();
+
+ // update counter
+ elData(responseList, 'responses', responseList.children.length);
+
+ return responseList.lastElementChild;
+ },
+
+ _ajaxSetup: function() {
+ var data = UiCommentResponseAdd._super.prototype._ajaxSetup.call(this);
+ data.data.actionName = 'addResponse';
+
+ return data;
+ }
+ });
+
+ return UiCommentResponseAdd;
+});
+
+/**
+ * Provides editing support for comment responses.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Response/Edit
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Comment/Response/Edit',[
+ 'Ajax', 'Core', 'Dictionary', 'Environment',
+ 'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll', 'WoltLabSuite/Core/Ui/Comment/Edit'
+ ],
+ function(
+ Ajax, Core, Dictionary, Environment,
+ EventHandler, Language, List, DomChangeListener, DomTraverse,
+ DomUtil, UiNotification, UiReusableDropdown, UiScroll, UiCommentEdit
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ _click: function() {},
+ _prepare: function() {},
+ _showEditor: function() {},
+ _restoreMessage: function() {},
+ _save: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showMessage: function() {},
+ _hideEditor: function() {},
+ _restoreEditor: function() {},
+ _destroyEditor: function() {},
+ _getEditorId: function() {},
+ _getObjectId: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentResponseEdit(container) { this.init(container); }
+ Core.inherit(UiCommentResponseEdit, UiCommentEdit, {
+ /**
+ * Initializes the comment edit manager.
+ *
+ * @param {Element} container container element
+ */
+ init: function(container) {
+ this._activeElement = null;
+ this._callbackClick = null;
+ this._container = container;
+ this._editorContainer = null;
+ this._responses = new List();
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Comment/Response/Edit_' + DomUtil.identify(this._container), this.rebuild.bind(this));
+ },
+
+ /**
+ * Initializes each applicable message, should be called whenever new
+ * messages are being displayed.
+ */
+ rebuild: function() {
+ elBySelAll('.commentResponse', this._container, (function (response) {
+ if (this._responses.has(response)) {
+ return;
+ }
+
+ if (elDataBool(response, 'can-edit')) {
+ var button = elBySel('.jsCommentResponseEditButton', response);
+ if (button !== null) {
+ if (this._callbackClick === null) {
+ this._callbackClick = this._click.bind(this);
+ }
+
+ button.addEventListener(WCF_CLICK_EVENT, this._callbackClick);
+ }
+ }
+
+ this._responses.add(response);
+ }).bind(this));
+ },
+
+ /**
+ * Handles clicks on the edit button.
+ *
+ * @param {?Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ if (this._activeElement === null) {
+ this._activeElement = event.currentTarget.closest('.commentResponse');
+
+ this._prepare();
+
+ Ajax.api(this, {
+ actionName: 'beginEdit',
+ objectIDs: [this._getObjectId(this._activeElement)]
+ });
+ }
+ else {
+ UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+ }
+ },
+
+ /**
+ * Prepares the message for editor display.
+ *
+ * @protected
+ */
+ _prepare: function() {
+ this._editorContainer = elCreate('div');
+ this._editorContainer.className = 'commentEditorContainer';
+ this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
+
+ var content = elBySel('.commentResponseContent', this._activeElement);
+ content.insertBefore(this._editorContainer, content.firstChild);
+ },
+
+ /**
+ * Shows the update message.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showMessage: function(data) {
+ // set new content
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.setInnerHtml(elBySel('.commentResponseContent .userMessage', this._editorContainer.parentNode), data.returnValues.message);
+
+ this._restoreMessage();
+
+ UiNotification.show();
+ },
+
+ /**
+ * Returns the unique editor id.
+ *
+ * @return {string} editor id
+ * @protected
+ */
+ _getEditorId: function() {
+ return 'commentResponseEditor' + this._getObjectId(this._activeElement);
+ },
+
+ _ajaxSetup: function() {
+ var objectTypeId = ~~elData(this._container, 'object-type-id');
+
+ return {
+ data: {
+ className: 'wcf\\data\\comment\\response\\CommentResponseAction',
+ parameters: {
+ data: {
+ objectTypeID: objectTypeId
+ }
+ }
+ },
+ silent: true
+ };
+ }
+ });
+
+ return UiCommentResponseEdit;
+});
+
+/**
+ * Manages the sticky page header.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Header/Fixed
+ */
+define('WoltLabSuite/Core/Ui/Page/Header/Fixed',['Core', 'EventHandler', 'Ui/Alignment', 'Ui/CloseOverlay', 'Ui/SimpleDropdown', 'Ui/Screen'], function(Core, EventHandler, UiAlignment, UiCloseOverlay, UiSimpleDropdown, UiScreen) {
+ "use strict";
+
+ var _pageHeader, _pageHeaderContainer, _pageHeaderPanel, _pageHeaderSearch, _searchInput, _topMenu, _userPanelSearchButton;
+ var _isMobile = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Header/Fixed
+ */
+ return {
+ /**
+ * Initializes the sticky page header handler.
+ */
+ init: function() {
+ _pageHeader = elById('pageHeader');
+ _pageHeaderContainer = elById('pageHeaderContainer');
+
+ this._initSearchBar();
+
+ UiScreen.on('screen-md-down', {
+ match: function () { _isMobile = true; },
+ unmatch: function () { _isMobile = false; },
+ setup: function () { _isMobile = true; }
+ });
+
+ EventHandler.add('com.woltlab.wcf.Search', 'close', this._closeSearchBar.bind(this));
+ },
+
+ /**
+ * Provides the collapsible search bar.
+ *
+ * @protected
+ */
+ _initSearchBar: function() {
+ _pageHeaderSearch = elById('pageHeaderSearch');
+ _pageHeaderSearch.addEventListener(WCF_CLICK_EVENT, function(event) { event.stopPropagation(); });
+
+ _pageHeaderPanel = elById('pageHeaderPanel');
+ _searchInput = elById('pageHeaderSearchInput');
+ _topMenu = elById('topMenu');
+
+ _userPanelSearchButton = elById('userPanelSearchButton');
+ _userPanelSearchButton.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (_pageHeader.classList.contains('searchBarOpen')) {
+ this._closeSearchBar();
+ }
+ else {
+ this._openSearchBar();
+ }
+ }).bind(this));
+
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Page/Header/Fixed', (function() {
+ if (_pageHeader.classList.contains('searchBarForceOpen')) return;
+
+ this._closeSearchBar();
+ }).bind(this));
+
+ EventHandler.add('com.woltlab.wcf.MainMenuMobile', 'more', (function(data) {
+ if (data.identifier === 'com.woltlab.wcf.search') {
+ data.handler.close(true);
+
+ Core.triggerEvent(_userPanelSearchButton, WCF_CLICK_EVENT);
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Opens the search bar.
+ *
+ * @protected
+ */
+ _openSearchBar: function() {
+ window.WCF.Dropdown.Interactive.Handler.closeAll();
+
+ _pageHeader.classList.add('searchBarOpen');
+ _userPanelSearchButton.parentNode.classList.add('open');
+
+ if (!_isMobile) {
+ // calculate value for `right` on desktop
+ UiAlignment.set(_pageHeaderSearch, _topMenu, {
+ horizontal: 'right'
+ });
+ }
+
+ _pageHeaderSearch.style.setProperty('top', _pageHeaderPanel.clientHeight + 'px', '');
+ _searchInput.focus();
+ window.setTimeout(function() {
+ _searchInput.selectionStart = _searchInput.selectionEnd = _searchInput.value.length;
+ }, 1);
+ },
+
+ /**
+ * Closes the search bar.
+ *
+ * @protected
+ */
+ _closeSearchBar: function () {
+ _pageHeader.classList.remove('searchBarOpen');
+ _userPanelSearchButton.parentNode.classList.remove('open');
+
+ ['bottom', 'left', 'right', 'top'].forEach(function(propertyName) {
+ _pageHeaderSearch.style.removeProperty(propertyName);
+ });
+
+ _searchInput.blur();
+
+ // close the scope selection
+ var scope = elBySel('.pageHeaderSearchType', _pageHeaderSearch);
+ UiSimpleDropdown.close(scope.id);
+ }
+ };
+});
+
+/**
+ * Suggestions for page object ids with external response data processing.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Search/Input
+ * @extends module:WoltLabSuite/Core/Ui/Search/Input
+ */
+define('WoltLabSuite/Core/Ui/Page/Search/Input',['Core', 'WoltLabSuite/Core/Ui/Search/Input'], function(Core, UiSearchInput) {
+ "use strict";
+
+ /**
+ * @param {Element} element input element
+ * @param {Object=} options search options and settings
+ * @constructor
+ */
+ function UiPageSearchInput(element, options) { this.init(element, options); }
+ Core.inherit(UiPageSearchInput, UiSearchInput, {
+ init: function(element, options) {
+ options = Core.extend({
+ ajax: {
+ className: 'wcf\\data\\page\\PageAction'
+ },
+ callbackSuccess: null
+ }, options);
+
+ if (typeof options.callbackSuccess !== 'function') {
+ throw new Error("Expected a valid callback function for 'callbackSuccess'.");
+ }
+
+ UiPageSearchInput._super.prototype.init.call(this, element, options);
+
+ this._pageId = 0;
+ },
+
+ /**
+ * Sets the target page id.
+ *
+ * @param {int} pageId target page id
+ */
+ setPageId: function(pageId) {
+ this._pageId = pageId;
+ },
+
+ _getParameters: function(value) {
+ var data = UiPageSearchInput._super.prototype._getParameters.call(this, value);
+
+ data.objectIDs = [this._pageId];
+
+ return data;
+ },
+
+ _ajaxSuccess: function(data) {
+ this._options.callbackSuccess(data);
+ }
+ });
+
+ return UiPageSearchInput;
+});
+
+/**
+ * Provides access to the lookup function of page handlers, allowing the user to search and
+ * select page object ids.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Search/Handler
+ */
+define('WoltLabSuite/Core/Ui/Page/Search/Handler',['Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './Input'], function(Language, StringUtil, DomUtil, UiDialog, UiPageSearchInput) {
+ "use strict";
+
+ var _callback = null;
+ var _searchInput = null;
+ var _searchInputLabel = null;
+ var _searchInputHandler = null;
+ var _resultList = null;
+ var _resultListContainer = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Search/Handler
+ */
+ return {
+ /**
+ * Opens the lookup overlay for provided page id.
+ *
+ * @param {int} pageId page id
+ * @param {string} title dialog title
+ * @param {function} callback callback function provided with the user-selected object id
+ * @param {string?} labelLanguageItem optional language item name for the search input label
+ */
+ open: function (pageId, title, callback, labelLanguageItem) {
+ _callback = callback;
+
+ UiDialog.open(this);
+ UiDialog.setTitle(this, title);
+
+ if (labelLanguageItem) {
+ _searchInputLabel.textContent = Language.get(labelLanguageItem);
+ }
+ else {
+ _searchInputLabel.textContent = Language.get('wcf.page.pageObjectID.search.terms');
+ }
+
+ this._getSearchInputHandler().setPageId(pageId);
+ },
+
+ /**
+ * Builds the result list.
+ *
+ * @param {Object} data AJAX response data
+ * @protected
+ */
+ _buildList: function(data) {
+ this._resetList();
+
+ // no matches
+ if (!Array.isArray(data.returnValues) || data.returnValues.length === 0) {
+ elInnerError(_searchInput, Language.get('wcf.page.pageObjectID.search.noResults'));
+
+ return;
+ }
+
+ var image, item, listItem;
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ item = data.returnValues[i];
+ image = item.image;
+ if (/^fa-/.test(image)) {
+ image = '<span class="icon icon48 ' + image + ' pointer jsTooltip" title="' + Language.get('wcf.global.select') + '"></span>';
+ }
+
+ listItem = elCreate('li');
+ elData(listItem, 'object-id', item.objectID);
+
+ listItem.innerHTML = '<div class="box48">'
+ + image
+ + '<div>'
+ + '<div class="containerHeadline">'
+ + '<h3><a href="' + StringUtil.escapeHTML(item.link) + '">' + StringUtil.escapeHTML(item.title) + '</a></h3>'
+ + (item.description ? '<p>' + item.description + '</p>' : '')
+ + '</div>'
+ + '</div>'
+ + '</div>';
+
+ listItem.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+
+ _resultList.appendChild(listItem);
+ }
+
+ elShow(_resultListContainer);
+ },
+
+ /**
+ * Resets the list and removes any error elements.
+ *
+ * @protected
+ */
+ _resetList: function() {
+ elInnerError(_searchInput, false);
+
+ _resultList.innerHTML = '';
+
+ elHide(_resultListContainer);
+ },
+
+ /**
+ * Initializes the search input handler and returns the instance.
+ *
+ * @returns {UiPageSearchInput} search input handler
+ * @protected
+ */
+ _getSearchInputHandler: function() {
+ if (_searchInputHandler === null) {
+ var callback = this._buildList.bind(this);
+ _searchInputHandler = new UiPageSearchInput(elById('wcfUiPageSearchInput'), {
+ callbackSuccess: callback
+ });
+ }
+
+ return _searchInputHandler;
+ },
+
+ /**
+ * Handles clicks on the item unless the click occurred directly on a link.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ if (event.target.nodeName === 'A') {
+ return;
+ }
+
+ event.stopPropagation();
+
+ _callback(elData(event.currentTarget, 'object-id'));
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiPageSearchHandler',
+ options: {
+ onShow: function() {
+ if (_searchInput === null) {
+ _searchInput = elById('wcfUiPageSearchInput');
+ _searchInputLabel = _searchInput.parentNode.previousSibling.childNodes[0];
+ _resultList = elById('wcfUiPageSearchResultList');
+ _resultListContainer = elById('wcfUiPageSearchResultListContainer');
+ }
+
+ // clear search input
+ _searchInput.value = '';
+
+ // reset results
+ elHide(_resultListContainer);
+ _resultList.innerHTML = '';
+
+ _searchInput.focus();
+ },
+ title: ''
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiPageSearchInput">' + Language.get('wcf.page.pageObjectID.search.terms') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="wcfUiPageSearchInput" class="long">'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList">'
+ + '<header class="sectionHeader">'
+ + '<h2 class="sectionTitle">' + Language.get('wcf.page.pageObjectID.search.results') + '</h2>'
+ + '</header>'
+ + '<ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul>'
+ + '</section>'
+ };
+ }
+ };
+});
+
+/**
+ * Handles the reaction list in the user profile.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Reaction/Profile/Loader
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/Reaction/Profile/Loader',['Ajax', 'Core', 'Language'], function(Ajax, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiReactionProfileLoader(userID, firstReactionTypeID) { this.init(userID, firstReactionTypeID); }
+ UiReactionProfileLoader.prototype = {
+ /**
+ * Initializes a new ReactionListLoader object.
+ *
+ * @param integer userID
+ */
+ init: function(userID, firstReactionTypeID) {
+ this._container = elById('likeList');
+ this._userID = userID;
+ this._reactionTypeID = firstReactionTypeID;
+ this._targetType = 'received';
+ this._options = {
+ parameters: []
+ };
+
+ if (!this._userID) {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Profile/Loader] Invalid parameter 'userID' given.");
+ }
+
+ if (!this._reactionTypeID) {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Profile/Loader] Invalid parameter 'firstReactionTypeID' given.");
+ }
+
+ var loadButtonList = elCreate('li');
+ loadButtonList.className = 'likeListMore showMore';
+ this._noMoreEntries = elCreate('small');
+ this._noMoreEntries.innerHTML = Language.get('wcf.like.reaction.noMoreEntries');
+ this._noMoreEntries.style.display = 'none';
+ loadButtonList.appendChild(this._noMoreEntries);
+
+ this._loadButton = elCreate('button');
+ this._loadButton.className = 'small';
+ this._loadButton.innerHTML = Language.get('wcf.like.reaction.more');
+ this._loadButton.addEventListener(WCF_CLICK_EVENT, this._loadReactions.bind(this));
+ this._loadButton.style.display = 'none';
+ loadButtonList.appendChild(this._loadButton);
+ this._container.appendChild(loadButtonList);
+
+ if (elBySel('#likeList > li').length === 2) {
+ this._noMoreEntries.style.display = '';
+ }
+ else {
+ this._loadButton.style.display = '';
+ }
+
+ this._setupReactionTypeButtons();
+ this._setupTargetTypeButtons();
+ },
+
+ /**
+ * Set up the reaction type buttons.
+ */
+ _setupReactionTypeButtons: function() {
+ var element, elements = elBySelAll('#reactionType .button');
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ element.addEventListener(WCF_CLICK_EVENT, this._changeReactionTypeValue.bind(this, ~~elData(element, 'reaction-type-id')));
+ }
+ },
+
+ /**
+ * Set up the target type buttons.
+ */
+ _setupTargetTypeButtons: function() {
+ var element, elements = elBySelAll('#likeType .button');
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ element.addEventListener(WCF_CLICK_EVENT, this._changeTargetType.bind(this, elData(element, 'like-type')));
+ }
+ },
+
+ /**
+ * Changes the reaction target type (given or received) and reload the entire element.
+ *
+ * @param {string} targetType
+ */
+ _changeTargetType: function(targetType) {
+ if (targetType !== 'given' && targetType !== 'received') {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Profile/Loader] Invalid parameter 'targetType' given.");
+ }
+
+ if (targetType !== this._targetType) {
+ // remove old active state
+ elBySel('#likeType .button.active').classList.remove('active');
+
+ // add active status to new button
+ elBySel('#likeType .button[data-like-type="'+ targetType +'"]').classList.add('active');
+
+ this._targetType = targetType;
+ this._reload();
+ }
+ },
+
+ /**
+ * Changes the reaction type value and reload the entire element.
+ *
+ * @param {int} reactionTypeID
+ */
+ _changeReactionTypeValue: function(reactionTypeID) {
+ if (this._reactionTypeID !== reactionTypeID) {
+ // remove old active state
+ elBySel('#reactionType .button.active').classList.remove('active');
+
+ // add active status to new button
+ elBySel('#reactionType .button[data-reaction-type-id="'+ reactionTypeID +'"]').classList.add('active');
+
+ this._reactionTypeID = reactionTypeID;
+ this._reload();
+ }
+ },
+
+ /**
+ * Handles reload.
+ */
+ _reload: function() {
+ var elements = elBySelAll('#likeList > li:not(:first-child):not(:last-child)');
+
+ for (var i = 0, length = elements.length; i < length; i++) {
+ this._container.removeChild(elements[i]);
+ }
+
+ elData(this._container, 'last-like-time', 0);
+
+ this._loadReactions();
+ },
+
+ /**
+ * Load a list of reactions.
+ */
+ _loadReactions: function() {
+ this._options.parameters.userID = this._userID;
+ this._options.parameters.lastLikeTime = elData(this._container, 'last-like-time');
+ this._options.parameters.targetType = this._targetType;
+ this._options.parameters.reactionTypeID = this._reactionTypeID;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.template) {
+ elBySel('#likeList > li:nth-last-child(1)').insertAdjacentHTML('beforebegin', data.returnValues.template);
+
+ elData(this._container, 'last-like-time', data.returnValues.lastLikeTime);
+ this._noMoreEntries.style.display = 'none';
+ this._loadButton.style.display = '';
+ }
+ else {
+ this._noMoreEntries.style.display = '';
+ this._loadButton.style.display = 'none';
+ }
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'load',
+ className: '\\wcf\\data\\reaction\\ReactionAction'
+ }
+ };
+ }
+ };
+
+ return UiReactionProfileLoader;
+});
+
+define('WoltLabSuite/Core/Ui/User/Activity/Recent',['Ajax', 'Language', 'Dom/Util'], function(Ajax, Language, DomUtil) {
+ "use strict";
+
+ function UiUserActivityRecent(containerId) { this.init(containerId); }
+ UiUserActivityRecent.prototype = {
+ init: function (containerId) {
+ this._containerId = containerId;
+ var container = elById(this._containerId);
+ this._list = elBySel('.recentActivityList', container);
+
+ var showMoreItem = elCreate('li');
+ showMoreItem.className = 'showMore';
+ if (this._list.childElementCount) {
+ showMoreItem.innerHTML = '<button class="small">' + Language.get('wcf.user.recentActivity.more') + '</button>';
+ showMoreItem.children[0].addEventListener(WCF_CLICK_EVENT, this._showMore.bind(this));
+ }
+ else {
+ showMoreItem.innerHTML = '<small>' + Language.get('wcf.user.recentActivity.noMoreEntries') + '</small>';
+ }
+
+ this._list.appendChild(showMoreItem);
+ this._showMoreItem = showMoreItem;
+
+ elBySelAll('.jsRecentActivitySwitchContext .button', container, (function (button) {
+ button.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ if (!button.classList.contains('active')) {
+ this._switchContext();
+ }
+ }).bind(this));
+ }).bind(this));
+ },
+
+ _showMore: function (event) {
+ event.preventDefault();
+
+ this._showMoreItem.children[0].disabled = true;
+
+ Ajax.api(this, {
+ actionName: 'load',
+ parameters: {
+ boxID: ~~elData(this._list, 'box-id'),
+ filteredByFollowedUsers: elDataBool(this._list, 'filtered-by-followed-users'),
+ lastEventId: elData(this._list, 'last-event-id'),
+ lastEventTime: elData(this._list, 'last-event-time'),
+ userID: ~~elData(this._list, 'user-id')
+ }
+ });
+ },
+
+ _switchContext: function() {
+ Ajax.api(
+ this,
+ {
+ actionName: 'switchContext'
+ },
+ (function () {
+ window.location.hash = '#' + this._containerId;
+ window.location.reload();
+ }).bind(this)
+ );
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.template) {
+ DomUtil.insertHtml(data.returnValues.template, this._showMoreItem, 'before');
+
+ elData(this._list, 'last-event-time', data.returnValues.lastEventTime);
+ elData(this._list, 'last-event-id', data.returnValues.lastEventID);
+
+ this._showMoreItem.children[0].disabled = false;
+ }
+ else {
+ this._showMoreItem.innerHTML = '<small>' + Language.get('wcf.user.recentActivity.noMoreEntries') + '</small>';
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\activity\\event\\UserActivityEventAction'
+ }
+ };
+ }
+ };
+
+ return UiUserActivityRecent;
+});
+
+/**
+ * Deletes the current user cover photo.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/CoverPhoto/Delete
+ */
+define('WoltLabSuite/Core/Ui/User/CoverPhoto/Delete',['Ajax', 'EventHandler', 'Language', 'Ui/Confirmation', 'Ui/Notification'], function (Ajax, EventHandler, Language, UiConfirmation, UiNotification) {
+ "use strict";
+
+ var _button;
+ var _userId = 0;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/User/CoverPhoto/Delete
+ */
+ return {
+ /**
+ * Initializes the delete handler and enables the delete button on upload.
+ */
+ init: function (userId) {
+ _button = elBySel('.jsButtonDeleteCoverPhoto');
+ _button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ _userId = userId;
+
+ EventHandler.add('com.woltlab.wcf.user', 'coverPhoto', function (data) {
+ if (typeof data.url === 'string' && data.url.length > 0) {
+ elShow(_button.parentNode);
+ }
+ });
+ },
+
+ /**
+ * Handles clicks on the delete button.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _click: function (event) {
+ event.preventDefault();
+
+ UiConfirmation.show({
+ confirm: Ajax.api.bind(Ajax, this),
+ message: Language.get('wcf.user.coverPhoto.delete.confirmMessage')
+ });
+ },
+
+ _ajaxSuccess: function (data) {
+ elBySel('.userProfileCoverPhoto').style.setProperty('background-image', 'url(' + data.returnValues.url + ')', '');
+
+ elHide(_button.parentNode);
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ actionName: 'deleteCoverPhoto',
+ className: 'wcf\\data\\user\\UserProfileAction',
+ parameters: {
+ userID: _userId
+ }
+ }
+ };
+ }
+ };
+});
+
+/**
+ * Uploads the user cover photo via AJAX.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/CoverPhoto/Upload
+ */
+define('WoltLabSuite/Core/Ui/User/CoverPhoto/Upload',['Core', 'EventHandler', 'Upload', 'Ui/Notification', 'Ui/Dialog'], function(Core, EventHandler, Upload, UiNotification, UiDialog) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiUserCoverPhotoUpload(userId) {
+ Upload.call(this, 'coverPhotoUploadButtonContainer', 'coverPhotoUploadPreview', {
+ action: 'uploadCoverPhoto',
+ className: 'wcf\\data\\user\\UserProfileAction'
+ });
+
+ this._userId = userId;
+ }
+ Core.inherit(UiUserCoverPhotoUpload, Upload, {
+ _getParameters: function() {
+ return {
+ userID: this._userId
+ };
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_success
+ */
+ _success: function(uploadId, data) {
+ // remove or display the error message
+ elInnerError(this._button, data.returnValues.errorMessage);
+
+ // remove the upload progress
+ this._target.innerHTML = '';
+
+ if (data.returnValues.url) {
+ elBySel('.userProfileCoverPhoto').style.setProperty('background-image', 'url(' + data.returnValues.url + ')', '');
+
+ UiDialog.close('userProfileCoverPhotoUpload');
+ UiNotification.show();
+
+ EventHandler.fire('com.woltlab.wcf.user', 'coverPhoto', {
+ url: data.returnValues.url
+ });
+ }
+ }
+ });
+
+ return UiUserCoverPhotoUpload;
+});
+
+/**
+ * Handles the user trophy dialog.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Trophy/List
+ */
+define('WoltLabSuite/Core/Ui/User/Trophy/List',['Ajax', 'Core', 'Dictionary', 'Dom/Util', 'Ui/Dialog', 'WoltLabSuite/Core/Ui/Pagination', 'Dom/ChangeListener', 'List'], function(Ajax, Core, Dictionary, DomUtil, UiDialog, UiPagination, DomChangeListener, List) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiUserTrophyList() { this.init(); }
+ UiUserTrophyList.prototype = {
+ /**
+ * Initializes the user trophy list.
+ */
+ init: function() {
+ this._cache = new Dictionary();
+ this._knownElements = new List();
+
+ this._options = {
+ className: 'wcf\\data\\user\\trophy\\UserTrophyAction',
+ parameters: {}
+ };
+
+ this._rebuild();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/User/Trophy/List', this._rebuild.bind(this));
+ },
+
+ /**
+ * Adds event userTrophyOverlayList elements.
+ */
+ _rebuild: function() {
+ elBySelAll('.userTrophyOverlayList', undefined, (function (element) {
+ if (!this._knownElements.has(element)) {
+ element.addEventListener(WCF_CLICK_EVENT, this._open.bind(this, elData(element, 'user-id')));
+
+ this._knownElements.add(element);
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Opens the user trophy list for a specific user.
+ *
+ * @param {int} userId
+ * @param {Event} event event object
+ */
+ _open: function(userId, event) {
+ event.preventDefault();
+
+ this._currentPageNo = 1;
+ this._currentUser = userId;
+ this._showPage();
+ },
+
+ /**
+ * Shows the current or given page.
+ *
+ * @param {int=} pageNo page number
+ */
+ _showPage: function(pageNo) {
+ if (pageNo !== undefined) {
+ this._currentPageNo = pageNo;
+ }
+
+ if (this._cache.has(this._currentUser)) {
+ // validate pageNo
+ if (this._cache.get(this._currentUser).get('pageCount') !== 0 && (this._currentPageNo < 1 || this._currentPageNo > this._cache.get(this._currentUser).get('pageCount'))) {
+ throw new RangeError("pageNo must be between 1 and " + this._cache.get(this._currentUser).get('pageCount') + " (" + this._currentPageNo + " given).");
+ }
+ }
+ else {
+ // init user page cache
+ this._cache.set(this._currentUser, new Dictionary());
+ }
+
+ if (this._cache.get(this._currentUser).has(this._currentPageNo)) {
+ var dialog = UiDialog.open(this, this._cache.get(this._currentUser).get(this._currentPageNo));
+ UiDialog.setTitle('userTrophyListOverlay', this._cache.get(this._currentUser).get('title'));
+
+ if (this._cache.get(this._currentUser).get('pageCount') > 1) {
+ var element = elBySel('.jsPagination', dialog.content);
+ if (element !== null) {
+ new UiPagination(element, {
+ activePage: this._currentPageNo,
+ maxPage: this._cache.get(this._currentUser).get('pageCount'),
+ callbackSwitch: this._showPage.bind(this)
+ });
+ }
+ }
+ }
+ else {
+ this._options.parameters.pageNo = this._currentPageNo;
+ this._options.parameters.userID = this._currentUser;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.pageCount !== undefined) {
+ this._cache.get(this._currentUser).set('pageCount', ~~data.returnValues.pageCount);
+ }
+
+ this._cache.get(this._currentUser).set(this._currentPageNo, data.returnValues.template);
+ this._cache.get(this._currentUser).set('title', data.returnValues.title);
+ this._showPage();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getGroupedUserTrophyList',
+ className: this._options.className
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'userTrophyListOverlay',
+ options: {
+ title: ""
+ },
+ source: null
+ };
+ }
+ };
+
+ return UiUserTrophyList;
+});
+
+/**
+ * Handles the JavaScript part of the label form field.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Controller/Label
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Controller/Label',['Core', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], function(Core, DomUtil, Language, UiSimpleDropdown) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldLabel(fielId, labelId, options) {
+ this.init(fielId, labelId, options);
+ };
+ FormBuilderFieldLabel.prototype = {
+ /**
+ * Initializes the label form field.
+ *
+ * @param {string} fieldId id of the relevant form builder field
+ * @param {integer} labelId id of the currently selected label
+ * @param {object} options additional label options
+ */
+ init: function(fieldId, labelId, options) {
+ this._formFieldContainer = elById(fieldId + 'Container');
+ this._labelChooser = elByClass('labelChooser', this._formFieldContainer)[0];
+ this._options = Core.extend({
+ forceSelection: false,
+ showWithoutSelection: false
+ }, options);
+
+ this._input = elCreate('input');
+ this._input.type = 'hidden';
+ this._input.id = fieldId;
+ this._input.name = fieldId;
+ this._input.value = ~~labelId;
+ this._formFieldContainer.appendChild(this._input);
+
+ var labelChooserId = DomUtil.identify(this._labelChooser);
+
+ // init dropdown
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(labelChooserId);
+ if (dropdownMenu === null) {
+ UiSimpleDropdown.init(elByClass('dropdownToggle', this._labelChooser)[0]);
+ dropdownMenu = UiSimpleDropdown.getDropdownMenu(labelChooserId);
+ }
+
+ var additionalOptionList = null;
+ if (this._options.showWithoutSelection || !this._options.forceSelection) {
+ additionalOptionList = elCreate('ul');
+ dropdownMenu.appendChild(additionalOptionList);
+
+ var dropdownDivider = elCreate('li');
+ dropdownDivider.className = 'dropdownDivider';
+ additionalOptionList.appendChild(dropdownDivider);
+ }
+
+ if (this._options.showWithoutSelection) {
+ var listItem = elCreate('li');
+ elData(listItem, 'label-id', -1);
+ this._blockScroll(listItem);
+ additionalOptionList.appendChild(listItem);
+
+ var span = elCreate('span');
+ listItem.appendChild(span);
+
+ var label = elCreate('span');
+ label.className = 'badge label';
+ label.innerHTML = Language.get('wcf.label.withoutSelection');
+ span.appendChild(label);
+ }
+
+ if (!this._options.forceSelection) {
+ var listItem = elCreate('li');
+ elData(listItem, 'label-id', 0);
+ this._blockScroll(listItem);
+ additionalOptionList.appendChild(listItem);
+
+ var span = elCreate('span');
+ listItem.appendChild(span);
+
+ var label = elCreate('span');
+ label.className = 'badge label';
+ label.innerHTML = Language.get('wcf.label.none');
+ span.appendChild(label);
+ }
+
+ elBySelAll('li:not(.dropdownDivider)', dropdownMenu, function(listItem) {
+ listItem.addEventListener('click', this._click.bind(this));
+
+ if (labelId) {
+ if (~~elData(listItem, 'label-id') === labelId) {
+ this._selectLabel(listItem);
+ }
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Blocks page scrolling for the given element.
+ *
+ * @param {HTMLElement} element
+ */
+ _blockScroll: function(element) {
+ element.addEventListener(
+ 'wheel',
+ function(event) {
+ event.preventDefault();
+ },
+ {
+ passive: false
+ }
+ );
+ },
+
+ /**
+ * Select a label after clicking on it.
+ *
+ * @param {Event} event click event in label selection dropdown
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ this._selectLabel(event.currentTarget, false);
+ },
+
+ /**
+ * Selects the given label.
+ *
+ * @param {HTMLElement} label
+ */
+ _selectLabel: function(label) {
+ // save label
+ var labelId = elData(label, 'label-id');
+ if (!labelId) {
+ labelId = 0;
+ }
+
+ // replace button with currently selected label
+ var displayLabel = elBySel('span > span', label);
+ var button = elBySel('.dropdownToggle > span', this._labelChooser);
+ button.className = displayLabel.className;
+ button.textContent = displayLabel.textContent;
+
+ this._input.value = labelId;
+ }
+ };
+
+ return FormBuilderFieldLabel;
+});
+
+/**
+ * Handles the JavaScript part of the rating form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Controller/Rating
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Controller/Rating',['Dictionary', 'Environment'], function(Dictionary, Environment) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldRating(fieldId, value, activeCssClasses, defaultCssClasses) {
+ this.init(fieldId, value, activeCssClasses, defaultCssClasses);
+ };
+ FormBuilderFieldRating.prototype = {
+ /**
+ * Initializes the rating form field.
+ *
+ * @param {string} fieldId id of the relevant form builder field
+ * @param {integer} value current value of the field
+ * @param {string[]} activeCssClasses CSS classes for the active state of rating elements
+ * @param {string[]} defaultCssClasses CSS classes for the default state of rating elements
+ */
+ init: function(fieldId, value, activeCssClasses, defaultCssClasses) {
+ this._field = elBySel('#' + fieldId + 'Container');
+ if (this._field === null) {
+ throw new Error("Unknown field with id '" + fieldId + "'");
+ }
+
+ this._input = elCreate('input');
+ this._input.id = fieldId;
+ this._input.name = fieldId;
+ this._input.type = 'hidden';
+ this._input.value = value;
+ this._field.appendChild(this._input);
+
+ this._activeCssClasses = activeCssClasses;
+ this._defaultCssClasses = defaultCssClasses;
+
+ this._ratingElements = new Dictionary();
+
+ var ratingList = elBySel('.ratingList', this._field);
+ ratingList.addEventListener('mouseleave', this._restoreRating.bind(this));
+
+ elBySelAll('li', ratingList, function(listItem) {
+ if (listItem.classList.contains('ratingMetaButton')) {
+ listItem.addEventListener('click', this._metaButtonClick.bind(this));
+ listItem.addEventListener('mouseenter', this._restoreRating.bind(this));
+ }
+ else {
+ this._ratingElements.set(~~elData(listItem, 'rating'), listItem);
+
+ listItem.addEventListener('click', this._listItemClick.bind(this));
+ listItem.addEventListener('mouseenter', this._listItemMouseEnter.bind(this));
+ listItem.addEventListener('mouseleave', this._listItemMouseLeave.bind(this));
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Saves the rating associated with the clicked rating element.
+ *
+ * @param {Event} event rating element `click` event
+ */
+ _listItemClick: function(event) {
+ this._input.value = ~~elData(event.currentTarget, 'rating');
+
+ if (Environment.platform() !== 'desktop') {
+ this._restoreRating();
+ }
+ },
+
+ /**
+ * Updates the rating UI when hovering over a rating element.
+ *
+ * @param {Event} event rating element `mouseenter` event
+ */
+ _listItemMouseEnter: function(event) {
+ var currentRating = elData(event.currentTarget, 'rating');
+
+ this._ratingElements.forEach(function(ratingElement, rating) {
+ var icon = elByClass('icon', ratingElement)[0];
+
+ this._toggleIcon(icon, ~~rating <= ~~currentRating);
+ }.bind(this));
+ },
+
+ /**
+ * Updates the rating UI when leaving a rating element by changing all rating elements
+ * to their default state.
+ */
+ _listItemMouseLeave: function() {
+ this._ratingElements.forEach(function(ratingElement) {
+ var icon = elByClass('icon', ratingElement)[0];
+
+ this._toggleIcon(icon, false);
+ }.bind(this));
+ },
+
+ /**
+ * Handles clicks on meta buttons.
+ *
+ * @param {Event} event meta button `click` event
+ */
+ _metaButtonClick: function(event) {
+ if (elData(event.currentTarget, 'action') === 'removeRating') {
+ this._input.value = '';
+
+ this._listItemMouseLeave();
+ }
+ },
+
+ /**
+ * Updates the rating UI by changing the rating elements to the stored rating state.
+ */
+ _restoreRating: function() {
+ this._ratingElements.forEach(function(ratingElement, rating) {
+ var icon = elByClass('icon', ratingElement)[0];
+
+ this._toggleIcon(icon, ~~rating <= ~~this._input.value);
+ }.bind(this));
+ },
+
+ /**
+ * Toggles the state of the given icon based on the given state parameter.
+ *
+ * @param {HTMLElement} icon toggled icon
+ * @param {boolean} active is `true` if icon will be changed to `active` state, otherwise changed to `default` state
+ */
+ _toggleIcon: function(icon, active) {
+ active = active || false;
+
+ if (active) {
+ for (var i = 0; i < this._defaultCssClasses.length; i++) {
+ icon.classList.remove(this._defaultCssClasses[i]);
+ }
+
+ for (var i = 0; i < this._activeCssClasses.length; i++) {
+ icon.classList.add(this._activeCssClasses[i]);
+ }
+ }
+ else {
+ for (var i = 0; i < this._activeCssClasses.length; i++) {
+ icon.classList.remove(this._activeCssClasses[i]);
+ }
+
+ for (var i = 0; i < this._defaultCssClasses.length; i++) {
+ icon.classList.add(this._defaultCssClasses[i]);
+ }
+ }
+ }
+ };
+
+ return FormBuilderFieldRating;
+});
+
+/**
+ * Abstract implementation of a form field dependency.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract',['./Manager'], function(DependencyManager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Abstract(dependentElementId, fieldId) {
+ this.init(dependentElementId, fieldId);
+ };
+ Abstract.prototype = {
+ /**
+ * Checks if the dependency is met.
+ *
+ * @abstract
+ */
+ checkDependency: function() {
+ throw new Error("Missing implementation of WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract.checkDependency!");
+ },
+
+ /**
+ * Return the node whose availability depends on the value of a field.
+ *
+ * @return {HtmlElement} dependent node
+ */
+ getDependentNode: function() {
+ return this._dependentElement;
+ },
+
+ /**
+ * Returns the field the availability of the element dependents on.
+ *
+ * @return {HtmlElement} field controlling element availability
+ */
+ getField: function() {
+ return this._field;
+ },
+
+ /**
+ * Returns all fields requiring `change` event listeners for this
+ * dependency to be properly resolved.
+ *
+ * @return {HtmlElement[]} fields to register event listeners on
+ */
+ getFields: function() {
+ return this._fields;
+ },
+
+ /**
+ * Initializes the new dependency object.
+ *
+ * @param {string} dependentElementId id of the (container of the) dependent element
+ * @param {string} fieldId id of the field controlling element availability
+ *
+ * @throws {Error} if either depenent element id or field id are invalid
+ */
+ init: function(dependentElementId, fieldId) {
+ this._dependentElement = elById(dependentElementId);
+ if (this._dependentElement === null) {
+ throw new Error("Unknown dependent element with container id '" + dependentElementId + "Container'.");
+ }
+
+ this._field = elById(fieldId);
+ if (this._field === null) {
+ this._fields = [];
+ elBySelAll('input[type=radio][name=' + fieldId + ']', undefined, function(field) {
+ this._fields.push(field);
+ }.bind(this));
+
+ if (!this._fields.length) {
+ throw new Error("Unknown field with id '" + fieldId + "'.");
+ }
+ }
+ else {
+ this._fields = [this._field];
+
+ // handle special case of boolean form fields that have to form fields
+ if (this._field.tagName === 'INPUT' && this._field.type === 'radio' && elData(this._field, 'no-input-id') !== '') {
+ this._noField = elById(elData(this._field, 'no-input-id'));
+ if (this._noField === null) {
+ throw new Error("Cannot find 'no' input field for input field '" + fieldId + "'");
+ }
+
+ this._fields.push(this._noField);
+ }
+ }
+
+ DependencyManager.addDependency(this);
+ }
+ };
+
+ return Abstract;
+});
+
+/**
+ * Form field dependency implementation that requires the value of a field not to be empty.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/NonEmpty
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/NonEmpty',['./Abstract', 'Core'], function(Abstract, Core) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function NonEmpty(dependentElementId, fieldId) {
+ this.init(dependentElementId, fieldId);
+ };
+ Core.inherit(NonEmpty, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract#checkDependency
+ */
+ checkDependency: function() {
+ switch (this._field.tagName) {
+ case 'INPUT':
+ switch (this._field.type) {
+ case 'checkbox':
+ // TODO: check if working
+ return this._field.checked;
+
+ case 'radio':
+ if (this._noField && this._noField.checked) {
+ return false;
+ }
+
+ return this._field.checked;
+
+ default:
+ return this._field.value.trim().length !== 0;
+ }
+
+ case 'SELECT':
+ // TODO: check if working for multiselect
+ return this._field.value.length !== 0;
+
+ case 'TEXTAREA':
+ // TODO: check if working
+ return this._field.value.trim().length !== 0;
+ }
+ }
+ });
+
+ return NonEmpty;
+});
+
+/**
+ * Form field dependency implementation that requires a field to have a certain value.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Value
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Value',['./Abstract', 'Core', './Manager'], function(Abstract, Core, Manager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Value(dependentElementId, fieldId, isNegated) {
+ this.init(dependentElementId, fieldId);
+
+ this._isNegated = false;
+ };
+ Core.inherit(Value, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract#checkDependency
+ */
+ checkDependency: function() {
+ if (!this._values) {
+ throw new Error("Values have not been set.");
+ }
+
+ var value;
+ if (this._field) {
+ if (Manager.isHiddenByDependencies(this._field)) {
+ return false;
+ }
+
+ value = this._field.value;
+ }
+ else {
+ for (var i = 0, length = this._fields.length, field; i < length; i++) {
+ field = this._fields[i];
+
+ if (field.checked) {
+ if (Manager.isHiddenByDependencies(field)) {
+ return false;
+ }
+
+ value = field.value;
+
+ break;
+ }
+ }
+ }
+
+ // do not use `Array.prototype.indexOf()` as we use a weak comparision
+ for (var i = 0, length = this._values.length; i < length; i++) {
+ if (this._values[i] == value) {
+ if (this._isNegated) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ if (this._isNegated) {
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Sets if the field value may not have any of the set values.
+ *
+ * @param {bool} negate
+ * @return {WoltLabSuite/Core/Form/Builder/Field/Dependency/Value}
+ */
+ negate: function(negate) {
+ this._isNegated = negate;
+
+ return this;
+ },
+
+ /**
+ * Sets the possible values the field may have for the dependency to be met.
+ *
+ * @param {array} values
+ * @return {WoltLabSuite/Core/Form/Builder/Field/Dependency/Value}
+ */
+ values: function(values) {
+ this._values = values;
+
+ return this;
+ }
+ });
+
+ return Value;
+});
+
+/**
+ * Data handler for a content language form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Language/ContentLanguage
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Language/ContentLanguage',['Core', 'WoltLabSuite/Core/Language/Chooser', '../Value'], function(Core, LanguageChooser, FormBuilderFieldValue) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldContentLanguage(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldContentLanguage, FormBuilderFieldValue, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#destroy
+ */
+ destroy: function() {
+ LanguageChooser.removeChooser(this._fieldId);
+ }
+ });
+
+ return FormBuilderFieldContentLanguage;
+});
+
+/**
+ * Abstract implementation of a handler for the visibility of container due the dependencies
+ * of its children.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Abstract',['EventHandler', '../Manager'], function(EventHandler, DependencyManager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Abstract(containerId) {
+ this.init(containerId);
+ };
+ Abstract.prototype = {
+ /**
+ * Checks if the container should be visible and shows or hides it accordingly.
+ *
+ * @abstract
+ */
+ checkContainer: function() {
+ throw new Error("Missing implementation of WoltLabSuite/Core/Form/Builder/Field/Dependency/Container.checkContainer!");
+ },
+
+ /**
+ * Initializes a new container dependency handler for the container with the given
+ * id.
+ *
+ * @param {string} containerId id of the handled container
+ *
+ * @throws {TypeError} if container id is no string
+ * @throws {Error} if container id is invalid
+ */
+ init: function(containerId) {
+ if (typeof containerId !== 'string') {
+ throw new TypeError("Container id has to be a string.");
+ }
+
+ this._container = elById(containerId);
+ if (this._container === null) {
+ throw new Error("Unknown container with id '" + containerId + "'.");
+ }
+
+ DependencyManager.addContainerCheckCallback(this.checkContainer.bind(this));
+ }
+ };
+
+ return Abstract
+});
+
+/**
+ * Default implementation for a container visibility handler due to the dependencies of its
+ * children that only considers the visibility of all of its children.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default',['./Abstract', 'Core', '../Manager'], function(Abstract, Core, DependencyManager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Default(containerId) {
+ this.init(containerId);
+ };
+ Core.inherit(Default, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default#checkContainer
+ */
+ checkContainer: function() {
+ if (elDataBool(this._container, 'ignore-dependencies')) {
+ return;
+ }
+
+ // only consider containers that have not been hidden by their own dependencies
+ if (DependencyManager.isHiddenByDependencies(this._container)) {
+ return;
+ }
+
+ var containerIsVisible = !elIsHidden(this._container);
+ var containerShouldBeVisible = false;
+
+ var children = this._container.children;
+ var start = 0;
+ // ignore container header for visibility considerations
+ if (this._container.children.item(0).tagName === 'H2' || this._container.children.item(0).tagName === 'HEADER') {
+ var start = 1;
+ }
+
+ for (var i = start, length = children.length; i < length; i++) {
+ if (!elIsHidden(children.item(i))) {
+ containerShouldBeVisible = true;
+ break;
+ }
+ }
+
+ if (containerIsVisible !== containerShouldBeVisible) {
+ if (containerShouldBeVisible) {
+ elShow(this._container);
+ }
+ else {
+ elHide(this._container);
+ }
+
+ // check containers again to make sure parent containers can react to
+ // changing the visibility of this container
+ DependencyManager.checkContainers();
+ }
+ }
+ });
+
+ return Default;
+});
+
+/**
+ * Container visibility handler implementation for a tab menu tab that, in addition to the
+ * tab itself, also handles the visibility of the tab menu list item.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Tab
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Tab',['./Abstract', 'Core', 'Dom/Util', '../Manager', 'Ui/TabMenu'], function(Abstract, Core, DomUtil, DependencyManager, UiTabMenu) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Tab(containerId) {
+ this.init(containerId);
+ };
+ Core.inherit(Tab, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default#checkContainer
+ */
+ checkContainer: function() {
+ // only consider containers that have not been hidden by their own dependencies
+ if (DependencyManager.isHiddenByDependencies(this._container)) {
+ return;
+ }
+
+ var containerIsVisible = !elIsHidden(this._container);
+ var containerShouldBeVisible = false;
+
+ var children = this._container.children;
+ for (var i = 0, length = children.length; i < length; i++) {
+ if (!elIsHidden(children.item(i))) {
+ containerShouldBeVisible = true;
+ break;
+ }
+ }
+
+ if (containerIsVisible !== containerShouldBeVisible) {
+ var tabMenuListItem = elBySel('#' + DomUtil.identify(this._container.parentNode) + ' > nav > ul > li[data-name=' + this._container.id + ']', this._container.parentNode.parentNode);
+ if (tabMenuListItem === null) {
+ throw new Error("Cannot find tab menu entry for tab '" + this._container.id + "'.");
+ }
+
+ if (containerShouldBeVisible) {
+ elShow(this._container);
+ elShow(tabMenuListItem);
+ }
+ else {
+ elHide(this._container);
+ elHide(tabMenuListItem);
+
+ var tabMenu = UiTabMenu.getTabMenu(DomUtil.identify(tabMenuListItem.closest('.tabMenuContainer')));
+
+ // check if currently active tab will be hidden
+ if (tabMenu.getActiveTab() === tabMenuListItem) {
+ tabMenu.selectFirstVisible();
+ }
+ }
+
+ // check containers again to make sure parent containers can react to
+ // changing the visibility of this container
+ DependencyManager.checkContainers();
+ }
+ }
+ });
+
+ return Tab;
+});
+
+/**
+ * Container visibility handler implementation for a tab menu that checks visibility
+ * based on the visibility of its tab menu list items.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/TabMenu
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/TabMenu',['./Abstract', 'Core', 'Dom/Util', '../Manager', 'Ui/TabMenu'], function(Abstract, Core, DomUtil, DependencyManager, UiTabMenu) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function TabMenu(containerId) {
+ this.init(containerId);
+ };
+ Core.inherit(TabMenu, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default#checkContainer
+ */
+ checkContainer: function() {
+ // only consider containers that have not been hidden by their own dependencies
+ if (DependencyManager.isHiddenByDependencies(this._container)) {
+ return;
+ }
+
+ var containerIsVisible = !elIsHidden(this._container);
+ var containerShouldBeVisible = false;
+
+ var tabMenuListItems = elBySelAll('#' + DomUtil.identify(this._container) + ' > nav > ul > li', this._container.parentNode);
+ for (var i = 0, length = tabMenuListItems.length; i < length; i++) {
+ if (!elIsHidden(tabMenuListItems[i])) {
+ containerShouldBeVisible = true;
+ break;
+ }
+ }
+
+ if (containerIsVisible !== containerShouldBeVisible) {
+ if (containerShouldBeVisible) {
+ elShow(this._container);
+
+ UiTabMenu.getTabMenu(DomUtil.identify(this._container)).selectFirstVisible();
+ }
+ else {
+ elHide(this._container);
+ }
+
+ // check containers again to make sure parent containers can react to
+ // changing the visibility of this container
+ DependencyManager.checkContainers();
+ }
+ }
+ });
+
+ return TabMenu;
+});
+
+/**
+ * Abstract implementation of the JavaScript component of a form field handling
+ * a list of packages.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList',['Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language'], function(DomChangeListener, DomTraverse, DomUtil, EventKey, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function AbstractPackageList(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ AbstractPackageList.prototype = {
+ /**
+ * Initializes the package list handler.
+ *
+ * @param {string} formFieldId id of the associated form field
+ * @param {object[]} existingPackages data of existing packages
+ */
+ init: function(formFieldId, existingPackages) {
+ this._formFieldId = formFieldId;
+
+ this._packageList = elById(this._formFieldId + '_packageList');
+ if (this._packageList === null) {
+ throw new Error("Cannot find package list for packages field with id '" + this._formFieldId + "'.");
+ }
+
+ this._packageIdentifier = elById(this._formFieldId + '_packageIdentifier');
+ if (this._packageIdentifier === null) {
+ throw new Error("Cannot find package identifier form field for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._packageIdentifier.addEventListener('keypress', this._keyPress.bind(this));
+
+ this._addButton = elById(this._formFieldId + '_addButton');
+ if (this._addButton === null) {
+ throw new Error("Cannot find add button for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._addButton.addEventListener('click', this._addPackage.bind(this));
+
+ this._form = this._packageList.closest('form');
+ if (this._form === null) {
+ throw new Error("Cannot find form element for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._form.addEventListener('submit', this._submit.bind(this));
+
+ existingPackages.forEach(this._addPackageByData.bind(this));
+ },
+
+ /**
+ * Adds a package to the package list as a consequence of the given
+ * event. If the package data is invalid, an error message is shown
+ * and no package is added.
+ *
+ * @param {Event} event event that triggered trying to add the package
+ */
+ _addPackage: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // validate data
+ if (!this._validateInput()) {
+ return;
+ }
+
+ this._addPackageByData(this._getInputData());
+
+ // empty fields
+ this._emptyInput();
+
+ this._packageIdentifier.focus();
+ },
+
+ /**
+ * Adds a package to the package list using the given package data.
+ *
+ * @param {object} packageData
+ */
+ _addPackageByData: function(packageData) {
+ // add package to list
+ var listItem = elCreate('li');
+ this._populateListItem(listItem, packageData);
+
+ // add delete button
+ var deleteButton = elCreate('span');
+ deleteButton.className = 'icon icon16 fa-times pointer jsTooltip';
+ elAttr(deleteButton, 'title', Language.get('wcf.global.button.delete'));
+ deleteButton.addEventListener('click', this._removePackage.bind(this));
+ DomUtil.prepend(deleteButton, listItem);
+
+ this._packageList.appendChild(listItem);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Creates the hidden fields when the form is submitted.
+ *
+ * @param {HTMLElement} listElement package list element from the package list
+ * @param {int} index package index
+ */
+ _createSubmitFields: function(listElement, index) {
+ var packageIdentifier = elCreate('input');
+ elAttr(packageIdentifier, 'type', 'hidden');
+ elAttr(packageIdentifier, 'name', this._formFieldId + '[' + index + '][packageIdentifier]')
+ packageIdentifier.value = elData(listElement, 'package-identifier');
+ this._form.appendChild(packageIdentifier);
+ },
+
+ /**
+ * Empties the input fields.
+ */
+ _emptyInput() {
+ this._packageIdentifier.value = '';
+ },
+
+ /**
+ * Returns the error element for the given form field element.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getErrorElement: function(element, createIfNoNExistent) {
+ var error = DomTraverse.nextByClass(element, 'innerError');
+
+ if (error === null && createIfNoNExistent) {
+ error = elCreate('small');
+ error.className = 'innerError';
+
+ DomUtil.insertAfter(error, element);
+ }
+
+ return error;
+ },
+
+ /**
+ * Returns the current data of the input fields to add a new package.
+ *
+ * @return {object}
+ */
+ _getInputData: function() {
+ return {
+ packageIdentifier: this._packageIdentifier.value
+ };
+ },
+
+ /**
+ * Returns the error element for the package identifier form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getPackageIdentifierErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._packageIdentifier, createIfNonExistent);
+ },
+
+ /**
+ * Adds a package to the package list after pressing ENTER in a
+ * text field.
+ *
+ * @param {Event} event
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ this._addPackage(event);
+ }
+ },
+
+ /**
+ * Adds all necessary package-relavant data to the given list item.
+ *
+ * @param {HTMLElement} listItem package list element holding package data
+ * @param {object} packageData package data
+ */
+ _populateListItem(listItem, packageData) {
+ elData(listItem, 'package-identifier', packageData.packageIdentifier);
+ },
+
+ /**
+ * Removes a package by clicking on its delete button.
+ *
+ * @param {Event} event delete button click event
+ */
+ _removePackage: function(event) {
+ elRemove(event.currentTarget.closest('li'));
+
+ // remove field errors if the last package has been deleted
+ if (
+ !this._packageList.childElementCount &&
+ this._packageList.nextElementSibling.tagName === 'SMALL' &&
+ this._packageList.nextElementSibling.classList.contains('innerError')
+ ) {
+ elRemove(this._packageList.nextElementSibling);
+ }
+ },
+
+ /**
+ * Adds all necessary (hidden) form fields to the form when
+ * submitting the form.
+ */
+ _submit: function() {
+ DomTraverse.childrenByTag(this._packageList, 'LI').forEach(this._createSubmitFields.bind(this));
+ },
+
+ /**
+ * Returns `true` if the currently entered package data is valid.
+ * Otherwise `false` is returned and relevant error messages are
+ * shown.
+ *
+ * @return {boolean}
+ */
+ _validateInput: function() {
+ return this._validatePackageIdentifier();
+ },
+
+ /**
+ * Returns `true` if the currently entered package identifier is
+ * valid. Otherwise `false` is returned and an error message is
+ * shown.
+ *
+ * @return {boolean}
+ */
+ _validatePackageIdentifier: function() {
+ var packageIdentifier = this._packageIdentifier.value;
+
+ if (packageIdentifier === '') {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.global.form.error.empty');
+
+ return false;
+ }
+
+ if (packageIdentifier.length < 3) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.minimumLength');
+
+ return false;
+ }
+ else if (packageIdentifier.length > 191) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.maximumLength');
+
+ return false;
+ }
+
+ // see `wcf\data\package\Package::isValidPackageName()`
+ if (!packageIdentifier.match(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/)) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.format');
+
+ return false;
+ }
+
+ // check if package has already been added
+ var duplicate = false;
+ DomTraverse.childrenByTag(this._packageList, 'LI').forEach(function(listItem, index) {
+ if (elData(listItem, 'package-identifier') === packageIdentifier) {
+ duplicate = true;
+ }
+ });
+
+ if (duplicate) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.duplicate');
+
+ return false;
+ }
+
+ // remove outdated errors
+ var error = this._getPackageIdentifierErrorElement();
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ },
+
+ /**
+ * Returns `true` if the given version is valid. Otherwise `false`
+ * is returned and an error message is shown.
+ *
+ * @param {string} version validated version
+ * @param {function} versionErrorGetter returns the version error element
+ * @return {boolean}
+ */
+ _validateVersion: function(version, versionErrorGetter) {
+ // see `wcf\data\package\Package::isValidVersion()`
+ // the version is no a required attribute
+ if (version !== '') {
+ if (version.length > 255) {
+ versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.maximumLength');
+
+ return false;
+ }
+
+ // see `wcf\data\package\Package::isValidVersion()`
+ if (!version.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
+ versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
+
+ return false;
+ }
+ }
+
+ // remove outdated errors
+ var error = versionErrorGetter();
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ }
+ };
+
+ return AbstractPackageList;
+});
+
+/**
+ * Manages the packages entered in a devtools project excluded package form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/ExcludedPackages
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/ExcludedPackages',['./AbstractPackageList', 'Core', 'Language'], function(AbstractPackageList, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function ExcludedPackages(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ Core.inherit(ExcludedPackages, AbstractPackageList, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#init
+ */
+ init: function(formFieldId, existingPackages) {
+ ExcludedPackages._super.prototype.init.call(this, formFieldId, existingPackages);
+
+ this._version = elById(this._formFieldId + '_version');
+ if (this._version === null) {
+ throw new Error("Cannot find version form field for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._version.addEventListener('keypress', this._keyPress.bind(this));
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_createSubmitFields
+ */
+ _createSubmitFields: function(listElement, index) {
+ ExcludedPackages._super.prototype._createSubmitFields.call(this, listElement, index);
+
+ var version = elCreate('input');
+ elAttr(version, 'type', 'hidden');
+ elAttr(version, 'name', this._formFieldId + '[' + index + '][version]')
+ version.value = elData(listElement, 'version');
+ this._form.appendChild(version);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_emptyInput
+ */
+ _emptyInput() {
+ ExcludedPackages._super.prototype._emptyInput.call(this);
+
+ this._version.value = '';
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_getInputData
+ */
+ _getInputData: function() {
+ return Core.extend(ExcludedPackages._super.prototype._getInputData.call(this), {
+ version: this._version.value
+ });
+ },
+
+ /**
+ * Returns the error element for the version form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getVersionErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._version, createIfNonExistent);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_populateListItem
+ */
+ _populateListItem(listItem, packageData) {
+ ExcludedPackages._super.prototype._populateListItem.call(this, listItem, packageData);
+
+ elData(listItem, 'version', packageData.version);
+ listItem.innerHTML = ' ' + Language.get('wcf.acp.devtools.project.excludedPackage.excludedPackage', {
+ packageIdentifier: packageData.packageIdentifier,
+ version: packageData.version
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_validateInput
+ */
+ _validateInput: function() {
+ return ExcludedPackages._super.prototype._validateInput.call(this) && this._validateVersion(
+ this._version.value,
+ this._getVersionErrorElement.bind(this)
+ );
+ }
+ });
+
+ return ExcludedPackages;
+});
+
+/**
+ * Manages the instructions entered in a devtools project instructions form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/Instructions
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/Instructions',[
+ 'Dom/ChangeListener',
+ 'Dom/Traverse',
+ 'Dom/Util',
+ 'EventKey',
+ 'Language',
+ 'Ui/Confirmation',
+ 'Ui/Dialog',
+ 'WoltLabSuite/Core/Ui/Sortable/List'
+], function(
+ DomChangeListener,
+ DomTraverse,
+ DomUtil,
+ EventKey,
+ Language,
+ UiConfirmation,
+ UiDialog,
+ UiSortableList
+) {
+ "use strict";
+
+ var _applicationPips = ['acpTemplate', 'file', 'script', 'template'];
+
+ /**
+ * @constructor
+ */
+ function Instructions(
+ formFieldId,
+ instructionsTemplate,
+ instructionsEditDialogTemplate,
+ instructionEditDialogTemplate,
+ pipDefaultFilenames,
+ existingInstructions
+ ) {
+ this.init(
+ formFieldId,
+ instructionsTemplate,
+ instructionsEditDialogTemplate,
+ instructionEditDialogTemplate,
+ pipDefaultFilenames,
+ existingInstructions || []
+ );
+ };
+ Instructions.prototype = {
+ /**
+ * Initializes the instructions handler.
+ *
+ * @param {string} formFieldId id of the associated form field
+ * @param {Template} instructionsTemplate template used for a new set of instructions
+ * @param {Template} instructionsEditDialogTemplate template used for instructions edit dialogs
+ * @param {Template} instructionEditDialogTemplate template used for instruction edit dialogs
+ * @param {object} pipDefaultFilenames maps pip names to their default filenames
+ * @param {object[]} existingInstructions data of existing instructions
+ */
+ init: function(
+ formFieldId,
+ instructionsTemplate,
+ instructionsEditDialogTemplate,
+ instructionEditDialogTemplate,
+ pipDefaultFilenames,
+ existingInstructions
+ ) {
+ this._formFieldId = formFieldId;
+ this._instructionsTemplate = instructionsTemplate;
+ this._instructionsEditDialogTemplate = instructionsEditDialogTemplate;
+ this._instructionEditDialogTemplate = instructionEditDialogTemplate;
+ this._instructionsCounter = 0;
+ this._pipDefaultFilenames = pipDefaultFilenames;
+ this._instructionCounter = 0;
+
+ this._instructionsList = elById(this._formFieldId + '_instructionsList');
+ if (this._instructionsList === null) {
+ throw new Error("Cannot find package list for packages field with id '" + this._formFieldId + "'.");
+ }
+
+ this._instructionsType = elById(this._formFieldId + '_instructionsType');
+ if (this._instructionsType === null) {
+ throw new Error("Cannot find instruction type form field for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._instructionsType.addEventListener('change', this._toggleFromVersionFormField.bind(this));
+
+ this._fromVersion = elById(this._formFieldId + '_fromVersion');
+ if (this._fromVersion === null) {
+ throw new Error("Cannot find from version form field for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._fromVersion.addEventListener('keypress', this._instructionsKeyPress.bind(this));
+
+ this._addButton = elById(this._formFieldId + '_addButton');
+ if (this._addButton === null) {
+ throw new Error("Cannot find add button for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._addButton.addEventListener('click', this._addInstructions.bind(this));
+
+ this._form = this._instructionsList.closest('form');
+ if (this._form === null) {
+ throw new Error("Cannot find form element for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._form.addEventListener('submit', this._submit.bind(this));
+
+ var hasInstallInstructions = false;
+
+ for (var index in existingInstructions) {
+ var instructions = existingInstructions[index];
+
+ if (instructions.type === 'install') {
+ hasInstallInstructions = true;
+ break;
+ }
+ }
+
+ // ensure that there are always installation instructions
+ if (!hasInstallInstructions) {
+ this._addInstructionsByData({
+ fromVersion: '',
+ type: 'install'
+ });
+ }
+
+ existingInstructions.forEach(this._addInstructionsByData.bind(this));
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Adds an instruction to a set of instructions as a consequence
+ * of the given event. If the instruction data is invalid, an
+ * error message is shown and no instruction is added.
+ *
+ * @param {Event} event event that triggered trying to add the instruction
+ */
+ _addInstruction: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var instructionsId = elData(event.currentTarget.closest('li.section'), 'instructions-id');
+
+ // note: data will be validated/filtered by the server
+
+ var pipField = elById(this._formFieldId + '_instructions' + instructionsId + '_pip');
+
+ // ignore pressing button if no PIP has been selected
+ if (!pipField.value) {
+ return;
+ }
+
+ var valueField = elById(this._formFieldId + '_instructions' + instructionsId + '_value');
+ var runStandaloneField = elById(this._formFieldId + '_instructions' + instructionsId + '_runStandalone');
+ var applicationField = elById(this._formFieldId + '_instructions' + instructionsId + '_application');
+
+ this._addInstructionByData(instructionsId, {
+ application: _applicationPips.indexOf(pipField.value) !== -1 ? applicationField.value : '',
+ pip: pipField.value,
+ runStandalone: ~~runStandaloneField.checked,
+ value: valueField.value
+ });
+
+ // empty fields
+ pipField.value = '';
+ valueField.value = '';
+ runStandaloneField.checked = false;
+ applicationField.value = '';
+ elById(this._formFieldId + '_instructions' + instructionsId + '_valueDescription').innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description');
+ this._toggleApplicationFormField(instructionsId);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Adds an instruction to the set of instructions with the given id.
+ *
+ * @param {int} instructionsId
+ * @param {object} instructionData
+ */
+ _addInstructionByData: function(instructionsId, instructionData) {
+ var instructionId = ++this._instructionCounter;
+
+ var instructionList = elById(this._formFieldId + '_instructions' + instructionsId + '_instructionList');
+
+ var listItem = elCreate('li');
+ listItem.className = 'sortableNode';
+ listItem.id = this._formFieldId + '_instruction' + instructionId;
+ elData(listItem, 'instruction-id', instructionId);
+ elData(listItem, 'application', instructionData.application);
+ elData(listItem, 'pip', instructionData.pip);
+ elData(listItem, 'runStandalone', instructionData.runStandalone);
+ elData(listItem, 'value', instructionData.value);
+
+ var content = '' +
+ '<div class="sortableNodeLabel">' +
+ ' <div class="jsDevtoolsProjectInstruction">' +
+ ' ' + Language.get('wcf.acp.devtools.project.instruction.instruction', instructionData);
+
+ if (instructionData.errors) {
+ for (var index in instructionData.errors) {
+ content += '<small class="innerError">' + instructionData.errors[index] + '</small>';
+ }
+ }
+
+ content += '' +
+ ' </div>' +
+ ' <span class="statusDisplay sortableButtonContainer">' +
+ ' <span class="icon icon16 fa-pencil pointer jsTooltip" id="' + this._formFieldId + '_instruction' + instructionId + '_editButton" title="' + Language.get('wcf.global.button.edit') + '"></span>' +
+ ' <span class="icon icon16 fa-times pointer jsTooltip" id="' + this._formFieldId + '_instruction' + instructionId + '_deleteButton" title="' + Language.get('wcf.global.button.delete') + '"></span>' +
+ ' </span>' +
+ '</div>';
+
+ listItem.innerHTML = content;
+
+ instructionList.appendChild(listItem);
+
+ elById(this._formFieldId + '_instruction' + instructionId + '_deleteButton').addEventListener('click', this._removeInstruction.bind(this));
+ elById(this._formFieldId + '_instruction' + instructionId + '_editButton').addEventListener('click', this._editInstruction.bind(this));
+ },
+
+ /**
+ * Adds a set of instructions as a consequenc of the given event.
+ * If the instructions data is invalid, an error message is shown
+ * and no instruction set is added.
+ *
+ * @param {Event} event event that triggered trying to add the instructions
+ */
+ _addInstructions: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // validate data
+ if (!this._validateInstructionsType() || (this._instructionsType.value === 'update' && !this._validateFromVersion(this._fromVersion))) {
+ return;
+ }
+
+ this._addInstructionsByData({
+ fromVersion: this._instructionsType.value === 'update' ? this._fromVersion.value : '',
+ type: this._instructionsType.value
+ });
+
+ // empty fields
+ this._instructionsType.value = '';
+ this._fromVersion.value = '';
+
+ this._toggleFromVersionFormField();
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Adds a set of instructions.
+ *
+ * @param {object} instructionData
+ */
+ _addInstructionsByData: function(instructionsData) {
+ var instructionsId = ++this._instructionsCounter;
+
+ var listItem = elCreate('li');
+ listItem.className = 'section';
+ listItem.innerHTML = this._instructionsTemplate.fetch({
+ instructionsId: instructionsId,
+ sectionTitle: Language.get('wcf.acp.devtools.project.instructions.type.' + instructionsData.type + '.title', {
+ fromVersion: instructionsData.fromVersion
+ }),
+ type: instructionsData.type
+ });
+
+ listItem.id = this._formFieldId + '_instructions' + instructionsId;
+ elData(listItem, 'instructions-id', instructionsId);
+ elData(listItem, 'type', instructionsData.type);
+ elData(listItem, 'fromVersion', instructionsData.fromVersion);
+
+ elById(this._formFieldId + '_instructions' + instructionsId + '_valueDescription')
+
+ this._instructionsList.appendChild(listItem);
+
+ var instructionListContainer = elById(this._formFieldId + '_instructions' + instructionsId + '_instructionListContainer');
+ for (var errorMessage of instructionsData.errors || []) {
+ var small = elCreate('small');
+ small.className = 'innerError';
+ small.innerHTML = errorMessage;
+
+ instructionListContainer.parentNode.insertBefore(small, instructionListContainer);
+ }
+
+ new UiSortableList({
+ containerId: instructionListContainer.id,
+ isSimpleSorting: true,
+ options: {
+ toleranceElement: '> div'
+ }
+ });
+
+ var deleteButton = elById(this._formFieldId + '_instructions' + instructionsId + '_deleteButton');
+ if (instructionsData.type === 'update') {
+ elById(this._formFieldId + '_instructions' + instructionsId + '_deleteButton').addEventListener('click', this._removeInstructions.bind(this));
+ elById(this._formFieldId + '_instructions' + instructionsId + '_editButton').addEventListener('click', this._editInstructions.bind(this));
+ }
+
+ elById(this._formFieldId + '_instructions' + instructionsId + '_pip').addEventListener('change', this._changeInstructionPip.bind(this));
+ elById(this._formFieldId + '_instructions' + instructionsId + '_value').addEventListener('keypress', this._instructionKeyPress.bind(this));
+ elById(this._formFieldId + '_instructions' + instructionsId + '_addButton').addEventListener('click', this._addInstruction.bind(this));
+
+ if (instructionsData.instructions) {
+ for (var index in instructionsData.instructions) {
+ this._addInstructionByData(instructionsId, instructionsData.instructions[index]);
+ }
+ }
+ },
+
+ /**
+ * Is called if the selected package installation plugin of an
+ * instruction is changed.
+ *
+ * @param {Event} event change event
+ */
+ _changeInstructionPip: function(event) {
+ var pip = event.currentTarget.value;
+ var instructionsId = elData(event.currentTarget.closest('li.section'), 'instructions-id');
+ var description = elById(this._formFieldId + '_instructions' + instructionsId + '_valueDescription');
+
+ // update value description
+ if (this._pipDefaultFilenames[pip] !== '') {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description.defaultFilename', {
+ defaultFilename: this._pipDefaultFilenames[pip]
+ });
+ }
+ else {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description');
+ }
+
+ var valueDlClassList = elById(this._formFieldId + '_instructions' + instructionsId + '_value').closest('dl').classList;
+ var applicationDl = elById(this._formFieldId + '_instructions' + instructionsId + '_application').closest('dl');
+
+ // toggle application selector
+ this._toggleApplicationFormField(instructionsId);
+ },
+
+ /**
+ * Opens a dialog to edit an existing instruction.
+ *
+ * @param {Event} event edit button click event
+ */
+ _editInstruction: function(event) {
+ var listItem = event.currentTarget.closest('li');
+
+ var instructionId = elData(listItem, 'instruction-id');
+ var application = elData(listItem, 'application');
+ var pip = elData(listItem, 'pip');
+ var runStandalone = elDataBool(listItem, 'runStandalone');
+ var value = elData(listItem, 'value');
+
+ var dialogContent = this._instructionEditDialogTemplate.fetch({
+ runStandalone: runStandalone,
+ value: value
+ });
+
+ var dialogId = 'instructionEditDialog' + instructionId;
+ if (!UiDialog.getDialog(dialogId)) {
+ UiDialog.openStatic(dialogId, dialogContent, {
+ onSetup: function(content) {
+ var applicationSelect = elBySel('select[name=application]', content);
+ var pipSelect = elBySel('select[name=pip]', content);
+ var runStandaloneInput = elBySel('input[name=runStandalone]', content);
+ var valueInput = elBySel('input[name=value]', content);
+
+ // set values of `select` elements
+ applicationSelect.value = application;
+ pipSelect.value = pip;
+
+ var submit = function() {
+ var listItem = elById(this._formFieldId + '_instruction' + instructionId);
+ elData(listItem, 'application', _applicationPips.indexOf(pipSelect.value) !== -1 ? applicationSelect.value : '');
+ elData(listItem, 'pip', pipSelect.value);
+ elData(listItem, 'runStandalone', ~~runStandaloneInput.checked);
+ elData(listItem, 'value', valueInput.value);
+
+ // note: data will be validated/filtered by the server
+
+ elByClass('jsDevtoolsProjectInstruction', listItem)[0].innerHTML = Language.get('wcf.acp.devtools.project.instruction.instruction', {
+ application: elData(listItem, 'application'),
+ pip: elData(listItem, 'pip'),
+ runStandalone: elDataBool(listItem, 'runStandalone'),
+ value: elData(listItem, 'value'),
+ });
+
+ DomChangeListener.trigger();
+
+ UiDialog.close(dialogId);
+ }.bind(this);
+
+ valueInput.addEventListener('keypress', function(event) {
+ if (EventKey.Enter(event)) {
+ submit();
+ }
+ });
+
+ elBySel('button[data-type=submit]', content).addEventListener('click', submit);
+
+ var pipChange = function() {
+ var pip = pipSelect.value;
+
+ if (_applicationPips.indexOf(pip) !== -1) {
+ elShow(applicationSelect.closest('dl'));
+ }
+ else {
+ elHide(applicationSelect.closest('dl'));
+ }
+
+ var description = DomTraverse.nextByTag(valueInput, 'SMALL');
+ if (this._pipDefaultFilenames[pip] !== '') {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description.defaultFilename', {
+ defaultFilename: this._pipDefaultFilenames[pip]
+ });
+ }
+ else {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description');
+ }
+ }.bind(this);
+
+ pipSelect.addEventListener('change', pipChange);
+ pipChange();
+ }.bind(this),
+ title: Language.get('wcf.acp.devtools.project.instruction.edit')
+ });
+ }
+ else {
+ UiDialog.openStatic(dialogId);
+ }
+ },
+
+ /**
+ * Opens a dialog to edit an existing set of instructions.
+ *
+ * @param {Event} event edit button click event
+ */
+ _editInstructions: function(event) {
+ var listItem = event.currentTarget.closest('li');
+
+ var instructionsId = elData(listItem, 'instructions-id');
+ var fromVersion = elData(listItem, 'fromVersion');
+
+ var dialogContent = this._instructionsEditDialogTemplate.fetch({
+ fromVersion: fromVersion
+ });
+
+ var dialogId = 'instructionsEditDialog' + instructionsId;
+ if (!UiDialog.getDialog(dialogId)) {
+ UiDialog.openStatic(dialogId, dialogContent, {
+ onSetup: function (content) {
+ var fromVersion = elBySel('input[name=fromVersion]', content);
+
+ var submit = function () {
+ if (!this._validateFromVersion(fromVersion)) {
+ return;
+ }
+
+ var instructions = elById(this._formFieldId + '_instructions' + instructionsId);
+ elData(instructions, 'fromVersion', fromVersion.value);
+
+ elByClass('jsInstructionsTitle', instructions)[0].textContent = Language.get('wcf.acp.devtools.project.instructions.type.update.title', {
+ fromVersion: fromVersion.value
+ });
+
+ DomChangeListener.trigger();
+
+ UiDialog.close(dialogId);
+ }.bind(this);
+
+ fromVersion.addEventListener('keypress', function (event) {
+ if (EventKey.Enter(event)) {
+ submit();
+ }
+ });
+
+ elBySel('button[data-type=submit]', content).addEventListener('click', submit);
+ }.bind(this),
+ title: Language.get('wcf.acp.devtools.project.instructions.edit')
+ });
+ }
+ else {
+ UiDialog.openStatic(dialogId);
+ }
+ },
+
+ /**
+ * Returns the error element for the given form field element.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getErrorElement: function(element, createIfNoNExistent) {
+ var error = DomTraverse.nextByClass(element, 'innerError');
+
+ if (error === null && createIfNoNExistent) {
+ error = elCreate('small');
+ error.className = 'innerError';
+
+ DomUtil.insertAfter(error, element);
+ }
+
+ return error;
+ },
+
+ /**
+ * Returns the error element for the from version form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getFromVersionErrorElement: function(inputField, createIfNonExistent) {
+ return this._getErrorElement(inputField, createIfNonExistent);
+ },
+
+ /**
+ * Returns the error element for the instruction type form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getInstructionsTypeErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._instructionsType, createIfNonExistent);
+ },
+
+ /**
+ * Adds an instruction after pressing ENTER in a relevant text
+ * field.
+ *
+ * @param {Event} event
+ */
+ _instructionKeyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ this._addInstruction(event);
+ }
+ },
+
+ /**
+ * Adds a set of instruction after pressing ENTER in a relevant
+ * text field.
+ *
+ * @param {Event} event
+ */
+ _instructionsKeyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ this._addInstructions(event);
+ }
+ },
+
+ /**
+ * Removes an instruction by clicking on its delete button.
+ *
+ * @param {Event} event delete button click event
+ */
+ _removeInstruction: function(event) {
+ var instruction = event.currentTarget.closest('li');
+
+ UiConfirmation.show({
+ confirm: function() {
+ elRemove(instruction);
+ },
+ message: Language.get('wcf.acp.devtools.project.instruction.delete.confirmMessages')
+ });
+ },
+
+ /**
+ * Removes a set of instructions by clicking on its delete button.
+ *
+ * @param {Event} event delete button click event
+ */
+ _removeInstructions: function(event) {
+ var instructions = event.currentTarget.closest('li');
+
+ UiConfirmation.show({
+ confirm: function() {
+ elRemove(instructions);
+ },
+ message: Language.get('wcf.acp.devtools.project.instructions.delete.confirmMessages')
+ });
+ },
+
+ /**
+ * Adds all necessary (hidden) form fields to the form when
+ * submitting the form.
+ */
+ _submit: function(event) {
+ DomTraverse.childrenByTag(this._instructionsList, 'LI').forEach(function(instructions, instructionsIndex) {
+ var namePrefix = this._formFieldId + '[' + instructionsIndex + ']';
+
+ var instructionsType = elCreate('input');
+ elAttr(instructionsType, 'type', 'hidden');
+ elAttr(instructionsType, 'name', namePrefix + '[type]')
+ instructionsType.value = elData(instructions, 'type');
+ this._form.appendChild(instructionsType);
+
+ if (instructionsType.value === 'update') {
+ var fromVersion = elCreate('input');
+ elAttr(fromVersion, 'type', 'hidden');
+ elAttr(fromVersion, 'name', this._formFieldId + '[' + instructionsIndex + '][fromVersion]')
+ fromVersion.value = elData(instructions, 'fromVersion');
+ this._form.appendChild(fromVersion);
+ }
+
+ DomTraverse.childrenByTag(elById(instructions.id + '_instructionList'), 'LI').forEach(function(instruction, instructionIndex) {
+ var namePrefix = this._formFieldId + '[' + instructionsIndex + '][instructions][' + instructionIndex + ']';
+
+ for (var property of ['pip', 'value', 'runStandalone']) {
+ var element = elCreate('input');
+ elAttr(element, 'type', 'hidden');
+ elAttr(element, 'name', namePrefix + '[' + property + ']')
+ element.value = elData(instruction, property);
+ this._form.appendChild(element);
+ }
+
+ if (_applicationPips.indexOf(elData(instruction, 'pip')) !== -1) {
+ var application = elCreate('input');
+ elAttr(application, 'type', 'hidden');
+ elAttr(application, 'name', namePrefix + '[application]')
+ application.value = elData(instruction, 'application');
+ this._form.appendChild(application);
+ }
+ }.bind(this));
+ }.bind(this));
+ },
+
+ /**
+ * Toggles the visibility of the application form field based on
+ * the selected pip for the instructions with the given id.
+ *
+ * @param {int} instructionsId id of the relevant instruction set
+ */
+ _toggleApplicationFormField: function(instructionsId) {
+ var pip = elById(this._formFieldId + '_instructions' + instructionsId + '_pip').value;
+
+ var valueDlClassList = elById(this._formFieldId + '_instructions' + instructionsId + '_value').closest('dl').classList;
+ var applicationDl = elById(this._formFieldId + '_instructions' + instructionsId + '_application').closest('dl');
+
+ if (_applicationPips.indexOf(pip) !== -1) {
+ valueDlClassList.remove('col-md-9');
+ valueDlClassList.add('col-md-7');
+ elShow(applicationDl);
+ }
+ else {
+ valueDlClassList.remove('col-md-7');
+ valueDlClassList.add('col-md-9');
+ elHide(applicationDl);
+ }
+ },
+
+ /**
+ * Toggles the visibility of the `fromVersion` form field based on
+ * the selected instructions type.
+ */
+ _toggleFromVersionFormField: function() {
+ var instructionsTypeList = this._instructionsType.closest('dl').classList;
+ var fromVersionDl = this._fromVersion.closest('dl');
+
+ if (this._instructionsType.value === 'update') {
+ instructionsTypeList.remove('col-md-10');
+ instructionsTypeList.add('col-md-5');
+ elShow(fromVersionDl);
+ }
+ else {
+ instructionsTypeList.remove('col-md-5');
+ instructionsTypeList.add('col-md-10');
+ elHide(fromVersionDl);
+ }
+ },
+
+ /**
+ * Returns `true` if the currently entered update "from version"
+ * is valid. Otherwise `false` is returned and an error message
+ * is shown.
+ *
+ * @return {boolean}
+ */
+ _validateFromVersion: function(inputField) {
+ var version = inputField.value;
+
+ if (version === '') {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.global.form.error.empty');
+
+ return false;
+ }
+
+ if (version.length > 50) {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.maximumLength');
+
+ return false;
+ }
+
+ // wildcard versions are checked on the server side
+ if (version.indexOf('*') === -1) {
+ // see `wcf\data\package\Package::isValidVersion()`
+ if (!version.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
+
+ return false;
+ }
+ }
+ else if (!version.replace('*', '0').match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
+
+ return false;
+ }
+
+ // remove outdated errors
+ var error = this._getFromVersionErrorElement(inputField);
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ },
+
+ /**
+ * Returns `true` if the entered update instructions type is valid.
+ * Otherwise `false` is returned and an error message is shown.
+ *
+ * @return {boolean}
+ */
+ _validateInstructionsType: function() {
+ if (this._instructionsType.value !== 'install' && this._instructionsType.value !== 'update') {
+ if (this._instructionsType.value === '') {
+ this._getInstructionsTypeErrorElement(true).textContent = Language.get('wcf.global.form.error.empty');
+ }
+ else {
+ this._getInstructionsTypeErrorElement(true).textContent = Language.get('wcf.global.form.error.noValidSelection');
+ }
+
+ return false;
+ }
+
+ // there may only be one set of installation instructions
+ if (this._instructionsType.value === 'install') {
+ var hasInstall = false;
+ [].forEach.call(this._instructionsList.children, function(instructions) {
+ if (elData(instructions, 'type') === 'install') {
+ hasInstall = true;
+ }
+ });
+
+ if (hasInstall) {
+ this._getInstructionsTypeErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.instructions.type.update.error.duplicate');
+
+ return false;
+ }
+ }
+
+ // remove outdated errors
+ var error = this._getInstructionsTypeErrorElement();
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ }
+ };
+
+ return Instructions;
+});
+
+/**
+ * Manages the packages entered in a devtools project optional package form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/OptionalPackages
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/OptionalPackages',['./AbstractPackageList', 'Core', 'Language'], function(AbstractPackageList, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function OptionalPackages(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ Core.inherit(OptionalPackages, AbstractPackageList, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_populateListItem
+ */
+ _populateListItem(listItem, packageData) {
+ OptionalPackages._super.prototype._populateListItem.call(this, listItem, packageData);
+
+ listItem.innerHTML = ' ' + Language.get('wcf.acp.devtools.project.optionalPackage.optionalPackage', {
+ file: packageData.file,
+ packageIdentifier: packageData.packageIdentifier
+ });
+ }
+ });
+
+ return OptionalPackages;
+});
+
+/**
+ * Manages the packages entered in a devtools project required package form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/RequiredPackages
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/RequiredPackages',['./AbstractPackageList', 'Core', 'Language'], function(AbstractPackageList, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function RequiredPackages(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ Core.inherit(RequiredPackages, AbstractPackageList, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#init
+ */
+ init: function(formFieldId, existingPackages) {
+ RequiredPackages._super.prototype.init.call(this, formFieldId, existingPackages);
+
+ this._minVersion = elById(this._formFieldId + '_minVersion');
+ if (this._minVersion === null) {
+ throw new Error("Cannot find minimum version form field for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._minVersion.addEventListener('keypress', this._keyPress.bind(this));
+
+ this._file = elById(this._formFieldId + '_file');
+ if (this._file === null) {
+ throw new Error("Cannot find file form field for required field with id '" + this._formFieldId + "'.");
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_createSubmitFields
+ */
+ _createSubmitFields: function(listElement, index) {
+ RequiredPackages._super.prototype._createSubmitFields.call(this, listElement, index);
+
+ var minVersion = elCreate('input');
+ elAttr(minVersion, 'type', 'hidden');
+ elAttr(minVersion, 'name', this._formFieldId + '[' + index + '][minVersion]')
+ minVersion.value = elData(listElement, 'min-version');
+ this._form.appendChild(minVersion);
+
+ var file = elCreate('input');
+ elAttr(file, 'type', 'hidden');
+ elAttr(file, 'name', this._formFieldId + '[' + index + '][file]')
+ file.value = elData(listElement, 'file');
+ this._form.appendChild(file);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_emptyInput
+ */
+ _emptyInput() {
+ RequiredPackages._super.prototype._emptyInput.call(this);
+
+ this._minVersion.value = '';
+ this._file.checked = false;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_getInputData
+ */
+ _getInputData: function() {
+ return Core.extend(RequiredPackages._super.prototype._getInputData.call(this), {
+ file: this._file.checked,
+ minVersion: this._minVersion.value
+ });
+ },
+
+ /**
+ * Returns the error element for the minimum version form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getMinVersionErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._minVersion, createIfNonExistent);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_populateListItem
+ */
+ _populateListItem(listItem, packageData) {
+ RequiredPackages._super.prototype._populateListItem.call(this, listItem, packageData);
+
+ elData(listItem, 'min-version', packageData.minVersion);
+ elData(listItem, 'file', ~~packageData.file);
+ listItem.innerHTML = ' ' + Language.get('wcf.acp.devtools.project.requiredPackage.requiredPackage', {
+ file: ~~packageData.file,
+ minVersion: packageData.minVersion,
+ packageIdentifier: packageData.packageIdentifier
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_validateInput
+ */
+ _validateInput: function() {
+ return RequiredPackages._super.prototype._validateInput.call(this) && this._validateVersion(
+ this._minVersion.value,
+ this._getMinVersionErrorElement.bind(this)
+ );
+ }
+ });
+
+ return RequiredPackages;
+});
+
+/**
+ * Default implementation for user interaction menu items used in the user profile.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Abstract
+ */
+define('WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Abstract',['Ajax', 'Dom/Util'], function(Ajax, DomUtil) {
+ "use strict";
+
+ /**
+ * Creates a new user profile menu item.
+ *
+ * @param {int} userId user id
+ * @param {boolean} isActive true if item is initially active
+ * @constructor
+ */
+ function UiUserProfileMenuItemAbstract(userId, isActive) {}
+ UiUserProfileMenuItemAbstract.prototype = {
+ /**
+ * Creates a new user profile menu item.
+ *
+ * @param {int} userId user id
+ * @param {boolean} isActive true if item is initially active
+ */
+ init: function(userId, isActive) {
+ this._userId = userId;
+ this._isActive = (isActive !== false);
+
+ this._initButton();
+ this._updateButton();
+ },
+
+ /**
+ * Initializes the menu item.
+ *
+ * @protected
+ */
+ _initButton: function() {
+ var button = elCreate('a');
+ button.href = '#';
+ button.addEventListener(WCF_CLICK_EVENT, this._toggle.bind(this));
+
+ var listItem = elCreate('li');
+ listItem.appendChild(button);
+
+ var menu = elBySel('.userProfileButtonMenu[data-menu="interaction"]');
+ DomUtil.prepend(listItem, menu);
+
+ this._button = button;
+ this._listItem = listItem;
+ },
+
+ /**
+ * Handles clicks on the menu item button.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _toggle: function(event) {
+ event.preventDefault();
+
+ Ajax.api(this, {
+ actionName: this._getAjaxActionName(),
+ parameters: {
+ data: {
+ userID: this._userId
+ }
+ }
+ });
+ },
+
+ /**
+ * Updates the button state and label.
+ *
+ * @protected
+ */
+ _updateButton: function() {
+ this._button.textContent = this._getLabel();
+ this._listItem.classList[(this._isActive ? 'add' : 'remove')]('active');
+ },
+
+ /**
+ * Returns the button label.
+ *
+ * @return {string} button label
+ * @protected
+ * @abstract
+ */
+ _getLabel: function() {
+ throw new Error("Implement me!");
+ },
+
+ /**
+ * Returns the Ajax action name.
+ *
+ * @return {string} ajax action name
+ * @protected
+ * @abstract
+ */
+ _getAjaxActionName: function() {
+ throw new Error("Implement me!");
+ },
+
+ /**
+ * Handles successful Ajax requests.
+ *
+ * @protected
+ * @abstract
+ */
+ _ajaxSuccess: function() {
+ throw new Error("Implement me!");
+ },
+
+ /**
+ * Returns the default Ajax request data
+ *
+ * @return {Object} ajax request data
+ * @protected
+ * @abstract
+ */
+ _ajaxSetup: function() {
+ throw new Error("Implement me!");
+ }
+ };
+
+ return UiUserProfileMenuItemAbstract;
+});
+
+define('WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Follow',['Core', 'Language', 'Ui/Notification', './Abstract'], function(Core, Language, UiNotification, UiUserProfileMenuItemAbstract) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _getLabel: function() {},
+ _getAjaxActionName: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ init: function() {},
+ _initButton: function() {},
+ _toggle: function() {},
+ _updateButton: function() {}
+ };
+ return Fake;
+ }
+
+ function UiUserProfileMenuItemFollow(userId, isActive) { this.init(userId, isActive); }
+ Core.inherit(UiUserProfileMenuItemFollow, UiUserProfileMenuItemAbstract, {
+ _getLabel: function() {
+ return Language.get('wcf.user.button.' + (this._isActive ? 'un' : '') + 'follow');
+ },
+
+ _getAjaxActionName: function() {
+ return this._isActive ? 'unfollow' : 'follow';
+ },
+
+ _ajaxSuccess: function(data) {
+ this._isActive = (data.returnValues.following ? true : false);
+ this._updateButton();
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\follow\\UserFollowAction'
+ }
+ };
+ }
+ });
+
+ return UiUserProfileMenuItemFollow;
+});
+
+define('WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Ignore',['Core', 'Language', 'Ui/Notification', './Abstract'], function(Core, Language, UiNotification, UiUserProfileMenuItemAbstract) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _getLabel: function() {},
+ _getAjaxActionName: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ init: function() {},
+ _initButton: function() {},
+ _toggle: function() {},
+ _updateButton: function() {}
+ };
+ return Fake;
+ }
+
+ function UiUserProfileMenuItemIgnore(userId, isActive) { this.init(userId, isActive); }
+ Core.inherit(UiUserProfileMenuItemIgnore, UiUserProfileMenuItemAbstract, {
+ _getLabel: function() {
+ return Language.get('wcf.user.button.' + (this._isActive ? 'un' : '') + 'ignore');
+ },
+
+ _getAjaxActionName: function() {
+ return this._isActive ? 'unignore' : 'ignore';
+ },
+
+ _ajaxSuccess: function(data) {
+ this._isActive = (data.returnValues.isIgnoredUser ? true : false);
+ this._updateButton();
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\ignore\\UserIgnoreAction'
+ }
+ };
+ }
+ });
+
+ return UiUserProfileMenuItemIgnore;
+});
+
+/*
+ * Polyfill for `Element.prototype.matches()` and `Element.prototype.closest()`
+ * Copyright (c) 2015 Jonathan Neal - https://github.com/jonathantneal/closest
+ * License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/)
+ */
+(function(ELEMENT) {
+ ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector;
+
+ ELEMENT.closest = ELEMENT.closest || function closest(selector) {
+ var element = this;
+
+ while (element) {
+ if (element.matches(selector)) {
+ break;
+ }
+
+ element = element.parentElement;
+ }
+
+ return element;
+ };
+}(Element.prototype));
+
+define("closest", function(){});
+
+(function(window) {
+ var orgRequire = window.require;
+ var queue = [];
+ var counter = 0;
+
+ window.orgRequire = orgRequire
+
+ window.require = function(dependencies, callback, errBack) {
+ if (!Array.isArray(dependencies)) {
+ return orgRequire.apply(window, arguments);
+ }
+
+ var promise = new Promise(function (resolve, reject) {
+ var i = counter++;
+ queue.push(i);
+
+ orgRequire(dependencies, function () {
+ var args = arguments;
+
+ queue[queue.indexOf(i)] = function() { resolve(args); };
+
+ executeCallbacks();
+ }, function (err) {
+ queue[queue.indexOf(i)] = function() { reject(err); };
+
+ executeCallbacks();
+ });
+ });
+
+ if (callback) {
+ promise = promise.then(function (objects) {
+ return callback.apply(window, objects);
+ });
+ }
+ if (errBack) {
+ promise.catch(errBack);
+ }
+
+ return promise;
+ };
+ window.require.config = orgRequire.config;
+
+ function executeCallbacks() {
+ while (queue.length) {
+ if (typeof queue[0] !== 'function') {
+ break;
+ }
+
+ queue.shift()();
+ }
+ }
+})(window);
+
+define("require.linearExecution", function(){});
+
// promise.min.js
-!function(e){function n(){}function t(e,n){return function(){e.apply(n,arguments)}}function o(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],s(e,this)}function i(e,n){for(;3===e._state;)e=e._value;return 0===e._state?void e._deferreds.push(n):(e._handled=!0,void o._immediateFn(function(){var t=1===e._state?n.onFulfilled:n.onRejected;if(null===t)return void(1===e._state?r:u)(n.promise,e._value);var o;try{o=t(e._value)}catch(i){return void u(n.promise,i)}r(n.promise,o)}))}function r(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var i=n.then;if(n instanceof o)return e._state=3,e._value=n,void f(e);if("function"==typeof i)return void s(t(i,n),e)}e._state=1,e._value=n,f(e)}catch(r){u(e,r)}}function u(e,n){e._state=2,e._value=n,f(e)}function f(e){2===e._state&&0===e._deferreds.length&&o._immediateFn(function(){e._handled||o._unhandledRejectionFn(e._value)});for(var n=0,t=e._deferreds.length;n<t;n++)i(e,e._deferreds[n]);e._deferreds=null}function c(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}function s(e,n){var t=!1;try{e(function(e){t||(t=!0,r(n,e))},function(e){t||(t=!0,u(n,e))})}catch(o){if(t)return;t=!0,u(n,o)}}var a=setTimeout;o.prototype["catch"]=function(e){return this.then(null,e)},o.prototype.then=function(e,t){var o=new this.constructor(n);return i(this,new c(e,t,o)),o},o.all=function(e){var n=Array.prototype.slice.call(e);return new o(function(e,t){function o(r,u){try{if(u&&("object"==typeof u||"function"==typeof u)){var f=u.then;if("function"==typeof f)return void f.call(u,function(e){o(r,e)},t)}n[r]=u,0===--i&&e(n)}catch(c){t(c)}}if(0===n.length)return e([]);for(var i=n.length,r=0;r<n.length;r++)o(r,n[r])})},o.resolve=function(e){return e&&"object"==typeof e&&e.constructor===o?e:new o(function(n){n(e)})},o.reject=function(e){return new o(function(n,t){t(e)})},o.race=function(e){return new o(function(n,t){for(var o=0,i=e.length;o<i;o++)e[o].then(n,t)})},o._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){a(e,0)},o._unhandledRejectionFn=function(e){"undefined"!=typeof console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)},o._setImmediateFn=function(e){o._immediateFn=e},o._setUnhandledRejectionFn=function(e){o._unhandledRejectionFn=e},"undefined"!=typeof module&&module.exports?module.exports=o:e.Promise||(e.Promise=o)}(this);
+!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(n){var t=this.constructor;return this.then(function(e){return t.resolve(n()).then(function(){return e})},function(e){return t.resolve(n()).then(function(){return t.reject(e)})})}var n=setTimeout;function o(){}function f(e){if(!(this instanceof f))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=undefined,this._deferreds=[],l(e,this)}function r(o,r){for(;3===o._state;)o=o._value;0!==o._state?(o._handled=!0,f._immediateFn(function(){var e=1===o._state?r.onFulfilled:r.onRejected;if(null!==e){var n;try{n=e(o._value)}catch(t){return void u(r.promise,t)}i(r.promise,n)}else(1===o._state?i:u)(r.promise,o._value)})):o._deferreds.push(r)}function i(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var t=n.then;if(n instanceof f)return e._state=3,e._value=n,void c(e);if("function"==typeof t)return void l((o=t,r=n,function(){o.apply(r,arguments)}),e)}e._state=1,e._value=n,c(e)}catch(i){u(e,i)}var o,r}function u(e,n){e._state=2,e._value=n,c(e)}function c(e){2===e._state&&0===e._deferreds.length&&f._immediateFn(function(){e._handled||f._unhandledRejectionFn(e._value)});for(var n=0,t=e._deferreds.length;n<t;n++)r(e,e._deferreds[n]);e._deferreds=null}function a(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}function l(e,n){var t=!1;try{e(function(e){t||(t=!0,i(n,e))},function(e){t||(t=!0,u(n,e))})}catch(o){if(t)return;t=!0,u(n,o)}}f.prototype["catch"]=function(e){return this.then(null,e)},f.prototype.then=function(e,n){var t=new this.constructor(o);return r(this,new a(e,n,t)),t},f.prototype["finally"]=e,f.all=function(n){return new f(function(r,i){if(!n||"undefined"==typeof n.length)throw new TypeError("Promise.all accepts an array");var f=Array.prototype.slice.call(n);if(0===f.length)return r([]);var u=f.length;function c(n,e){try{if(e&&("object"==typeof e||"function"==typeof e)){var t=e.then;if("function"==typeof t)return void t.call(e,function(e){c(n,e)},i)}f[n]=e,0==--u&&r(f)}catch(o){i(o)}}for(var e=0;e<f.length;e++)c(e,f[e])})},f.resolve=function(n){return n&&"object"==typeof n&&n.constructor===f?n:new f(function(e){e(n)})},f.reject=function(t){return new f(function(e,n){n(t)})},f.race=function(r){return new f(function(e,n){for(var t=0,o=r.length;t<o;t++)r[t].then(e,n)})},f._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){n(e,0)},f._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var t=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw Error("unable to locate global object")}();"Promise"in t?t.Promise.prototype["finally"]||(t.Promise.prototype["finally"]=e):t.Promise=f});
+
// WoltLabSuite.Core.tiny.min.js
-var requirejs,require,define;!function(global,Promise,undef){function commentReplace(e,t){return t||""}function hasProp(e,t){return hasOwn.call(e,t)}function getOwn(e,t){return e&&hasProp(e,t)&&e[t]}function obj(){return Object.create(null)}function eachProp(e,t){var n;for(n in e)if(hasProp(e,n)&&t(e[n],n))break}function mixin(e,t,n,i){return t&&eachProp(t,function(t,o){!n&&hasProp(e,o)||(!i||"object"!=typeof t||!t||Array.isArray(t)||"function"==typeof t||t instanceof RegExp?e[o]=t:(e[o]||(e[o]={}),mixin(e[o],t,n,i)))}),e}function getGlobal(e){if(!e)return e;var t=global;return e.split(".").forEach(function(e){t=t[e]}),t}function newContext(e){function t(e){var t,n,i=e.length;for(t=0;t<i;t++)if("."===(n=e[t]))e.splice(t,1),t-=1;else if(".."===n){if(0===t||1===t&&".."===e[2]||".."===e[t-1])continue;t>0&&(e.splice(t-1,2),t-=2)}}function n(e,n,i){var o,a,r,s,l,c,u,d,h,f,p=n&&n.split("/"),g=p,m=M.map,v=m&&m["*"];if(e&&(e=e.split("/"),c=e.length-1,M.nodeIdCompat&&jsSuffixRegExp.test(e[c])&&(e[c]=e[c].replace(jsSuffixRegExp,"")),"."===e[0].charAt(0)&&p&&(g=p.slice(0,p.length-1),e=g.concat(e)),t(e),e=e.join("/")),i&&m&&(p||v)){a=e.split("/");e:for(r=a.length;r>0;r-=1){if(l=a.slice(0,r).join("/"),p)for(s=p.length;s>0;s-=1)if((o=getOwn(m,p.slice(0,s).join("/")))&&(o=getOwn(o,l))){u=o,d=r;break e}!h&&v&&getOwn(v,l)&&(h=getOwn(v,l),f=r)}!u&&h&&(u=h,d=f),u&&(a.splice(0,d,u),e=a.join("/"))}return getOwn(M.pkgs,e)||e}function i(e){function t(){var t;return e.init&&(t=e.init.apply(global,arguments)),t||e.exports&&getGlobal(e.exports)}return t}function o(e){var t,n,i,o;for(t=0;t<queue.length;t+=1){if("string"!=typeof queue[t][0]){if(!e)break;queue[t].unshift(e),e=undef}i=queue.shift(),n=i[0],t-=1,n in k||n in I||(n in U?C.apply(undef,i):I[n]=i)}e&&(o=getOwn(M.shim,e)||{},C(e,o.deps||[],o.exportsFn))}function a(e,t){var i=function(n,a,r,s){var l,c;if(t&&o(),"string"==typeof n){if(E[n])return E[n](e);if(!((l=L(n,e,!0).id)in k))throw new Error("Not loaded: "+l);return k[l]}return n&&!Array.isArray(n)&&(c=n,n=undef,Array.isArray(a)&&(n=a,a=r,r=s),t)?i.config(c)(n,a,r):(a=a||function(){return slice.call(arguments,0)},V.then(function(){return o(),C(undef,n||[],a,r,e)}))};return i.isBrowser="undefined"!=typeof document&&"undefined"!=typeof navigator,i.nameToUrl=function(e,t,n){var o,a,r,s,l,c,u,d=getOwn(M.pkgs,e);if(d&&(e=d),u=getOwn(F,e))return i.nameToUrl(u,t,n);if(urlRegExp.test(e))l=e+(t||"");else{for(o=M.paths,a=e.split("/"),r=a.length;r>0;r-=1)if(s=a.slice(0,r).join("/"),c=getOwn(o,s)){Array.isArray(c)&&(c=c[0]),a.splice(0,r,c);break}l=a.join("/"),l+=t||(/^data\:|^blob\:|\?/.test(l)||n?"":".js"),l=("/"===l.charAt(0)||l.match(/^[\w\+\.\-]+:/)?"":M.baseUrl)+l}return M.urlArgs&&!/^blob\:/.test(l)?l+M.urlArgs(e,l):l},i.toUrl=function(t){var o,a=t.lastIndexOf("."),r=t.split("/")[0],s="."===r||".."===r;return-1!==a&&(!s||a>1)&&(o=t.substring(a,t.length),t=t.substring(0,a)),i.nameToUrl(n(t,e),o,!0)},i.defined=function(t){return L(t,e,!0).id in k},i.specified=function(t){return(t=L(t,e,!0).id)in k||t in U},i}function r(e,t,n){e&&(k[e]=n,requirejs.onResourceLoad&&requirejs.onResourceLoad(T,t.map,t.deps)),t.finished=!0,t.resolve(n)}function s(e,t){e.finished=!0,e.rejected=!0,e.reject(t)}function l(e){return function(t){return n(t,e,!0)}}function c(e){e.factoryCalled=!0;var t,n=e.map.id;try{t=T.execCb(n,e.factory,e.values,k[n])}catch(t){return s(e,t)}n?t===undef&&(e.cjsModule?t=e.cjsModule.exports:e.usingExports&&(t=k[n])):A.splice(A.indexOf(e),1),r(n,e,t)}function u(e,t){this.rejected||this.depDefined[t]||(this.depDefined[t]=!0,this.depCount+=1,this.values[t]=e,this.depending||this.depCount!==this.depMax||c(this))}function d(e,t){var n={};return n.promise=new Promise(function(t,i){n.resolve=t,n.reject=function(t){e||A.splice(A.indexOf(n),1),i(t)}}),n.map=e?t||L(e):{},n.depCount=0,n.depMax=0,n.values=[],n.depDefined=[],n.depFinished=u,n.map.pr&&(n.deps=[L(n.map.pr)]),n}function h(e,t){var n;return e?(n=e in U&&U[e])||(n=U[e]=d(e,t)):(n=d(),A.push(n)),n}function f(e,t){return function(n){e.rejected||(n.dynaId||(n.dynaId="id"+(B+=1),n.requireModules=[t]),s(e,n))}}function p(e,t,n,i){n.depMax+=1,S(e,t).then(function(e){n.depFinished(e,i)},f(n,e.id)).catch(f(n,n.map.id))}function g(e){function t(t){n||r(e,h(e),t)}var n;return t.error=function(t){h(e).reject(t)},t.fromText=function(t,i){var a=h(e),r=L(L(e).n),l=r.id;n=!0,a.factory=function(e,t){return t},i&&(t=i),hasProp(M.config,e)&&(M.config[l]=M.config[e]);try{w.exec(t)}catch(e){s(a,new Error("fromText eval for "+l+" failed: "+e))}o(l),a.deps=[r],p(r,null,a,a.deps.length)},t}function m(e,t,n){e.load(t.n,a(n),g(t.id),M)}function v(e){var t,n=e?e.indexOf("!"):-1;return n>-1&&(t=e.substring(0,n),e=e.substring(n+1,e.length)),[t,e]}function b(e,t,n){var i=e.map.id;t[i]=!0,!e.finished&&e.deps&&e.deps.forEach(function(i){var o=i.id,a=!hasProp(E,o)&&h(o,i);!a||a.finished||n[o]||(hasProp(t,o)?e.deps.forEach(function(t,n){t.id===o&&e.depFinished(k[o],n)}):b(a,t,n))}),n[i]=!0}function _(e){var t,n,i,o=[],a=1e3*M.waitSeconds,r=a&&O+a<(new Date).getTime();if(0===j&&(e?e.finished||b(e,{},{}):A.length&&A.forEach(function(e){b(e,{},{})})),r){for(n in U)i=U[n],i.finished||o.push(i.map.id);t=new Error("Timeout for modules: "+o),t.requireModules=o,w.onError(t)}else(j||A.length)&&(x||(x=!0,setTimeout(function(){x=!1,_()},70)))}function y(e){return setTimeout(function(){e.dynaId&&H[e.dynaId]||(H[e.dynaId]=!0,w.onError(e))}),e}var w,C,L,S,E,x,D,T,k=obj(),I=obj(),M={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},N=obj(),A=[],U=obj(),P=obj(),W=obj(),j=0,O=(new Date).getTime(),B=0,H=obj(),R=obj(),F=obj(),V=Promise.resolve();return D="function"==typeof importScripts?function(e){var t=e.url;R[t]||(R[t]=!0,h(e.id),importScripts(t),o(e.id))}:function(e){var t,n=e.id,i=e.url;R[i]||(R[i]=!0,t=document.createElement("script"),t.setAttribute("data-requiremodule",n),t.type=M.scriptType||"text/javascript",t.charset="utf-8",t.async=!0,j+=1,t.addEventListener("load",function(){j-=1,o(n)},!1),t.addEventListener("error",function(){j-=1;var e,i=getOwn(M.paths,n);if(i&&Array.isArray(i)&&i.length>1){t.parentNode.removeChild(t),i.shift();var o=h(n);o.map=L(n),o.map.url=w.nameToUrl(n),D(o.map)}else e=new Error("Load failed: "+n+": "+t.src),e.requireModules=[n],h(n).reject(e)},!1),t.src=i,10===document.documentMode?asap.then(function(){document.head.appendChild(t)}):document.head.appendChild(t))},S=function(e,t){var n,i,o=e.id,a=M.shim[o];if(o in I)n=I[o],delete I[o],C.apply(undef,n);else if(!(o in U))if(e.pr){if(!(i=getOwn(F,o)))return S(L(e.pr)).then(function(n){var i=e.prn?e:L(o,t,!0),a=i.id,r=getOwn(M.shim,a);return a in W||(W[a]=!0,r&&r.deps?w(r.deps,function(){m(n,i,t)}):m(n,i,t)),h(a).promise});e.url=w.nameToUrl(i),D(e)}else a&&a.deps?w(a.deps,function(){D(e)}):D(e);return h(o).promise},L=function(e,t,i){if("string"!=typeof e)return e;var o,a,r,s,c,u,d=e+" & "+(t||"")+" & "+!!i;return r=v(e),s=r[0],e=r[1],!s&&d in N?N[d]:(s&&(s=n(s,t,i),o=s in k&&k[s]),s?o&&o.normalize?(e=o.normalize(e,l(t)),u=!0):e=-1===e.indexOf("!")?n(e,t,i):e:(e=n(e,t,i),r=v(e),s=r[0],e=r[1],a=w.nameToUrl(e)),c={id:s?s+"!"+e:e,n:e,pr:s,url:a,prn:s&&u},s||(N[d]=c),c)},E={require:function(e){return a(e)},exports:function(e){var t=k[e];return void 0!==t?t:k[e]={}},module:function(e){return{id:e,uri:"",exports:E.exports(e),config:function(){return getOwn(M.config,e)||{}}}}},C=function(e,t,n,i,o){if(e){if(e in P)return;P[e]=!0}var a=h(e);return t&&!Array.isArray(t)&&(n=t,t=[]),t=t?slice.call(t,0):null,i||(hasProp(M,"defaultErrback")?M.defaultErrback&&(i=M.defaultErrback):i=y),i&&a.promise.catch(i),o=o||e,"function"==typeof n?(!t.length&&n.length&&(n.toString().replace(commentRegExp,commentReplace).replace(cjsRequireRegExp,function(e,n){t.push(n)}),t=(1===n.length?["require"]:["require","exports","module"]).concat(t)),a.factory=n,a.deps=t,a.depending=!0,t.forEach(function(n,i){var r;t[i]=r=L(n,o,!0),n=r.id,"require"===n?a.values[i]=E.require(e):"exports"===n?(a.values[i]=E.exports(e),a.usingExports=!0):"module"===n?a.values[i]=a.cjsModule=E.module(e):void 0===n?a.values[i]=void 0:p(r,o,a,i)}),a.depending=!1,a.depCount===a.depMax&&c(a)):e&&r(e,a,n),O=(new Date).getTime(),e||_(a),a.promise},w=a(null,!0),w.config=function(t){if(t.context&&t.context!==e){var n=getOwn(contexts,t.context);return n?n.req.config(t):newContext(t.context).config(t)}if(N=obj(),t.baseUrl&&"/"!==t.baseUrl.charAt(t.baseUrl.length-1)&&(t.baseUrl+="/"),"string"==typeof t.urlArgs){var o=t.urlArgs;t.urlArgs=function(e,t){return(-1===t.indexOf("?")?"?":"&")+o}}var a=M.shim,r={paths:!0,bundles:!0,config:!0,map:!0};return eachProp(t,function(e,t){r[t]?(M[t]||(M[t]={}),mixin(M[t],e,!0,!0)):M[t]=e}),t.bundles&&eachProp(t.bundles,function(e,t){e.forEach(function(e){e!==t&&(F[e]=t)})}),t.shim&&(eachProp(t.shim,function(e,t){Array.isArray(e)&&(e={deps:e}),!e.exports&&!e.init||e.exportsFn||(e.exportsFn=i(e)),a[t]=e}),M.shim=a),t.packages&&t.packages.forEach(function(e){var t,n;e="string"==typeof e?{name:e}:e,n=e.name,t=e.location,t&&(M.paths[n]=e.location),M.pkgs[n]=e.name+"/"+(e.main||"main").replace(currDirRegExp,"").replace(jsSuffixRegExp,"")}),(t.deps||t.callback)&&w(t.deps,t.callback),w},w.onError=function(e){throw e},T={id:e,defined:k,waiting:I,config:M,deferreds:U,req:w,execCb:function(e,t,n,i){return t.apply(i,n)}},contexts[e]=T,w}if(!Promise)throw new Error("No Promise implementation available");var topReq,dataMain,src,subPath,bootstrapConfig=requirejs||require,hasOwn=Object.prototype.hasOwnProperty,contexts={},queue=[],currDirRegExp=/^\.\//,urlRegExp=/^\/|\:|\?|\.js$/,commentRegExp=/\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,cjsRequireRegExp=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,jsSuffixRegExp=/\.js$/,slice=Array.prototype.slice;if("function"!=typeof requirejs){var asap=Promise.resolve(void 0);requirejs=topReq=newContext("_"),"function"!=typeof require&&(require=topReq),topReq.exec=function(text){return eval(text)},topReq.contexts=contexts,define=function(){queue.push(slice.call(arguments,0))},define.amd={jQuery:!0},bootstrapConfig&&topReq.config(bootstrapConfig),topReq.isBrowser&&!contexts._.config.skipDataMain&&(dataMain=document.querySelectorAll("script[data-main]")[0],(dataMain=dataMain&&dataMain.getAttribute("data-main"))&&(dataMain=dataMain.replace(jsSuffixRegExp,""),bootstrapConfig&&bootstrapConfig.baseUrl||-1!==dataMain.indexOf("!")||(src=dataMain.split("/"),dataMain=src.pop(),subPath=src.length?src.join("/")+"/":"./",topReq.config({baseUrl:subPath})),topReq([dataMain])))}}(this,"undefined"!=typeof Promise?Promise:void 0),define("requireLib",function(){}),requirejs.config({paths:{enquire:"3rdParty/enquire",favico:"3rdParty/favico","perfect-scrollbar":"3rdParty/perfect-scrollbar"},shim:{enquire:{exports:"enquire"},favico:{exports:"Favico"},"perfect-scrollbar":{exports:"PerfectScrollbar"}},map:{"*":{Ajax:"WoltLabSuite/Core/Ajax",AjaxJsonp:"WoltLabSuite/Core/Ajax/Jsonp",AjaxRequest:"WoltLabSuite/Core/Ajax/Request",CallbackList:"WoltLabSuite/Core/CallbackList",ColorUtil:"WoltLabSuite/Core/ColorUtil",Core:"WoltLabSuite/Core/Core",DateUtil:"WoltLabSuite/Core/Date/Util",Devtools:"WoltLabSuite/Core/Devtools",Dictionary:"WoltLabSuite/Core/Dictionary","Dom/ChangeListener":"WoltLabSuite/Core/Dom/Change/Listener","Dom/Traverse":"WoltLabSuite/Core/Dom/Traverse","Dom/Util":"WoltLabSuite/Core/Dom/Util",Environment:"WoltLabSuite/Core/Environment",EventHandler:"WoltLabSuite/Core/Event/Handler",EventKey:"WoltLabSuite/Core/Event/Key",Language:"WoltLabSuite/Core/Language",List:"WoltLabSuite/Core/List",ObjectMap:"WoltLabSuite/Core/ObjectMap",Permission:"WoltLabSuite/Core/Permission",StringUtil:"WoltLabSuite/Core/StringUtil","Ui/Alignment":"WoltLabSuite/Core/Ui/Alignment","Ui/CloseOverlay":"WoltLabSuite/Core/Ui/CloseOverlay","Ui/Confirmation":"WoltLabSuite/Core/Ui/Confirmation","Ui/Dialog":"WoltLabSuite/Core/Ui/Dialog","Ui/Notification":"WoltLabSuite/Core/Ui/Notification","Ui/ReusableDropdown":"WoltLabSuite/Core/Ui/Dropdown/Reusable","Ui/Screen":"WoltLabSuite/Core/Ui/Screen","Ui/Scroll":"WoltLabSuite/Core/Ui/Scroll","Ui/SimpleDropdown":"WoltLabSuite/Core/Ui/Dropdown/Simple","Ui/TabMenu":"WoltLabSuite/Core/Ui/TabMenu",Upload:"WoltLabSuite/Core/Upload",User:"WoltLabSuite/Core/User"}},waitSeconds:0}),define("jquery",[],function(){return window.jQuery}),define("require.config",function(){}),function(e,t){e.elAttr=function(e,t,n){if(void 0===n)return e.getAttribute(t)||"";e.setAttribute(t,n)},e.elAttrBool=function(e,t){var n=elAttr(e,t);return"1"===n||"true"===n},e.elByClass=function(e,n){return(n||t).getElementsByClassName(e)},e.elById=function(e){return t.getElementById(e)},e.elBySel=function(e,n){return(n||t).querySelector(e)},e.elBySelAll=function(e,n,i){var o=(n||t).querySelectorAll(e);return"function"==typeof i&&Array.prototype.forEach.call(o,i),o},e.elByTag=function(e,n){return(n||t).getElementsByTagName(e)},e.elCreate=function(e){return t.createElement(e)},e.elClosest=function(e,t){if(!(e instanceof Node))throw new TypeError("Provided element is not a Node.");return e.nodeType===Node.TEXT_NODE&&null===(e=e.parentNode)?null:("string"!=typeof t&&(t=""),0===t.length?e:e.closest(t))},e.elData=function(e,t,n){if(t="data-"+t,void 0===n)return e.getAttribute(t)||"";e.setAttribute(t,n)},e.elDataBool=function(e,t){var n=elData(e,t);return"1"===n||"true"===n},e.elHide=function(e){e.style.setProperty("display","none","")},e.elInnerError=function(e,t,n){var i=e.parentNode;if(null===i)throw new Error("Only elements that have a parent element or document are valid.");if("string"!=typeof t){if(void 0!==t&&null!==t&&!1!==t)throw new TypeError("The error message must be a string; `false`, `null` or `undefined` can be used as a substitute for an empty string.");t=""}var o=e.nextElementSibling;return null!==o&&"SMALL"===o.nodeName&&o.classList.contains("innerError")||(""===t?o=null:(o=elCreate("small"),o.className="innerError",i.insertBefore(o,e.nextSibling))),""===t?null!==o&&(i.removeChild(o),o=null):o[n?"innerHTML":"textContent"]=t,o},e.elRemove=function(e){e.parentNode.removeChild(e)},e.elShow=function(e){e.style.removeProperty("display")},e.elToggle=function(e){"none"===e.style.getPropertyValue("display")?elShow(e):elHide(e)},e.forEach=function(e,t){for(var n=0,i=e.length;n<i;n++)t(e[n],n)},e.objOwns=function(e,t){return e.hasOwnProperty(t)};"touchstart"in t.documentElement||"ontouchstart"in e||navigator.MaxTouchPoints>0||navigator.msMaxTouchPoints;Object.defineProperty(e,"WCF_CLICK_EVENT",{value:"click"}),function(){function t(){e.history.state&&e.history.state.name&&"initial"!==e.history.state.name?(e.history.replaceState({name:"skip",depth:++n},""),e.history.back(),setTimeout(t,1)):e.history.replaceState({name:"initial"},"")}var n=0;t(),e.addEventListener("popstate",function(t){t.state&&t.state.name&&"skip"===t.state.name&&e.history.go(t.state.depth)})}(),e.String.prototype.hashCode=function(){var e,t=0;if(this.length)for(var n=0,i=this.length;n<i;n++)e=this.charCodeAt(n),t=(t<<5)-t+e,t&=t;return t}}(window,document),define("wcf.globalHelper",function(){}),define("WoltLabSuite/Core/Core",[],function(){"use strict";var e=function(e){return"object"==typeof e&&(Array.isArray(e)||i.isPlainObject(e))?t(e):e},t=function(t){if(!t)return null;if(Array.isArray(t))return t.slice();var n={};for(var i in t)t.hasOwnProperty(i)&&void 0!==t[i]&&(n[i]=e(t[i]));return n},n="wsc"+window.WCF_PATH.hashCode()+"-",i={clone:function(t){return e(t)},convertLegacyUrl:function(e){return e.replace(/^index\.php\/(.*?)\/\?/,function(e,t){var n=t.split(/([A-Z][a-z0-9]+)/);t="";for(var i=0,o=n.length;i<o;i++){var a=n[i].trim();a.length&&(t.length&&(t+="-"),t+=a.toLowerCase())}return"index.php?"+t+"/&"})},extend:function(e){e=e||{};for(var t=this.clone(e),n=1,i=arguments.length;n<i;n++){var o=arguments[n];if(o)for(var a in o)objOwns(o,a)&&(Array.isArray(o[a])||"object"!=typeof o[a]?t[a]=o[a]:this.isPlainObject(o[a])?t[a]=this.extend(e[a],o[a]):t[a]=o[a])}return t},inherit:function(e,t,n){if(void 0===e||null===e)throw new TypeError("The constructor must not be undefined or null.");if(void 0===t||null===t)throw new TypeError("The super constructor must not be undefined or null.");if(void 0===t.prototype)throw new TypeError("The super constructor must have a prototype.");e._super=t,e.prototype=i.extend(Object.create(t.prototype,{constructor:{configurable:!0,enumerable:!1,value:e,writable:!0}}),n||{})},isPlainObject:function(e){return"object"==typeof e&&null!==e&&!e.nodeType&&Object.getPrototypeOf(e)===Object.prototype},getType:function(e){return Object.prototype.toString.call(e).replace(/^\[object (.+)\]$/,"$1")},getUuid:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)})},serialize:function(e,t){var n=[];for(var i in e)if(objOwns(e,i)){var o=t?t+"["+i+"]":i,a=e[i];"object"==typeof a?n.push(this.serialize(a,o)):n.push(encodeURIComponent(o)+"="+encodeURIComponent(a))}return n.join("&")},triggerEvent:function(e,t){var n;try{n=new Event(t,{bubbles:!0,cancelable:!0})}catch(e){n=document.createEvent("Event"),n.initEvent(t,!0,!0)}e.dispatchEvent(n)},getStoragePrefix:function(){return n}};return i}),define("WoltLabSuite/Core/Dictionary",["Core"],function(e){"use strict";function t(){this._dictionary=n?new Map:{}}var n=objOwns(window,"Map")&&"function"==typeof window.Map;return t.prototype={set:function(e,t){if("number"==typeof e&&(e=e.toString()),"string"!=typeof e)throw new TypeError("Only strings can be used as keys, rejected '"+e+"' ("+typeof e+").");n?this._dictionary.set(e,t):this._dictionary[e]=t},delete:function(e){"number"==typeof e&&(e=e.toString()),n?this._dictionary.delete(e):this._dictionary[e]=void 0},has:function(e){return"number"==typeof e&&(e=e.toString()),n?this._dictionary.has(e):objOwns(this._dictionary,e)&&void 0!==this._dictionary[e]},get:function(e){if("number"==typeof e&&(e=e.toString()),this.has(e))return n?this._dictionary.get(e):this._dictionary[e]},forEach:function(e){if("function"!=typeof e)throw new TypeError("forEach() expects a callback as first parameter.");if(n)this._dictionary.forEach(e);else for(var t=Object.keys(this._dictionary),i=0,o=t.length;i<o;i++)e(this._dictionary[t[i]],t[i])},merge:function(){for(var e=0,n=arguments.length;e<n;e++){var i=arguments[e];if(!(i instanceof t))throw new TypeError("Expected an object of type Dictionary, but argument "+e+" is not.");i.forEach(function(e,t){this.set(t,e)}.bind(this))}},toObject:function(){if(!n)return e.clone(this._dictionary);var t={};return this._dictionary.forEach(function(e,n){t[n]=e}),t}},t.fromObject=function(e){var n=new t;for(var i in e)objOwns(e,i)&&n.set(i,e[i]);return n},Object.defineProperty(t.prototype,"size",{enumerable:!1,configurable:!0,get:function(){return n?this._dictionary.size:Object.keys(this._dictionary).length}}),t}),define("WoltLabSuite/Core/Template.grammar",["require"],function(e){var t=function(e,t,n,i){for(n=n||{},i=e.length;i--;n[e[i]]=t);return n},n=[2,37],i=[5,9,11,12,13,18,19,21,22,23,25,26,27,28,30,31,32,33,35,37,39],o=[1,24],a=[1,25],r=[1,31],s=[1,29],l=[1,30],c=[1,26],u=[1,27],d=[1,33],h=[11,12,15,40,41,45,47,49,50,52],f=[9,11,12,13,18,19,21,23,26,28,30,31,32,33,35,37],p=[11,12,15,40,41,44,45,46,47,49,50,52],g=[18,35,37],m=[12,15],v={trace:function(){},yy:{},symbols_:{error:2,TEMPLATE:3,CHUNK_STAR:4,EOF:5,CHUNK_STAR_repetition0:6,CHUNK:7,PLAIN_ANY:8,T_LITERAL:9,COMMAND:10,T_ANY:11,T_WS:12,"{if":13,COMMAND_PARAMETERS:14,"}":15,COMMAND_repetition0:16,COMMAND_option0:17,"{/if}":18,"{include":19,COMMAND_PARAMETER_LIST:20,"{implode":21,"{/implode}":22,"{foreach":23,COMMAND_option1:24,"{/foreach}":25,"{lang}":26,"{/lang}":27,"{":28,VARIABLE:29,"{#":30,"{@":31,"{ldelim}":32,"{rdelim}":33,ELSE:34,"{else}":35,ELSE_IF:36,"{elseif":37,FOREACH_ELSE:38,"{foreachelse}":39,T_VARIABLE:40,T_VARIABLE_NAME:41,VARIABLE_repetition0:42,VARIABLE_SUFFIX:43,"[":44,"]":45,".":46,"(":47,VARIABLE_SUFFIX_option0:48,")":49,"=":50,COMMAND_PARAMETER_VALUE:51,T_QUOTED_STRING:52,COMMAND_PARAMETERS_repetition_plus0:53,COMMAND_PARAMETER:54,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",9:"T_LITERAL",11:"T_ANY",12:"T_WS",13:"{if",15:"}",18:"{/if}",19:"{include",21:"{implode",22:"{/implode}",23:"{foreach",25:"{/foreach}",26:"{lang}",27:"{/lang}",28:"{",30:"{#",31:"{@",32:"{ldelim}",33:"{rdelim}",35:"{else}",37:"{elseif",39:"{foreachelse}",40:"T_VARIABLE",41:"T_VARIABLE_NAME",44:"[",45:"]",46:".",47:"(",49:")",50:"=",52:"T_QUOTED_STRING"},productions_:[0,[3,2],[4,1],[7,1],[7,1],[7,1],[8,1],[8,1],[10,7],[10,3],[10,5],[10,6],[10,3],[10,3],[10,3],[10,3],[10,1],[10,1],[34,2],[36,4],[38,2],[29,3],[43,3],[43,2],[43,3],[20,5],[20,3],[51,1],[51,1],[14,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,3],[6,0],[6,2],[16,0],[16,2],[17,0],[17,1],[24,0],[24,1],[42,0],[42,2],[48,0],[48,1],[53,1],[53,2]],performAction:function(e,t,n,i,o,a,r){var s=a.length-1;switch(o){case 1:return a[s-1]+";";case 2:var l=a[s].reduce(function(e,t){return t.encode&&!e[1]?e[0]+=" + '"+t.value:t.encode&&e[1]?e[0]+=t.value:!t.encode&&e[1]?e[0]+="' + "+t.value:t.encode||e[1]||(e[0]+=" + "+t.value),e[1]=t.encode,e},["''",!1]);l[1]&&(l[0]+="'"),this.$=l[0];break;case 3:case 4:this.$={encode:!0,value:a[s].replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/g,"\\n")};break;case 5:this.$={encode:!1,value:a[s]};break;case 8:this.$="(function() { if ("+a[s-5]+") { return "+a[s-3]+"; } "+a[s-2].join(" ")+" "+(a[s-1]||"")+" return ''; })()";break;case 9:if(!a[s-1].file)throw new Error("Missing parameter file");this.$=a[s-1].file+".fetch(v)";break;case 10:if(!a[s-3].from)throw new Error("Missing parameter from");if(!a[s-3].item)throw new Error("Missing parameter item");a[s-3].glue||(a[s-3].glue="', '"),this.$="(function() { return "+a[s-3].from+".map(function(item) { v["+a[s-3].item+"] = item; return "+a[s-1]+"; }).join("+a[s-3].glue+"); })()";break;case 11:if(!a[s-4].from)throw new Error("Missing parameter from");if(!a[s-4].item)throw new Error("Missing parameter item");this.$="(function() {var looped = false, result = '';if ("+a[s-4].from+" instanceof Array) {for (var i = 0; i < "+a[s-4].from+".length; i++) { looped = true;v["+a[s-4].key+"] = i;v["+a[s-4].item+"] = "+a[s-4].from+"[i];result += "+a[s-2]+";}} else {for (var key in "+a[s-4].from+") {if (!"+a[s-4].from+".hasOwnProperty(key)) continue;looped = true;v["+a[s-4].key+"] = key;v["+a[s-4].item+"] = "+a[s-4].from+"[key];result += "+a[s-2]+";}}return (looped ? result : "+(a[s-1]||"''")+"); })()";break;case 12:this.$="Language.get("+a[s-1]+", v)";break;case 13:this.$="StringUtil.escapeHTML("+a[s-1]+")";break;case 14:this.$="StringUtil.formatNumeric("+a[s-1]+")";break;case 15:this.$=a[s-1];break;case 16:this.$="'{'";break;case 17:this.$="'}'";break;case 18:this.$="else { return "+a[s]+"; }";break;case 19:this.$="else if ("+a[s-2]+") { return "+a[s]+"; }";break;case 20:this.$=a[s];break;case 21:this.$="v['"+a[s-1]+"']"+a[s].join("");break;case 22:this.$=a[s-2]+a[s-1]+a[s];break;case 23:this.$="['"+a[s]+"']";break;case 24:case 36:this.$=a[s-2]+(a[s-1]||"")+a[s];break;case 25:this.$=a[s],this.$[a[s-4]]=a[s-2];break;case 26:this.$={},this.$[a[s-2]]=a[s];break;case 29:this.$=a[s].join("");break;case 37:case 39:case 45:this.$=[];break;case 38:case 40:case 46:case 50:a[s-1].push(a[s]);break;case 49:this.$=[a[s]]}},table:[t([5,9,11,12,13,19,21,23,26,28,30,31,32,33],n,{3:1,4:2,6:3}),{1:[3]},{5:[1,4]},t([5,18,22,25,27,35,37,39],[2,2],{7:5,8:6,10:8,9:[1,7],11:[1,9],12:[1,10],13:[1,11],19:[1,12],21:[1,13],23:[1,14],26:[1,15],28:[1,16],30:[1,17],31:[1,18],32:[1,19],33:[1,20]}),{1:[2,1]},t(i,[2,38]),t(i,[2,3]),t(i,[2,4]),t(i,[2,5]),t(i,[2,6]),t(i,[2,7]),{11:o,12:a,14:21,29:28,40:r,41:s,47:l,50:c,52:u,53:22,54:23},{20:32,41:d},{20:34,41:d},{20:35,41:d},t([9,11,12,13,19,21,23,26,27,28,30,31,32,33],n,{6:3,4:36}),{29:37,40:r},{29:38,40:r},{29:39,40:r},t(i,[2,16]),t(i,[2,17]),{15:[1,40]},t([15,45,49],[2,29],{29:28,54:41,11:o,12:a,40:r,41:s,47:l,50:c,52:u}),t(h,[2,49]),t(h,[2,30]),t(h,[2,31]),t(h,[2,32]),t(h,[2,33]),t(h,[2,34]),t(h,[2,35]),{11:o,12:a,14:42,29:28,40:r,41:s,47:l,50:c,52:u,53:22,54:23},{41:[1,43]},{15:[1,44]},{50:[1,45]},{15:[1,46]},{15:[1,47]},{27:[1,48]},{15:[1,49]},{15:[1,50]},{15:[1,51]},t(f,n,{6:3,4:52}),t(h,[2,50]),{49:[1,53]},t(p,[2,45],{42:54}),t(i,[2,9]),{29:57,40:r,51:55,52:[1,56]},t([9,11,12,13,19,21,22,23,26,28,30,31,32,33],n,{6:3,4:58}),t([9,11,12,13,19,21,23,25,26,28,30,31,32,33,39],n,{6:3,4:59}),t(i,[2,12]),t(i,[2,13]),t(i,[2,14]),t(i,[2,15]),t(g,[2,39],{16:60}),t(h,[2,36]),t([11,12,15,40,41,45,49,50,52],[2,21],{43:61,44:[1,62],46:[1,63],47:[1,64]}),{12:[1,65],15:[2,26]},t(m,[2,27]),t(m,[2,28]),{22:[1,66]},{24:67,25:[2,43],38:68,39:[1,69]},{17:70,18:[2,41],34:72,35:[1,74],36:71,37:[1,73]},t(p,[2,46]),{11:o,12:a,14:75,29:28,40:r,41:s,47:l,50:c,52:u,53:22,54:23},{41:[1,76]},{11:o,12:a,14:78,29:28,40:r,41:s,47:l,48:77,49:[2,47],50:c,52:u,53:22,54:23},{20:79,41:d},t(i,[2,10]),{25:[1,80]},{25:[2,44]},t([9,11,12,13,19,21,23,25,26,28,30,31,32,33],n,{6:3,4:81}),{18:[1,82]},t(g,[2,40]),{18:[2,42]},{11:o,12:a,14:83,29:28,40:r,41:s,47:l,50:c,52:u,53:22,54:23},t([9,11,12,13,18,19,21,23,26,28,30,31,32,33],n,{6:3,4:84}),{45:[1,85]},t(p,[2,23]),{49:[1,86]},{49:[2,48]},{15:[2,25]},t(i,[2,11]),{25:[2,20]},t(i,[2,8]),{15:[1,87]},{18:[2,18]},t(p,[2,22]),t(p,[2,24]),t(f,n,{6:3,4:88}),t(g,[2,19])],defaultActions:{4:[2,1],68:[2,44],72:[2,42],78:[2,48],79:[2,25],81:[2,20],84:[2,18]},parseError:function(e,t){function n(e,t){this.message=e,this.hash=t}if(!t.recoverable)throw n.prototype=Error,new n(e,t);this.trace(e)},parse:function(e){var t=this,n=[0],i=[null],o=[],a=this.table,r="",s=0,l=0,c=0,u=o.slice.call(arguments,1),d=Object.create(this.lexer),h={yy:{}};for(var f in this.yy)Object.prototype.hasOwnProperty.call(this.yy,f)&&(h.yy[f]=this.yy[f]);d.setInput(e,h.yy),h.yy.lexer=d,h.yy.parser=this,void 0===d.yylloc&&(d.yylloc={});var p=d.yylloc;o.push(p);var g=d.options&&d.options.ranges;"function"==typeof h.yy.parseError?this.parseError=h.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var m,v,b,_,y,w,C,L,S,E=function(){var e;return e=d.lex()||1,"number"!=typeof e&&(e=t.symbols_[e]||e),e},x={};;){if(b=n[n.length-1],this.defaultActions[b]?_=this.defaultActions[b]:(null!==m&&void 0!==m||(m=E()),_=a[b]&&a[b][m]),void 0===_||!_.length||!_[0]){var D="";S=[];for(w in a[b])this.terminals_[w]&&w>2&&S.push("'"+this.terminals_[w]+"'");D=d.showPosition?"Parse error on line "+(s+1)+":\n"+d.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[m]||m)+"'":"Parse error on line "+(s+1)+": Unexpected "+(1==m?"end of input":"'"+(this.terminals_[m]||m)+"'"),this.parseError(D,{text:d.match,token:this.terminals_[m]||m,line:d.yylineno,loc:p,expected:S})}if(_[0]instanceof Array&&_.length>1)throw new Error("Parse Error: multiple actions possible at state: "+b+", token: "+m);switch(_[0]){case 1:n.push(m),i.push(d.yytext),o.push(d.yylloc),n.push(_[1]),m=null,v?(m=v,v=null):(l=d.yyleng,r=d.yytext,s=d.yylineno,p=d.yylloc,c>0&&c--);break;case 2:if(C=this.productions_[_[1]][1],x.$=i[i.length-C],x._$={first_line:o[o.length-(C||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-(C||1)].first_column,last_column:o[o.length-1].last_column},g&&(x._$.range=[o[o.length-(C||1)].range[0],o[o.length-1].range[1]]),void 0!==(y=this.performAction.apply(x,[r,l,s,h.yy,_[1],i,o].concat(u))))return y;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),o=o.slice(0,-1*C)),n.push(this.productions_[_[1]][0]),i.push(x.$),o.push(x._$),L=a[n[n.length-2]][n[n.length-1]],n.push(L);break;case 3:return!0}}return!0}},b=function(){return{EOF:1,parseError:function(e,t){if(!this.yy.parser)throw new Error(e);this.yy.parser.parseError(e,t)},setInput:function(e,t){return this.yy=t||this.yy||{},this._input=e,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var e=this._input[0];return this.yytext+=e,this.yyleng++,this.offset++,this.match+=e,this.matched+=e,e.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),e},unput:function(e){var t=e.length,n=e.split(/(?:\r\n?|\n)/g);this._input=e+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-t),this.offset-=t;var i=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var o=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===i.length?this.yylloc.first_column:0)+i[i.length-n.length].length-n[0].length:this.yylloc.first_column-t},this.options.ranges&&(this.yylloc.range=[o[0],o[0]+this.yyleng-t]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(e){this.unput(this.match.slice(e))},pastInput:function(){var e=this.matched.substr(0,this.matched.length-this.match.length);return(e.length>20?"...":"")+e.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var e=this.match;return e.length<20&&(e+=this._input.substr(0,20-e.length)),(e.substr(0,20)+(e.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var e=this.pastInput(),t=new Array(e.length+1).join("-");return e+this.upcomingInput()+"\n"+t+"^"},test_match:function(e,t){var n,i,o;if(this.options.backtrack_lexer&&(o={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(o.yylloc.range=this.yylloc.range.slice(0))),i=e[0].match(/(?:\r\n?|\n).*/g),i&&(this.yylineno+=i.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:i?i[i.length-1].length-i[i.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],n=this.performAction.call(this,this.yy,this,t,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in o)this[a]=o[a];return!1}return!1},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var e,t,n,i;this._more||(this.yytext="",this.match="");for(var o=this._currentRules(),a=0;a<o.length;a++)if((n=this._input.match(this.rules[o[a]]))&&(!t||n[0].length>t[0].length)){if(t=n,i=a,this.options.backtrack_lexer){if(!1!==(e=this.test_match(n,o[a])))return e;if(this._backtrack){t=!1;continue}return!1}if(!this.options.flex)break}return t?!1!==(e=this.test_match(t,o[i]))&&e:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var e=this.next();return e||this.lex()},begin:function(e){this.conditionStack.push(e)},popState:function(){
-return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(e){return e=this.conditionStack.length-1-Math.abs(e||0),e>=0?this.conditionStack[e]:"INITIAL"},pushState:function(e){this.begin(e)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(e,t,n,i){switch(n){case 0:break;case 1:return t.yytext=t.yytext.substring(9,t.yytext.length-10),9;case 2:case 3:return 52;case 4:return 40;case 5:return 41;case 6:return 46;case 7:return 44;case 8:return 45;case 9:return 47;case 10:return 49;case 11:return 50;case 12:return 32;case 13:return 33;case 14:return this.begin("command"),30;case 15:return this.begin("command"),31;case 16:return this.begin("command"),13;case 17:case 18:return this.begin("command"),37;case 19:return 35;case 20:return 18;case 21:return 26;case 22:return 27;case 23:return this.begin("command"),19;case 24:return this.begin("command"),21;case 25:return 22;case 26:return this.begin("command"),23;case 27:return 39;case 28:return 25;case 29:return this.begin("command"),28;case 30:return this.popState(),15;case 31:return 12;case 32:return 5;case 33:return 11}},rules:[/^(?:\{\*[\s\S]*?\*\})/,/^(?:\{literal\}[\s\S]*?\{\/literal\})/,/^(?:"([^"]|\\\.)*")/,/^(?:'([^']|\\\.)*')/,/^(?:\$)/,/^(?:[_a-zA-Z][_a-zA-Z0-9]*)/,/^(?:\.)/,/^(?:\[)/,/^(?:\])/,/^(?:\()/,/^(?:\))/,/^(?:=)/,/^(?:\{ldelim\})/,/^(?:\{rdelim\})/,/^(?:\{#)/,/^(?:\{@)/,/^(?:\{if )/,/^(?:\{else if )/,/^(?:\{elseif )/,/^(?:\{else\})/,/^(?:\{\/if\})/,/^(?:\{lang\})/,/^(?:\{\/lang\})/,/^(?:\{include )/,/^(?:\{implode )/,/^(?:\{\/implode\})/,/^(?:\{foreach )/,/^(?:\{foreachelse\})/,/^(?:\{\/foreach\})/,/^(?:\{(?!\s))/,/^(?:\})/,/^(?:\s+)/,/^(?:$)/,/^(?:[^{])/],conditions:{command:{rules:[0,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],inclusive:!0},INITIAL:{rules:[0,1,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33],inclusive:!0}}}}();return v.lexer=b,v}),define("WoltLabSuite/Core/NumberUtil",[],function(){"use strict";return{round:function(e,t){return void 0===t||0==+t?Math.round(e):(e=+e,t=+t,isNaN(e)||"number"!=typeof t||t%1!=0?NaN:(e=e.toString().split("e"),e=Math.round(+(e[0]+"e"+(e[1]?+e[1]-t:-t))),e=e.toString().split("e"),+(e[0]+"e"+(e[1]?+e[1]+t:t))))}}}),define("WoltLabSuite/Core/StringUtil",["Language","./NumberUtil"],function(e,t){"use strict";return{addThousandsSeparator:function(t){return void 0===e&&(e=require("Language")),String(t).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g,"$1"+e.get("wcf.global.thousandsSeparator"))},escapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")},escapeRegExp:function(e){return String(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},formatNumeric:function(n,i){void 0===e&&(e=require("Language")),n=String(t.round(n,i||-2));var o=n.split(".");return n=this.addThousandsSeparator(o[0]),o.length>1&&(n+=e.get("wcf.global.decimalPoint")+o[1]),n=n.replace("-","−")},lcfirst:function(e){return String(e).substring(0,1).toLowerCase()+e.substring(1)},ucfirst:function(e){return String(e).substring(0,1).toUpperCase()+e.substring(1)},unescapeHTML:function(e){return String(e).replace(/&/g,"&").replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">")},shortUnit:function(e){var n="";return e>=1e6?(e/=1e6,e=e>10?Math.floor(e):t.round(e,-1),n="M"):e>=1e3&&(e/=1e3,e=e>10?Math.floor(e):t.round(e,-1),n="k"),this.formatNumeric(e)+n}}}),define("WoltLabSuite/Core/Template",["./Template.grammar","./StringUtil","Language"],function(e,t,n){"use strict";function i(){this.yy={}}function o(i){void 0===n&&(n=require("Language")),void 0===t&&(t=require("StringUtil"));try{i=e.parse(i),i="var tmp = {};\nfor (var key in v) tmp[key] = v[key];\nv = tmp;\nv.__wcf = window.WCF; v.__window = window;\nreturn "+i,this.fetch=new Function("StringUtil","Language","v",i).bind(void 0,t,n)}catch(e){throw console.debug(e.message),e}}return i.prototype=e,e.Parser=i,e=new i,Object.defineProperty(o,"callbacks",{enumerable:!1,configurable:!1,get:function(){throw new Error("WCF.Template.callbacks is no longer supported")},set:function(e){throw new Error("WCF.Template.callbacks is no longer supported")}}),o.prototype={fetch:function(e){throw new Error("This Template is not initialized.")}},o}),define("WoltLabSuite/Core/Language",["Dictionary","./Template"],function(e,t){"use strict";var n=new e;return{addObject:function(t){n.merge(e.fromObject(t))},add:function(e,t){n.set(e,t)},get:function(e,i){i||(i={});var o=n.get(e);if(void 0===o)return e;if(void 0===t&&(t=require("WoltLabSuite/Core/Template")),"string"==typeof o){try{n.set(e,new t(o))}catch(i){n.set(e,new t("{literal}"+o.replace(/\{\/literal\}/g,"{/literal}{ldelim}/literal}{literal}")+"{/literal}"))}o=n.get(e)}return o instanceof t&&(o=o.fetch(i)),o}}}),define("WoltLabSuite/Core/CallbackList",["Dictionary"],function(e){"use strict";function t(){this._dictionary=new e}return t.prototype={add:function(e,t){if("function"!=typeof t)throw new TypeError("Expected a valid callback as second argument for identifier '"+e+"'.");this._dictionary.has(e)||this._dictionary.set(e,[]),this._dictionary.get(e).push(t)},remove:function(e){this._dictionary.delete(e)},forEach:function(e,t){if(null===e)this._dictionary.forEach(function(e,n){e.forEach(t)});else{var n=this._dictionary.get(e);void 0!==n&&n.forEach(t)}}},t}),define("WoltLabSuite/Core/Dom/Change/Listener",["CallbackList"],function(e){"use strict";var t=new e,n=!1;return{add:t.add.bind(t),remove:t.remove.bind(t),trigger:function(){if(!n)try{n=!0,t.forEach(null,function(e){e()})}finally{n=!1}}}}),define("WoltLabSuite/Core/Environment",[],function(){"use strict";var e="other",t="none",n="desktop",i=!1;return{setup:function(){if("object"==typeof window.chrome)e="chrome";else for(var o=window.getComputedStyle(document.documentElement),a=0,r=o.length;a<r;a++){var s=o[a];0===s.indexOf("-ms-")?e="microsoft":0===s.indexOf("-moz-")?e="firefox":"firefox"!==e&&0===s.indexOf("-webkit-")&&(e="safari")}var l=window.navigator.userAgent.toLowerCase();-1!==l.indexOf("crios")?(e="chrome",n="ios"):/(?:iphone|ipad|ipod)/.test(l)?(e="safari",n="ios"):-1!==l.indexOf("android")?n="android":-1!==l.indexOf("iemobile")&&(e="microsoft",n="windows"),"desktop"!==n||-1===l.indexOf("mobile")&&-1===l.indexOf("tablet")||(n="mobile"),t="redactor",i=!!("ontouchstart"in window)||!!("msMaxTouchPoints"in window.navigator)&&window.navigator.msMaxTouchPoints>0||window.DocumentTouch&&document instanceof DocumentTouch},browser:function(){return e},editor:function(){return t},platform:function(){return n},touch:function(){return i}}}),define("WoltLabSuite/Core/Dom/Util",["Environment","StringUtil"],function(e,t){"use strict";function n(e,t,n){if(!t.contains(e))throw new Error("Ancestor element does not contain target element.");for(var i,o=n+"Sibling";null!==e&&e!==t;){if(null!==e[n+"ElementSibling"])return!1;if(e[o])for(i=e[o];i;){if(""!==i.textContent.trim())return!1;i=i[o]}e=e.parentNode}return!0}var i=0,o={createFragmentFromHtml:function(e){var t=elCreate("div");this.setInnerHtml(t,e);for(var n=document.createDocumentFragment();t.childNodes.length;)n.appendChild(t.childNodes[0]);return n},getUniqueId:function(){var e;do{e="wcf"+i++}while(null!==elById(e));return e},identify:function(e){if(!(e instanceof Element))throw new TypeError("Expected a valid DOM element as argument.");var t=elAttr(e,"id");return t||(t=this.getUniqueId(),elAttr(e,"id",t)),t},outerHeight:function(e,t){t=t||window.getComputedStyle(e);var n=e.offsetHeight;return n+=~~t.marginTop+~~t.marginBottom},outerWidth:function(e,t){t=t||window.getComputedStyle(e);var n=e.offsetWidth;return n+=~~t.marginLeft+~~t.marginRight},outerDimensions:function(e){var t=window.getComputedStyle(e);return{height:this.outerHeight(e,t),width:this.outerWidth(e,t)}},offset:function(e){var t=e.getBoundingClientRect();return{top:Math.round(t.top+(window.scrollY||window.pageYOffset)),left:Math.round(t.left+(window.scrollX||window.pageXOffset))}},prepend:function(e,t){0===t.childNodes.length?t.appendChild(e):t.insertBefore(e,t.childNodes[0])},insertAfter:function(e,t){null!==t.nextSibling?t.parentNode.insertBefore(e,t.nextSibling):t.parentNode.appendChild(e)},setStyles:function(e,t){var n=!1;for(var i in t)t.hasOwnProperty(i)&&(/ !important$/.test(t[i])?(n=!0,t[i]=t[i].replace(/ !important$/,"")):n=!1,"important"!==e.style.getPropertyPriority(i)||n||e.style.removeProperty(i),e.style.setProperty(i,t[i],n?"important":""))},styleAsInt:function(e,t){var n=e.getPropertyValue(t);return null===n?0:parseInt(n)},setInnerHtml:function(e,t){e.innerHTML=t;for(var n,i,o=elBySelAll("script",e),a=0,r=o.length;a<r;a++)i=o[a],n=elCreate("script"),i.src?n.src=i.src:n.textContent=i.textContent,e.appendChild(n),elRemove(i)},insertHtml:function(e,t,n){var i=elCreate("div");if(this.setInnerHtml(i,e),i.childNodes.length){var o=i.childNodes[0];switch(n){case"append":t.appendChild(o);break;case"after":this.insertAfter(o,t);break;case"prepend":this.prepend(o,t);break;case"before":t.parentNode.insertBefore(o,t);break;default:throw new Error("Unknown insert method '"+n+"'.")}for(var a;i.childNodes.length;)a=i.childNodes[0],this.insertAfter(a,o),o=a}},contains:function(e,t){for(;null!==t;)if(t=t.parentNode,e===t)return!0;return!1},getDataAttributes:function(e,n,i,o){n=n||"",/^data-/.test(n)||(n="data-"+n),i=!0===i,o=!0===o;for(var a,r,s,l={},c=0,u=e.attributes.length;c<u;c++)if(a=e.attributes[c],0===a.name.indexOf(n)){if(r=a.name.replace(new RegExp("^"+n),""),i){s=r.split("-"),r="";for(var d=0,h=s.length;d<h;d++)r.length&&(o&&"id"===s[d]?s[d]="ID":s[d]=t.ucfirst(s[d])),r+=s[d]}l[r]=a.value}return l},unwrapChildNodes:function(e){for(var t=e.parentNode;e.childNodes.length;)t.insertBefore(e.childNodes[0],e);elRemove(e)},replaceElement:function(e,t){for(;e.childNodes.length;)t.appendChild(e.childNodes[0]);e.parentNode.insertBefore(t,e),elRemove(e)},isAtNodeStart:function(e,t){return n(e,t,"previous")},isAtNodeEnd:function(e,t){return n(e,t,"next")},getFixedParent:function(e){for(;e&&e!==document.body;){if("fixed"===window.getComputedStyle(e).getPropertyValue("position"))return e;e=e.offsetParent}return null}};return window.bc_wcfDomUtil=o,o}),function(e,t,n){var i=window.matchMedia;"undefined"!=typeof module&&module.exports?module.exports=n(i):"function"==typeof define&&define.amd?define("enquire",[],function(){return t.enquire=n(i)}):t.enquire=n(i)}(0,this,function(e){"use strict";function t(e,t){var n=0,i=e.length;for(n;n<i&&!1!==t(e[n],n);n++);}function n(e){return"[object Array]"===Object.prototype.toString.apply(e)}function i(e){return"function"==typeof e}function o(e){this.options=e,!e.deferSetup&&this.setup()}function a(t,n){this.query=t,this.isUnconditional=n,this.handlers=[],this.mql=e(t);var i=this;this.listener=function(e){i.mql=e,i.assess()},this.mql.addListener(this.listener)}function r(){if(!e)throw new Error("matchMedia not present, legacy browsers require a polyfill");this.queries={},this.browserIsIncapable=!e("only all").matches}return o.prototype={setup:function(){this.options.setup&&this.options.setup(),this.initialised=!0},on:function(){!this.initialised&&this.setup(),this.options.match&&this.options.match()},off:function(){this.options.unmatch&&this.options.unmatch()},destroy:function(){this.options.destroy?this.options.destroy():this.off()},equals:function(e){return this.options===e||this.options.match===e}},a.prototype={addHandler:function(e){var t=new o(e);this.handlers.push(t),this.matches()&&t.on()},removeHandler:function(e){var n=this.handlers;t(n,function(t,i){if(t.equals(e))return t.destroy(),!n.splice(i,1)})},matches:function(){return this.mql.matches||this.isUnconditional},clear:function(){t(this.handlers,function(e){e.destroy()}),this.mql.removeListener(this.listener),this.handlers.length=0},assess:function(){var e=this.matches()?"on":"off";t(this.handlers,function(t){t[e]()})}},r.prototype={register:function(e,o,r){var s=this.queries,l=r&&this.browserIsIncapable;return s[e]||(s[e]=new a(e,l)),i(o)&&(o={match:o}),n(o)||(o=[o]),t(o,function(t){i(t)&&(t={match:t}),s[e].addHandler(t)}),this},unregister:function(e,t){var n=this.queries[e];return n&&(t?n.removeHandler(t):(n.clear(),delete this.queries[e])),this}},new r}),define("WoltLabSuite/Core/ObjectMap",[],function(){"use strict";function e(){this._map=t?new WeakMap:{key:[],value:[]}}var t=objOwns(window,"WeakMap")&&"function"==typeof window.WeakMap;return e.prototype={set:function(e,n){if("object"!=typeof e||null===e)throw new TypeError("Only objects can be used as key");if("object"!=typeof n||null===n)throw new TypeError("Only objects can be used as value");t?this._map.set(e,n):(this._map.key.push(e),this._map.value.push(n))},delete:function(e){if(t)this._map.delete(e);else{var n=this._map.key.indexOf(e);this._map.key.splice(n),this._map.value.splice(n)}},has:function(e){return t?this._map.has(e):-1!==this._map.key.indexOf(e)},get:function(e){if(t)return this._map.get(e);var n=this._map.key.indexOf(e);return-1!==n?this._map.value[n]:void 0}},e}),define("WoltLabSuite/Core/Dom/Traverse",[],function(){"use strict";var e=[function(e,t){return!0},function(e,t){return e.matches(t)},function(e,t){return e.classList.contains(t)},function(e,t){return e.nodeName===t}],t=function(t,n,i){if(!(t instanceof Element))throw new TypeError("Expected a valid element as first argument.");for(var o=[],a=0;a<t.childElementCount;a++)e[n](t.children[a],i)&&o.push(t.children[a]);return o},n=function(t,n,i,o){if(!(t instanceof Element))throw new TypeError("Expected a valid element as first argument.");for(t=t.parentNode;t instanceof Element;){if(t===o)return null;if(e[n](t,i))return t;t=t.parentNode}return null},i=function(t,n,i,o){if(!(t instanceof Element))throw new TypeError("Expected a valid element as first argument.");return t instanceof Element&&null!==t[n]&&e[i](t[n],o)?t[n]:null};return{childBySel:function(e,n){return t(e,1,n)[0]||null},childByClass:function(e,n){return t(e,2,n)[0]||null},childByTag:function(e,n){return t(e,3,n)[0]||null},childrenBySel:function(e,n){return t(e,1,n)},childrenByClass:function(e,n){return t(e,2,n)},childrenByTag:function(e,n){return t(e,3,n)},parentBySel:function(e,t,i){return n(e,1,t,i)},parentByClass:function(e,t,i){return n(e,2,t,i)},parentByTag:function(e,t,i){return n(e,3,t,i)},next:function(e){return i(e,"nextElementSibling",0,null)},nextBySel:function(e,t){return i(e,"nextElementSibling",1,t)},nextByClass:function(e,t){return i(e,"nextElementSibling",2,t)},nextByTag:function(e,t){return i(e,"nextElementSibling",3,t)},prev:function(e){return i(e,"previousElementSibling",0,null)},prevBySel:function(e,t){return i(e,"previousElementSibling",1,t)},prevByClass:function(e,t){return i(e,"previousElementSibling",2,t)},prevByTag:function(e,t){return i(e,"previousElementSibling",3,t)}}}),define("WoltLabSuite/Core/Ui/Confirmation",["Core","Language","Ui/Dialog"],function(e,t,n){"use strict";var i=!1,o=null,a=null,r={},s=null;return{show:function(t){if(void 0===n&&(n=require("Ui/Dialog")),!i){if(r=e.extend({cancel:null,confirm:null,legacyCallback:null,message:"",messageIsHtml:!1,parameters:{},template:""},t),r.message="string"==typeof r.message?r.message.trim():"",!r.message.length)throw new Error("Expected a non-empty string for option 'message'.");if("function"!=typeof r.confirm&&"function"!=typeof r.legacyCallback)throw new TypeError("Expected a valid callback for option 'confirm'.");null===a&&this._createDialog(),a.innerHTML="string"==typeof r.template?r.template.trim():"",r.messageIsHtml?s.innerHTML=r.message:s.textContent=r.message,i=!0,n.open(this)}},_dialogSetup:function(){return{id:"wcfSystemConfirmation",options:{onClose:this._onClose.bind(this),onShow:this._onShow.bind(this),title:t.get("wcf.global.confirmation.title")}}},getContentElement:function(){return a},_createDialog:function(){var e=elCreate("div");elAttr(e,"id","wcfSystemConfirmation"),e.classList.add("systemConfirmation"),s=elCreate("p"),e.appendChild(s),a=elCreate("div"),elAttr(a,"id","wcfSystemConfirmationContent"),e.appendChild(a);var i=elCreate("div");i.classList.add("formSubmit"),e.appendChild(i),o=elCreate("button"),o.classList.add("buttonPrimary"),o.textContent=t.get("wcf.global.confirmation.confirm"),o.addEventListener(WCF_CLICK_EVENT,this._confirm.bind(this)),i.appendChild(o);var r=elCreate("button");r.textContent=t.get("wcf.global.confirmation.cancel"),r.addEventListener(WCF_CLICK_EVENT,function(){n.close("wcfSystemConfirmation")}),i.appendChild(r),document.body.appendChild(e)},_confirm:function(){"function"==typeof r.legacyCallback?r.legacyCallback("confirm",r.parameters,a):r.confirm(r.parameters,a),i=!1,n.close("wcfSystemConfirmation")},_onClose:function(){i&&(o.blur(),i=!1,"function"==typeof r.legacyCallback?r.legacyCallback("cancel",r.parameters,a):"function"==typeof r.cancel&&r.cancel(r.parameters))},_onShow:function(){o.blur(),o.focus()}}}),define("WoltLabSuite/Core/Ui/Screen",["Core","Dictionary","Environment"],function(e,t,n){"use strict";var i=null,o=new t,a=0,r=null,s=0,l=t.fromObject({"screen-xs":"(max-width: 544px)","screen-sm":"(min-width: 545px) and (max-width: 768px)","screen-sm-down":"(max-width: 768px)","screen-sm-up":"(min-width: 545px)","screen-sm-md":"(min-width: 545px) and (max-width: 1024px)","screen-md":"(min-width: 769px) and (max-width: 1024px)","screen-md-down":"(max-width: 1024px)","screen-md-up":"(min-width: 769px)","screen-lg":"(min-width: 1025px)"}),c=new t;return{on:function(t,n){var i=e.getUuid(),o=this._getQueryObject(t);return"function"==typeof n.match&&o.callbacksMatch.set(i,n.match),"function"==typeof n.unmatch&&o.callbacksUnmatch.set(i,n.unmatch),"function"==typeof n.setup&&(o.mql.matches?n.setup():o.callbacksSetup.set(i,n.setup)),i},remove:function(e,t){var n=this._getQueryObject(e);n.callbacksMatch.delete(t),n.callbacksUnmatch.delete(t),n.callbacksSetup.delete(t)},is:function(e){return this._getQueryObject(e).mql.matches},scrollDisable:function(){if(0===a){s=document.body.scrollTop,r="body",s||(s=document.documentElement.scrollTop,r="documentElement");var e=elById("pageContainer");"ios"===n.platform()?(e.style.setProperty("position","relative",""),e.style.setProperty("top","-"+s+"px","")):e.style.setProperty("margin-top","-"+s+"px",""),document.documentElement.classList.add("disableScrolling")}a++},scrollEnable:function(){if(a&&0===--a){document.documentElement.classList.remove("disableScrolling");var e=elById("pageContainer");"ios"===n.platform()?(e.style.removeProperty("position"),e.style.removeProperty("top")):e.style.removeProperty("margin-top"),s&&(document[r].scrollTop=~~s)}},setDialogContainer:function(e){i=e},_getQueryObject:function(e){if("string"!=typeof e||""===e.trim())throw new TypeError("Expected a non-empty string for parameter 'query'.");c.has(e)&&(e=c.get(e)),l.has(e)&&(e=l.get(e));var n=o.get(e);return n||(n={callbacksMatch:new t,callbacksUnmatch:new t,callbacksSetup:new t,mql:window.matchMedia(e)},n.mql.addListener(this._mqlChange.bind(this)),o.set(e,n),e!==n.mql.media&&c.set(n.mql.media,e)),n},_mqlChange:function(e){var n=this._getQueryObject(e.media);e.matches?n.callbacksSetup.size?(n.callbacksSetup.forEach(function(e){e()}),n.callbacksSetup=new t):n.callbacksMatch.forEach(function(e){e()}):n.callbacksUnmatch.forEach(function(e){e()})}}}),define("WoltLabSuite/Core/Ui/Alignment",["Core","Language","Dom/Traverse","Dom/Util"],function(e,t,n,i){"use strict";return{set:function(o,a,r){r=e.extend({verticalOffset:0,pointer:!1,pointerOffset:4,pointerClassNames:[],refDimensionsElement:null,horizontal:"left",vertical:"bottom",allowFlip:"both"},r),Array.isArray(r.pointerClassNames)&&r.pointerClassNames.length===(r.pointer?1:2)||(r.pointerClassNames=[]),-1===["left","right","center"].indexOf(r.horizontal)&&(r.horizontal="left"),"bottom"!==r.vertical&&(r.vertical="top"),-1===["both","horizontal","vertical","none"].indexOf(r.allowFlip)&&(r.allowFlip="both"),i.setStyles(o,{bottom:"auto !important",left:"0 !important",right:"auto !important",top:"0 !important",visibility:"hidden !important"});var s=i.outerDimensions(o),l=i.outerDimensions(r.refDimensionsElement instanceof Element?r.refDimensionsElement:a),c=i.offset(a),u=window.innerHeight,d=document.body.clientWidth,h={result:null},f=!1;if("center"===r.horizontal&&(f=!0,h=this._tryAlignmentHorizontal(r.horizontal,s,l,c,d),h.result||("both"===r.allowFlip||"horizontal"===r.allowFlip?r.horizontal="left":h.result=!0)),"rtl"===t.get("wcf.global.pageDirection")&&(r.horizontal="left"===r.horizontal?"right":"left"),!h.result){var p=h;if(h=this._tryAlignmentHorizontal(r.horizontal,s,l,c,d),!h.result&&("both"===r.allowFlip||"horizontal"===r.allowFlip)){var g=this._tryAlignmentHorizontal("left"===r.horizontal?"right":"left",s,l,c,d);g.result?h=g:f&&(h=p)}}var m=h.left,v=h.right,b=this._tryAlignmentVertical(r.vertical,s,l,c,u,r.verticalOffset);if(!b.result&&("both"===r.allowFlip||"vertical"===r.allowFlip)){var _=this._tryAlignmentVertical("top"===r.vertical?"bottom":"top",s,l,c,u,r.verticalOffset);_.result&&(b=_)}var y=b.bottom,w=b.top;if(r.pointer){var C=n.childrenByClass(o,"elementPointer");if(null===(C=C[0]||null))throw new Error("Expected the .elementPointer element to be a direct children.");"center"===h.align?(C.classList.add("center"),C.classList.remove("left"),C.classList.remove("right")):(C.classList.add(h.align),C.classList.remove("center"),C.classList.remove("left"===h.align?"right":"left")),"top"===b.align?C.classList.add("flipVertical"):C.classList.remove("flipVertical")}else if(2===r.pointerClassNames.length){o.classList["auto"===w?"add":"remove"](r.pointerClassNames[0]),o.classList["auto"===m?"add":"remove"](r.pointerClassNames[1])}"auto"!==y&&(y=Math.round(y)+"px"),"auto"!==m&&(m=Math.ceil(m)+"px"),"auto"!==v&&(v=Math.floor(v)+"px"),"auto"!==w&&(w=Math.round(w)+"px"),i.setStyles(o,{bottom:y,left:m,right:v,top:w}),elShow(o),o.style.removeProperty("visibility")},_tryAlignmentHorizontal:function(e,t,n,i,o){var a="auto",r="auto",s=!0;return"left"===e?(a=i.left)+t.width>o&&(s=!1):"right"===e?i.left+n.width<t.width?s=!1:(r=o-(i.left+n.width))<0&&(s=!1):(a=i.left+n.width/2-t.width/2,((a=~~a)<0||a+t.width>o)&&(s=!1)),{align:e,left:a,right:r,result:s}},_tryAlignmentVertical:function(e,t,n,i,o,a){var r="auto",s="auto",l=!0;if("top"===e){var c=document.body.clientHeight;r=c-i.top+a,c-(r+t.height)<(window.scrollY||window.pageYOffset)&&(l=!1)}else(s=i.top+n.height+a)+t.height-(window.scrollY||window.pageYOffset)>o&&(l=!1);return{align:e,bottom:r,top:s,result:l}}}}),define("WoltLabSuite/Core/Ui/CloseOverlay",["CallbackList"],function(e){"use strict";var t=new e,n={setup:function(){document.body.addEventListener(WCF_CLICK_EVENT,this.execute.bind(this))},add:t.add.bind(t),remove:t.remove.bind(t),execute:function(){t.forEach(null,function(e){e()})}};return n.setup(),n}),define("WoltLabSuite/Core/Ui/Dropdown/Simple",["CallbackList","Core","Dictionary","Ui/Alignment","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/CloseOverlay"],function(e,t,n,i,o,a,r,s){"use strict";var l=null,c=new e,u=!1,d=new n,h=new n,f=null;return{setup:function(){u||(u=!0,f=elCreate("div"),f.className="dropdownMenuContainer",document.body.appendChild(f),l=elByClass("dropdownToggle"),this.initAll(),s.add("WoltLabSuite/Core/Ui/Dropdown/Simple",this.closeAll.bind(this)),o.add("WoltLabSuite/Core/Ui/Dropdown/Simple",this.initAll.bind(this)),document.addEventListener("scroll",this._onScroll.bind(this)),window.bc_wcfSimpleDropdown=this)},initAll:function(){for(var e=0,t=l.length;e<t;e++)this.init(l[e],!1)},init:function(e,n){if(this.setup(),e.classList.contains("jsDropdownEnabled")||elData(e,"target"))return!1;var i=a.parentByClass(e,"dropdown");if(null===i)throw new Error("Invalid dropdown passed, button '"+r.identify(e)+"' does not have a parent with .dropdown.");var o=a.nextByClass(e,"dropdownMenu");if(null===o)throw new Error("Invalid dropdown passed, button '"+r.identify(e)+"' does not have a menu as next sibling.");f.appendChild(o);var s=r.identify(i);if(!d.has(s)&&(e.classList.add("jsDropdownEnabled"),e.addEventListener(WCF_CLICK_EVENT,this._toggle.bind(this)),d.set(s,i),h.set(s,o),s.match(/^wcf\d+$/)||elData(o,"source",s),o.childElementCount&&o.children[0].classList.contains("scrollableDropdownMenu"))){o=o.children[0],elData(o,"scroll-to-active",!0);var l=null,c=null;o.addEventListener("wheel",function(e){null===l&&(l=o.clientHeight),null===c&&(c=o.scrollHeight),e.deltaY<0&&0===o.scrollTop?e.preventDefault():e.deltaY>0&&o.scrollTop+l===c&&e.preventDefault()},{passive:!1})}elData(e,"target",s),n&&setTimeout(function(){t.triggerEvent(e,WCF_CLICK_EVENT)},10)},initFragment:function(e,t){this.setup();var n=r.identify(e);d.has(n)||(d.set(n,e),f.appendChild(t),h.set(n,t))},registerCallback:function(e,t){c.add(e,t)},getDropdown:function(e){return d.get(e)},getDropdownMenu:function(e){return h.get(e)},toggleDropdown:function(e,t){this._toggle(null,e,t)},setAlignment:function(e,t,n){var o,a=elBySel(".dropdownToggle",e);null!==a&&a.parentNode.classList.contains("inputAddonTextarea")&&(o=a),i.set(t,n||e,{pointerClassNames:["dropdownArrowBottom","dropdownArrowRight"],refDimensionsElement:o||null,horizontal:"right"===elData(t,"dropdown-alignment-horizontal")?"right":"left",vertical:"top"===elData(t,"dropdown-alignment-vertical")?"top":"bottom"})},setAlignmentById:function(e){var t=d.get(e);if(void 0===t)throw new Error("Unknown dropdown identifier '"+e+"'.");var n=h.get(e);this.setAlignment(t,n)},isOpen:function(e){var t=h.get(e);return void 0!==t&&t.classList.contains("dropdownOpen")},open:function(e){var t=h.get(e);void 0===t||t.classList.contains("dropdownOpen")||this.toggleDropdown(e)},close:function(e){var t=d.get(e);void 0!==t&&(t.classList.remove("dropdownOpen"),h.get(e).classList.remove("dropdownOpen"))},closeAll:function(){d.forEach(function(e,t){e.classList.contains("dropdownOpen")&&(e.classList.remove("dropdownOpen"),h.get(t).classList.remove("dropdownOpen"),this._notifyCallbacks(t,"close"))}.bind(this))},destroy:function(e){if(!d.has(e))return!1;try{this.close(e),elRemove(h.get(e))}catch(e){}return h.delete(e),d.delete(e),!0},_onDialogScroll:function(e){for(var t=e.currentTarget,n=elBySelAll(".dropdown.dropdownOpen",t),i=0,o=n.length;i<o;i++){var a=n[i],s=r.identify(a),l=r.offset(a),c=r.offset(t);l.top+a.clientHeight<=c.top?this.toggleDropdown(s):l.top>=c.top+t.offsetHeight?this.toggleDropdown(s):l.left<=c.left?this.toggleDropdown(s):l.left>=c.left+t.offsetWidth?this.toggleDropdown(s):this.setAlignment(s,h.get(s))}},_onScroll:function(){d.forEach(function(e,t){e.classList.contains("dropdownOpen")&&(elDataBool(e,"is-overlay-dropdown-button")?this.setAlignment(e,h.get(t)):this.close(t))}.bind(this))},_notifyCallbacks:function(e,t){c.forEach(e,function(n){n(e,t)})},_toggle:function(e,t,n){null!==e&&(e.preventDefault(),e.stopPropagation(),t=elData(e.currentTarget,"target"));var i=d.get(t),o=!1;if(void 0!==i){if(e){var r=e.currentTarget,s=r.parentNode;s!==i&&(s.classList.add("dropdown"),s.id=i.id,i.classList.remove("dropdown"),i.id="",i=s,d.set(t,s))}if(elDataBool(i,"dropdown-prevent-toggle")&&i.classList.contains("dropdownOpen")&&(o=!0),null===elData(i,"is-overlay-dropdown-button")){var l=a.parentByClass(i,"dialogContent");elData(i,"is-overlay-dropdown-button",null!==l),null!==l&&l.addEventListener("scroll",this._onDialogScroll.bind(this))}}return d.forEach(function(e,i){var a=h.get(i);if(e.classList.contains("dropdownOpen"))!1===o&&(e.classList.remove("dropdownOpen"),a.classList.remove("dropdownOpen"),this._notifyCallbacks(i,"close"));else if(i===t&&a.childElementCount>0){if(e.classList.add("dropdownOpen"),a.classList.add("dropdownOpen"),a.childElementCount&&elDataBool(a.children[0],"scroll-to-active")){var r=a.children[0];r.removeAttribute("data-scroll-to-active");for(var s=null,l=0,c=r.childElementCount;l<c;l++)if(r.children[l].classList.contains("active")){s=r.children[l];break}s&&(r.scrollTop=Math.max(s.offsetTop+s.clientHeight-a.clientHeight,0))}var u=elBySel(".scrollableDropdownMenu",a);null!==u&&u.classList[u.scrollHeight>u.clientHeight?"add":"remove"]("forceScrollbar"),this._notifyCallbacks(i,"open"),this.setAlignment(e,a,n)}}.bind(this)),window.WCF.Dropdown.Interactive.Handler.closeAll(),null===e}}}),define("WoltLabSuite/Core/Devtools",[],function(){"use strict";return{help:function(){},toggleEditorAutosave:function(){},toggleEventLogging:function(){},_internal_:{enable:function(){},editorAutosave:function(){},eventLog:function(){}}}}),define("WoltLabSuite/Core/Event/Handler",["Core","Devtools","Dictionary"],function(e,t,n){"use strict";var i=new n;return{add:function(t,o,a){if("function"!=typeof a)throw new TypeError("[WoltLabSuite/Core/Event/Handler] Expected a valid callback for '"+o+"@"+t+"'.");var r=i.get(t);void 0===r&&(r=new n,i.set(t,r));var s=r.get(o);void 0===s&&(s=new n,r.set(o,s));var l=e.getUuid();return s.set(l,a),l},fire:function(e,n,o){t._internal_.eventLog(e,n),o=o||{};var a=i.get(e);if(void 0!==a){var r=a.get(n);void 0!==r&&r.forEach(function(e){e(o)})}},remove:function(e,t,n){var o=i.get(e);if(void 0!==o){var a=o.get(t);void 0!==a&&a.delete(n)}},removeAll:function(e,t){"string"!=typeof t&&(t=void 0);var n=i.get(e);void 0!==n&&(void 0===t?i.delete(e):n.delete(t))},removeAllBySuffix:function(e,t){var n=i.get(e);if(void 0!==n){t="_"+t;var o=-1*t.length;n.forEach(function(n,i){i.substr(o)===t&&this.removeAll(e,i)}.bind(this))}}}}),define("WoltLabSuite/Core/List",[],function(){"use strict";function e(){this._set=t?new Set:[]}var t=objOwns(window,"Set")&&"function"==typeof window.Set;return e.prototype={add:function(e){t?this._set.add(e):this.has(e)||this._set.push(e)},clear:function(){t?this._set.clear():this._set=[]},delete:function(e){if(t)return this._set.delete(e);var n=this._set.indexOf(e);return-1!==n&&(this._set.splice(n,1),!0)},forEach:function(e){if(t)this._set.forEach(e);else for(var n=0,i=this._set.length;n<i;n++)e(this._set[n])},has:function(e){return t?this._set.has(e):-1!==this._set.indexOf(e)}},Object.defineProperty(e.prototype,"size",{enumerable:!1,configurable:!0,get:function(){return t?this._set.size:this._set.length}}),e}),define("WoltLabSuite/Core/Event/Key",[],function(){"use strict";function e(e,t,n){if(!(e instanceof Event))throw new TypeError("Expected a valid event when testing for key '"+t+"'.");return e.key===t||e.which===n}return{ArrowDown:function(t){return e(t,"ArrowDown",40)},ArrowLeft:function(t){return e(t,"ArrowLeft",37)},ArrowRight:function(t){return e(t,"ArrowRight",39)},ArrowUp:function(t){return e(t,"ArrowUp",38)},Comma:function(t){return e(t,",",44)},Enter:function(t){return e(t,"Enter",13)},Escape:function(t){return e(t,"Escape",27)},Tab:function(t){return e(t,"Tab",9)}}}),define("WoltLabSuite/Core/Ui/Dialog",["enquire","Ajax","Core","Dictionary","Environment","Language","ObjectMap","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Confirmation","Ui/Screen","Ui/SimpleDropdown","EventHandler","List","EventKey"],function(e,t,n,i,o,a,r,s,l,c,u,d,h,f,p,g){"use strict";var m=null,v=null,b=new i,_=!1,y=new r,w=new i,C=null,L=elByClass("jsStaticDialog"),S=["onBeforeClose","onClose","onShow"],E=["number","password","search","tel","text","url"];return{setup:function(){void 0===t&&(t=require("Ajax")),v=elCreate("div"),v.classList.add("dialogOverlay"),elAttr(v,"aria-hidden","true"),v.addEventListener(WCF_CLICK_EVENT,this._closeOnBackdrop.bind(this)),v.addEventListener("wheel",function(e){e.target===v&&e.preventDefault()},{passive:!1}),elById("content").appendChild(v),C=function(e){return 27!==e.keyCode||"INPUT"===e.target.nodeName||"TEXTAREA"===e.target.nodeName||(this.close(m),!1)}.bind(this),d.on("screen-xs",{match:function(){_=!0},
-unmatch:function(){_=!1},setup:function(){_=!0}}),this._initStaticDialogs(),s.add("Ui/Dialog",this._initStaticDialogs.bind(this)),d.setDialogContainer(v),"ios"===o.platform()&&window.addEventListener("resize",function(){b.forEach(function(e){elAttrBool(e.dialog,"aria-hidden")||this.rebuild(elData(e.dialog,"id"))}.bind(this))}.bind(this))},_initStaticDialogs:function(){for(var e,t,n;L.length;)e=L[0],e.classList.remove("jsStaticDialog"),(n=elData(e,"dialog-id"))&&(t=elById(n))&&function(e,t){t.classList.remove("jsStaticDialogContent"),elData(t,"is-static-dialog",!0),elHide(t),e.addEventListener(WCF_CLICK_EVENT,this.openStatic.bind(this,t.id,null,{title:elData(t,"title")}))}.bind(this)(e,t)},open:function(e,i){var o=y.get(e);if(n.isPlainObject(o))return this.openStatic(o.id,i);if("function"!=typeof e._dialogSetup)throw new Error("Callback object does not implement the method '_dialogSetup()'.");var a=e._dialogSetup();if(!n.isPlainObject(a))throw new Error("Expected an object literal as return value of '_dialogSetup()'.");o={id:a.id};var r=!0;if(void 0===a.source){var s=elById(a.id);if(null===s)throw new Error("Element id '"+a.id+"' is invalid and no source attribute was given. If you want to use the `html` argument instead, please add `source: null` to your dialog configuration.");a.source=document.createDocumentFragment(),a.source.appendChild(s),s.removeAttribute("id"),elShow(s)}else if(null===a.source)a.source=i;else if("function"==typeof a.source)a.source();else if(n.isPlainObject(a.source)){if("string"!=typeof i||""===i.trim())return t.api(this,a.source.data,function(t){t.returnValues&&"string"==typeof t.returnValues.template&&(this.open(e,t.returnValues.template),"function"==typeof a.source.after&&a.source.after(b.get(a.id).content,t))}.bind(this)),{};a.source=i}else{if("string"==typeof a.source){var s=elCreate("div");elAttr(s,"id",a.id),c.setInnerHtml(s,a.source),a.source=document.createDocumentFragment(),a.source.appendChild(s)}if(!a.source.nodeType||a.source.nodeType!==Node.DOCUMENT_FRAGMENT_NODE)throw new Error("Expected at least a document fragment as 'source' attribute.");r=!1}return y.set(e,o),w.set(a.id,e),this.openStatic(a.id,a.source,a.options,r)},openStatic:function(e,t,i,r){document.documentElement.classList.add("pageOverlayActive"),"desktop"!==o.platform()&&(this.isOpen(e)||d.scrollDisable()),b.has(e)?this._updateDialog(e,t):(i=n.extend({backdropCloseOnClick:!0,closable:!0,closeButtonLabel:a.get("wcf.global.button.close"),closeConfirmMessage:"",disableContentPadding:!1,title:"",onBeforeClose:null,onClose:null,onShow:null},i),i.closable||(i.backdropCloseOnClick=!1),i.closeConfirmMessage&&(i.onBeforeClose=function(e){u.show({confirm:this.close.bind(this,e),message:i.closeConfirmMessage})}.bind(this)),this._createDialog(e,t,i));var s=b.get(e);return"ios"===o.platform()&&window.setTimeout(function(){var e=elBySel("input, textarea",s.content);null!==e&&e.focus()}.bind(this),200),s},setTitle:function(e,t){e=this._getDialogId(e);var n=b.get(e);if(void 0===n)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");var i=elByClass("dialogTitle",n.dialog);i.length&&(i[0].textContent=t)},setCallback:function(e,t,n){if("object"==typeof e){var i=y.get(e);void 0!==i&&(e=i.id)}var o=b.get(e);if(void 0===o)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");if(-1===S.indexOf(t))throw new Error("Invalid callback identifier, '"+t+"' is not recognized.");if("function"!=typeof n&&null!==n)throw new Error("Only functions or the 'null' value are acceptable callback values ('"+typeof n+"' given).");o[t]=n},_createDialog:function(e,t,n,i){var o=null;if(null===t&&null===(o=elById(e)))throw new Error("Expected either a HTML string or an existing element id.");var a=elCreate("div");a.classList.add("dialogContainer"),elAttr(a,"aria-hidden","true"),elAttr(a,"role","dialog"),elData(a,"id",e);var r=elCreate("header");a.appendChild(r);var s=c.getUniqueId();elAttr(a,"aria-labelledby",s);var l=elCreate("span");if(l.classList.add("dialogTitle"),l.textContent=n.title,elAttr(l,"id",s),r.appendChild(l),n.closable){var u=elCreate("a");u.className="dialogCloseButton jsTooltip",elAttr(u,"title",n.closeButtonLabel),elAttr(u,"aria-label",n.closeButtonLabel),u.addEventListener(WCF_CLICK_EVENT,this._close.bind(this)),r.appendChild(u);var d=elCreate("span");d.className="icon icon24 fa-times",u.appendChild(d)}var h=elCreate("div");h.classList.add("dialogContent"),n.disableContentPadding&&h.classList.add("dialogContentNoPadding"),a.appendChild(h),h.addEventListener("wheel",function(e){for(var t,n,i,o=!1,a=e.target;;){if(t=a.clientHeight,n=a.scrollHeight,t<n){if(i=a.scrollTop,e.deltaY<0&&i>0){o=!0;break}if(e.deltaY>0&&i+t<n){o=!0;break}}if(!a||a===h)break;a=a.parentNode}!1===o&&e.preventDefault()},{passive:!1});var f;if(null===o)if("string"==typeof t)f=elCreate("div"),f.id=e,c.setInnerHtml(f,t);else{if(!(t instanceof DocumentFragment))throw new TypeError("'html' must either be a string or a DocumentFragment");for(var g,m=[],_=0,y=t.childNodes.length;_<y;_++)g=t.childNodes[_],g.nodeType===Node.ELEMENT_NODE&&m.push(g);"DIV"!==m[0].nodeName||m.length>1?(f=elCreate("div"),f.id=e,f.appendChild(t)):f=m[0]}else f=o;h.appendChild(f),"none"===f.style.getPropertyValue("display")&&elShow(f),b.set(e,{backdropCloseOnClick:n.backdropCloseOnClick,closable:n.closable,content:f,dialog:a,header:r,onBeforeClose:n.onBeforeClose,onClose:n.onClose,onShow:n.onShow,submitButton:null,inputFields:new p}),c.prepend(a,v),"function"==typeof n.onSetup&&n.onSetup(f),!0!==i&&this._updateDialog(e,null)},_updateDialog:function(e,t){var n=b.get(e);if(void 0===n)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");if("string"==typeof t&&c.setInnerHtml(n.content,t),"true"===elAttr(n.dialog,"aria-hidden")){n.closable&&"true"===elAttr(v,"aria-hidden")&&window.addEventListener("keyup",C),elAttr(n.dialog,"aria-hidden","false"),elAttr(v,"aria-hidden","false"),elData(v,"close-on-click",n.backdropCloseOnClick?"true":"false"),m=e;var i=elBySel(".jsDialogAutoFocus",n.dialog);null!==i&&null!==i.offsetParent&&("username"!==i.id&&"username"!==i.name||"safari"===o.browser()&&"ios"===o.platform()&&(i=null),i&&i.focus()),"function"==typeof n.onShow&&n.onShow(n.content),elDataBool(n.content,"is-static-dialog")&&f.fire("com.woltlab.wcf.dialog","openStatic",{content:n.content,id:e}),h.closeAll(),window.WCF.Dropdown.Interactive.Handler.closeAll()}this.rebuild(e),s.trigger()},rebuild:function(e){e=this._getDialogId(e);var t=b.get(e);if(void 0===t)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");if("true"!==elAttr(t.dialog,"aria-hidden")){var n=t.content.parentNode,i=elBySel(".formSubmit",t.content),a=0;null!==i?(n.classList.add("dialogForm"),i.classList.add("dialogFormSubmit"),a+=c.outerHeight(i),a-=1,n.style.setProperty("margin-bottom",a+"px","")):(n.classList.remove("dialogForm"),n.style.removeProperty("margin-bottom")),a+=c.outerHeight(t.header);var r=window.innerHeight*(_?1:.8)-a;if(n.style.setProperty("max-height",~~r+"px",""),"chrome"===o.browser()&&(t.content.scrollHeight>r?t.content.style.setProperty("margin-right","-1px",""):t.content.style.removeProperty("margin-right")),"chrome"===o.browser()||"safari"===o.browser()){var s=parseFloat(window.getComputedStyle(t.content).width),l=Math.round(s)%2!=0;t.content.parentNode.classList[l?"add":"remove"]("jsWebKitFractionalPixel")}var u=w.get(e);if(void 0!==u&&"function"==typeof u._dialogSubmit){var d=elBySelAll('input[data-dialog-submit-on-enter="true"]',t.content),h=elBySel('.formSubmit > input[type="submit"], .formSubmit > button[data-type="submit"]',t.content);if(null===h)return void(0===d.length&&console.warn("Broken dialog, expected a submit button.",t.content));if(t.submitButton!==h){t.submitButton=h,h.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),this._submit(e)}.bind(this));for(var f,p=null,m=0,v=d.length;m<v;m++)f=d[m],t.inputFields.has(f)||(-1!==E.indexOf(f.type)?(t.inputFields.add(f),null===p&&(p=function(t){g.Enter(t)&&(t.preventDefault(),this._submit(e))}.bind(this)),f.addEventListener("keydown",p)):console.warn("Unsupported input type.",f))}}}},_submit:function(e){var t=b.get(e),n=!0;t.inputFields.forEach(function(e){e.required&&(""===e.value.trim()?(elInnerError(e,a.get("wcf.global.form.error.empty")),n=!1):elInnerError(e,!1))}),n&&w.get(e)._dialogSubmit()},_close:function(e){e.preventDefault();var t=b.get(m);if("function"==typeof t.onBeforeClose)return t.onBeforeClose(m),!1;this.close(m)},_closeOnBackdrop:function(e){if(e.target!==v)return!0;"true"===elData(v,"close-on-click")?this._close(e):e.preventDefault()},close:function(e){e=this._getDialogId(e);var t=b.get(e);if(void 0===t)throw new Error("Expected a valid dialog id, '"+e+"' does not match any active dialog.");elAttr(t.dialog,"aria-hidden","true"),document.activeElement.closest(".dialogContainer")===t.dialog&&document.activeElement.blur(),"function"==typeof t.onClose&&t.onClose(e),m=null;for(var n=0;n<v.childElementCount;n++){var i=v.children[n];if("false"===elAttr(i,"aria-hidden")){m=elData(i,"id");break}}null===m?(elAttr(v,"aria-hidden","true"),elData(v,"close-on-click","false"),t.closable&&window.removeEventListener("keyup",C),document.documentElement.classList.remove("pageOverlayActive")):(t=b.get(m),elData(v,"close-on-click",t.backdropCloseOnClick?"true":"false")),"desktop"!==o.platform()&&d.scrollEnable()},getDialog:function(e){return b.get(this._getDialogId(e))},isOpen:function(e){var t=this.getDialog(e);return void 0!==t&&"false"===elAttr(t.dialog,"aria-hidden")},destroy:function(e){if("object"!=typeof e||e instanceof String)throw new TypeError("Expected the callback object as parameter.");if(y.has(e)){var t=y.get(e).id;this.isOpen(t)&&this.close(t),b.delete(t),y.delete(e)}},_getDialogId:function(e){if("object"==typeof e){var t=y.get(e);if(void 0!==t)return t.id}return e.toString()},_ajaxSetup:function(){return{}}}}),define("WoltLabSuite/Core/Ajax/Status",["Language"],function(e){"use strict";var t=0,n=null,i=null;return{_init:function(){n=elCreate("div"),n.classList.add("spinner");var t=elCreate("span");t.className="icon icon48 fa-spinner",n.appendChild(t);var i=elCreate("span");i.textContent=e.get("wcf.global.loading"),n.appendChild(i),document.body.appendChild(n)},show:function(){null===n&&this._init(),t++,null===i&&(i=window.setTimeout(function(){t&&n.classList.add("active"),i=null},250))},hide:function(){0===--t&&(null!==i&&window.clearTimeout(i),n.classList.remove("active"))}}}),define("WoltLabSuite/Core/Ajax/Request",["Core","Language","Dom/ChangeListener","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ajax/Status"],function(e,t,n,i,o,a){"use strict";function r(e){this._data=null,this._options={},this._previousXhr=null,this._xhr=null,this._init(e)}var s=!1,l=!1;return r.prototype={_init:function(t){this._options=e.extend({data:{},contentType:"application/x-www-form-urlencoded; charset=UTF-8",responseType:"application/json",type:"POST",url:"",withCredentials:!1,autoAbort:!1,ignoreError:!1,pinData:!1,silent:!1,includeRequestedWith:!0,failure:null,finalize:null,success:null,progress:null,uploadProgress:null,callbackObject:null},t),"object"==typeof t.callbackObject&&(this._options.callbackObject=t.callbackObject),this._options.url=e.convertLegacyUrl(this._options.url),0===this._options.url.indexOf("index.php")&&(this._options.url=WSC_API_URL+this._options.url),0===this._options.url.indexOf(WSC_API_URL)&&(this._options.includeRequestedWith=!0,this._options.withCredentials=!0),this._options.pinData&&(this._data=e.extend({},this._options.data)),null!==this._options.callbackObject&&("function"==typeof this._options.callbackObject._ajaxFailure&&(this._options.failure=this._options.callbackObject._ajaxFailure.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxFinalize&&(this._options.finalize=this._options.callbackObject._ajaxFinalize.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxSuccess&&(this._options.success=this._options.callbackObject._ajaxSuccess.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxProgress&&(this._options.progress=this._options.callbackObject._ajaxProgress.bind(this._options.callbackObject)),"function"==typeof this._options.callbackObject._ajaxUploadProgress&&(this._options.uploadProgress=this._options.callbackObject._ajaxUploadProgress.bind(this._options.callbackObject))),!1===s&&(s=!0,window.addEventListener("beforeunload",function(){l=!0}))},sendRequest:function(t){(!0===t||this._options.autoAbort)&&this.abortPrevious(),this._options.silent||a.show(),this._xhr instanceof XMLHttpRequest&&(this._previousXhr=this._xhr),this._xhr=new XMLHttpRequest,this._xhr.open(this._options.type,this._options.url,!0),this._options.contentType&&this._xhr.setRequestHeader("Content-Type",this._options.contentType),(this._options.withCredentials||this._options.includeRequestedWith)&&this._xhr.setRequestHeader("X-Requested-With","XMLHttpRequest"),this._options.withCredentials&&(this._xhr.withCredentials=!0);var n=this,i=e.clone(this._options);if(this._xhr.onload=function(){this.readyState===XMLHttpRequest.DONE&&(this.status>=200&&this.status<300||304===this.status?i.responseType&&0!==this.getResponseHeader("Content-Type").indexOf(i.responseType)?n._failure(this,i):n._success(this,i):n._failure(this,i))},this._xhr.onerror=function(){n._failure(this,i)},this._options.progress&&(this._xhr.onprogress=this._options.progress),this._options.uploadProgress&&(this._xhr.upload.onprogress=this._options.uploadProgress),"POST"===this._options.type){var o=this._options.data;"object"==typeof o&&"FormData"!==e.getType(o)&&(o=e.serialize(o)),this._xhr.send(o)}else this._xhr.send()},abortPrevious:function(){null!==this._previousXhr&&(this._previousXhr.abort(),this._previousXhr=null,this._options.silent||a.hide())},setOption:function(e,t){this._options[e]=t},getOption:function(e){return objOwns(this._options,e)?this._options[e]:null},setData:function(t){null!==this._data&&"FormData"!==e.getType(t)&&(t=e.extend(this._data,t)),this._options.data=t},_success:function(e,t){if(t.silent||a.hide(),"function"==typeof t.success){var n=null;if("application/json"===e.getResponseHeader("Content-Type")){try{n=JSON.parse(e.responseText)}catch(n){return void this._failure(e,t)}n&&n.returnValues&&void 0!==n.returnValues.template&&(n.returnValues.template=n.returnValues.template.trim()),n&&n.forceBackgroundQueuePerform&&require(["WoltLabSuite/Core/BackgroundQueue"],function(e){e.invoke()})}t.success(n,e.responseText,e,t.data)}this._finalize(t)},_failure:function(e,n){if(!l){n.silent||a.hide();var r=null;try{r=JSON.parse(e.responseText)}catch(e){}var s=!0;if("function"==typeof n.failure&&(s=n.failure(r||{},e.responseText||"",e,n.data)),!0!==n.ignoreError&&!1!==s){var c=this.getErrorHtml(r,e);c&&(void 0===o&&(o=require("Ui/Dialog")),o.openStatic(i.getUniqueId(),c,{title:t.get("wcf.global.error.title")}))}this._finalize(n)}},getErrorHtml:function(e,t){var n="",i="";if(null!==e?(e.stacktrace?n="<br><p>Stacktrace:</p><p>"+e.stacktrace+"</p>":e.exceptionID&&(n="<br><p>Exception ID: <code>"+e.exceptionID+"</code></p>"),i=e.message,e.previous.forEach(function(e){n+="<hr><p>"+e.message+"</p>",n+="<br><p>Stacktrace</p><p>"+e.stacktrace+"</p>"})):i=t.responseText,!i||"undefined"===i){if(!ENABLE_DEBUG_MODE)return null;i="XMLHttpRequest failed without a responseText. Check your browser console."}return'<div class="ajaxDebugMessage"><p>'+i+"</p>"+n+"</div>"},_finalize:function(e){"function"==typeof e.finalize&&e.finalize(this._xhr),this._previousXhr=null,n.trigger();for(var t=elBySelAll('a[href*="#"]'),i=0,o=t.length;i<o;i++){var a=t[i],r=elAttr(a,"href");-1===r.indexOf("AJAXProxy")&&-1===r.indexOf("ajax-proxy")||(r=r.substr(r.indexOf("#")),elAttr(a,"href",document.location.toString().replace(/#.*/,"")+r))}}},r}),define("WoltLabSuite/Core/Ajax",["AjaxRequest","Core","ObjectMap"],function(e,t,n){"use strict";var i=new n;return{api:function(t,n,o,a){void 0===e&&(e=require("AjaxRequest")),"object"!=typeof n&&(n={});var r=i.get(t);if(void 0===r){if("function"!=typeof t._ajaxSetup)throw new TypeError("Callback object must implement at least _ajaxSetup().");var s=t._ajaxSetup();s.pinData=!0,s.callbackObject=t,s.url||(s.url="index.php?ajax-proxy/&t="+SECURITY_TOKEN,s.withCredentials=!0),r=new e(s),i.set(t,r)}var l=null,c=null;return"function"==typeof o&&(l=r.getOption("success"),r.setOption("success",o)),"function"==typeof a&&(c=r.getOption("failure"),r.setOption("failure",a)),r.setData(n),r.sendRequest(),null!==l&&r.setOption("success",l),null!==c&&r.setOption("failure",c),r},apiOnce:function(t){void 0===e&&(e=require("AjaxRequest")),t.pinData=!1,t.callbackObject=null,t.url||(t.url="index.php?ajax-proxy/&t="+SECURITY_TOKEN,t.withCredentials=!0),new e(t).sendRequest(!1)},getRequestObject:function(e){if(!i.has(e))throw new Error("Expected a previously used callback object, provided object is unknown.");return i.get(e)}}}),define("WoltLabSuite/Core/BackgroundQueue",["Ajax"],function(e){"use strict";var t=0,n=!1,i="";return{setUrl:function(e){i=e},invoke:function(){if(""===i)return void console.error("The background queue has not been initialized yet.");n||(n=!0,e.api(this))},_ajaxSuccess:function(e){t++,e>0&&t<5?window.setTimeout(function(){n=!1,this.invoke()}.bind(this),1e3):(n=!1,t=0)},_ajaxSetup:function(){return{url:i,ignoreError:!0,silent:!0}}}}),function(){var e=function(e){"use strict";function t(e){if(e.paused||e.ended||m)return!1;try{u.clearRect(0,0,l,s),u.drawImage(e,0,0,l,s)}catch(e){}_=setTimeout(function(){t(e)},A.duration),N.setIcon(c)}function n(e){var t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;e=e.replace(t,function(e,t,n,i){return t+t+n+n+i+i});var n=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return!!n&&{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}}function i(e,t){var n,i={};for(n in e)i[n]=e[n];for(n in t)i[n]=t[n];return i}function o(){return y.hidden||y.msHidden||y.webkitHidden||y.mozHidden}e=e||{};var a,r,s,l,c,u,d,h,f,p,g,m,v,b,_,y,w={bgColor:"#d00",textColor:"#fff",fontFamily:"sans-serif",fontStyle:"bold",type:"circle",position:"down",animation:"slide",elementId:!1,element:null,dataUrl:!1,win:window};v={},v.ff="undefined"!=typeof InstallTrigger,v.chrome=!!window.chrome,v.opera=!!window.opera||navigator.userAgent.indexOf("Opera")>=0,v.ie=!1,v.safari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,v.supported=v.chrome||v.ff||v.opera;var C=[];g=function(){},h=m=!1;var L={};L.ready=function(){h=!0,L.reset(),g()},L.reset=function(){h&&(C=[],f=!1,p=!1,u.clearRect(0,0,l,s),u.drawImage(d,0,0,l,s),N.setIcon(c),window.clearTimeout(b),window.clearTimeout(_))},L.start=function(){if(h&&!p){var e=function(){f=C[0],p=!1,C.length>0&&(C.shift(),L.start())};if(C.length>0){p=!0;var t=function(){["type","animation","bgColor","textColor","fontFamily","fontStyle"].forEach(function(e){e in C[0].options&&(a[e]=C[0].options[e])}),A.run(C[0].options,function(){e()},!1)};f?A.run(f.options,function(){t()},!0):t()}}};var S={},E=function(e){return e.n="number"==typeof e.n?Math.abs(0|e.n):e.n,e.x=l*e.x,e.y=s*e.y,e.w=l*e.w,e.h=s*e.h,e.len=(""+e.n).length,e};S.circle=function(e){e=E(e);var t=!1;2===e.len?(e.x=e.x-.4*e.w,e.w=1.4*e.w,t=!0):e.len>=3&&(e.x=e.x-.65*e.w,e.w=1.65*e.w,t=!0),u.clearRect(0,0,l,s),u.drawImage(d,0,0,l,s),u.beginPath(),u.font=a.fontStyle+" "+Math.floor(e.h*(e.n>99?.85:1))+"px "+a.fontFamily,u.textAlign="center",t?(u.moveTo(e.x+e.w/2,e.y),u.lineTo(e.x+e.w-e.h/2,e.y),u.quadraticCurveTo(e.x+e.w,e.y,e.x+e.w,e.y+e.h/2),u.lineTo(e.x+e.w,e.y+e.h-e.h/2),u.quadraticCurveTo(e.x+e.w,e.y+e.h,e.x+e.w-e.h/2,e.y+e.h),u.lineTo(e.x+e.h/2,e.y+e.h),u.quadraticCurveTo(e.x,e.y+e.h,e.x,e.y+e.h-e.h/2),u.lineTo(e.x,e.y+e.h/2),u.quadraticCurveTo(e.x,e.y,e.x+e.h/2,e.y)):u.arc(e.x+e.w/2,e.y+e.h/2,e.h/2,0,2*Math.PI),u.fillStyle="rgba("+a.bgColor.r+","+a.bgColor.g+","+a.bgColor.b+","+e.o+")",u.fill(),u.closePath(),u.beginPath(),u.stroke(),u.fillStyle="rgba("+a.textColor.r+","+a.textColor.g+","+a.textColor.b+","+e.o+")","number"==typeof e.n&&e.n>999?u.fillText((e.n>9999?9:Math.floor(e.n/1e3))+"k+",Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.2*e.h)):u.fillText(e.n,Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.15*e.h)),u.closePath()},S.rectangle=function(e){e=E(e);2===e.len?(e.x=e.x-.4*e.w,e.w=1.4*e.w):e.len>=3&&(e.x=e.x-.65*e.w,e.w=1.65*e.w),u.clearRect(0,0,l,s),u.drawImage(d,0,0,l,s),u.beginPath(),u.font=a.fontStyle+" "+Math.floor(e.h*(e.n>99?.9:1))+"px "+a.fontFamily,u.textAlign="center",u.fillStyle="rgba("+a.bgColor.r+","+a.bgColor.g+","+a.bgColor.b+","+e.o+")",u.fillRect(e.x,e.y,e.w,e.h),u.fillStyle="rgba("+a.textColor.r+","+a.textColor.g+","+a.textColor.b+","+e.o+")","number"==typeof e.n&&e.n>999?u.fillText((e.n>9999?9:Math.floor(e.n/1e3))+"k+",Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.2*e.h)):u.fillText(e.n,Math.floor(e.x+e.w/2),Math.floor(e.y+e.h-.15*e.h)),u.closePath()};var x=function(e,t){t=("string"==typeof t?{animation:t}:t)||{},g=function(){try{if("number"==typeof e?e>0:""!==e){var i={type:"badge",options:{n:e}};if("animation"in t&&A.types[""+t.animation]&&(i.options.animation=""+t.animation),"type"in t&&S[""+t.type]&&(i.options.type=""+t.type),["bgColor","textColor"].forEach(function(e){e in t&&(i.options[e]=n(t[e]))}),["fontStyle","fontFamily"].forEach(function(e){e in t&&(i.options[e]=t[e])}),C.push(i),C.length>100)throw new Error("Too many badges requests in queue.");L.start()}else L.reset()}catch(e){throw new Error("Error setting badge. Message: "+e.message)}},h&&g()},D=function(e){g=function(){try{var t=e.width,n=e.height,i=document.createElement("img"),o=t/l<n/s?t/l:n/s;i.setAttribute("crossOrigin","anonymous"),i.onload=function(){u.clearRect(0,0,l,s),u.drawImage(i,0,0,l,s),N.setIcon(c)},i.setAttribute("src",e.getAttribute("src")),i.height=n/o,i.width=t/o}catch(e){throw new Error("Error setting image. Message: "+e.message)}},h&&g()},T=function(e){g=function(){N.setIconSrc(e)},h&&g()},k=function(e){g=function(){try{if("stop"===e)return m=!0,L.reset(),void(m=!1);e.addEventListener("play",function(){t(this)},!1)}catch(e){throw new Error("Error setting video. Message: "+e.message)}},h&&g()},I=function(e){if(window.URL&&window.URL.createObjectURL||(window.URL=window.URL||{},window.URL.createObjectURL=function(e){return e}),v.supported){var n=!1;navigator.getUserMedia=navigator.getUserMedia||navigator.oGetUserMedia||navigator.msGetUserMedia||navigator.mozGetUserMedia||navigator.webkitGetUserMedia,g=function(){try{if("stop"===e)return m=!0,L.reset(),void(m=!1);n=document.createElement("video"),n.width=l,n.height=s,navigator.getUserMedia({video:!0,audio:!1},function(e){n.src=URL.createObjectURL(e),n.play(),t(n)},function(){})}catch(e){throw new Error("Error setting webcam. Message: "+e.message)}},h&&g()}},M=function(e,t){var i=e;null==t&&"[object Object]"==Object.prototype.toString.call(e)||(i={},i[e]=t);for(var o=Object.keys(i),r=0;r<o.length;r++)"bgColor"==o[r]||"textColor"==o[r]?a[o[r]]=n(i[o[r]]):a[o[r]]=i[o[r]];C.push(f),L.start()},N={};N.getIcons=function(){var e=[];return a.element?e=[a.element]:a.elementId?(e=[y.getElementById(a.elementId)],e[0].setAttribute("href",e[0].getAttribute("src"))):(e=function(){for(var e=[],t=y.getElementsByTagName("head")[0].getElementsByTagName("link"),n=0;n<t.length;n++)/(^|\s)icon(\s|$)/i.test(t[n].getAttribute("rel"))&&e.push(t[n]);return e}(),0===e.length&&(e=[y.createElement("link")],e[0].setAttribute("rel","icon"),y.getElementsByTagName("head")[0].appendChild(e[0]))),e.forEach(function(e){e.setAttribute("type","image/png")}),e},N.setIcon=function(e){var t=e.toDataURL("image/png");N.setIconSrc(t)},N.setIconSrc=function(e){if(a.dataUrl&&a.dataUrl(e),a.element)a.element.setAttribute("href",e),a.element.setAttribute("src",e);else if(a.elementId){var t=y.getElementById(a.elementId);t.setAttribute("href",e),t.setAttribute("src",e)}else if(v.ff||v.opera){var n=r[r.length-1],i=y.createElement("link");r=[i],v.opera&&i.setAttribute("rel","icon"),i.setAttribute("rel","icon"),i.setAttribute("type","image/png"),y.getElementsByTagName("head")[0].appendChild(i),i.setAttribute("href",e),n.parentNode&&n.parentNode.removeChild(n)}else r.forEach(function(t){t.setAttribute("href",e)})};var A={};return A.duration=40,A.types={},A.types.fade=[{x:.4,y:.4,w:.6,h:.6,o:0},{x:.4,y:.4,w:.6,h:.6,o:.1},{x:.4,y:.4,w:.6,h:.6,o:.2},{x:.4,y:.4,w:.6,h:.6,o:.3},{x:.4,y:.4,w:.6,h:.6,o:.4},{x:.4,y:.4,w:.6,h:.6,o:.5},{x:.4,y:.4,w:.6,h:.6,o:.6},{x:.4,y:.4,w:.6,h:.6,o:.7},{x:.4,y:.4,w:.6,h:.6,o:.8},{x:.4,y:.4,w:.6,h:.6,o:.9},{x:.4,y:.4,w:.6,h:.6,o:1}],A.types.none=[{x:.4,y:.4,w:.6,h:.6,o:1}],A.types.pop=[{x:1,y:1,w:0,h:0,o:1},{x:.9,y:.9,w:.1,h:.1,o:1},{x:.8,y:.8,w:.2,h:.2,o:1},{x:.7,y:.7,w:.3,h:.3,o:1},{x:.6,y:.6,w:.4,h:.4,o:1},{x:.5,y:.5,w:.5,h:.5,o:1},{x:.4,y:.4,w:.6,h:.6,o:1}],A.types.popFade=[{x:.75,y:.75,w:0,h:0,o:0},{x:.65,y:.65,w:.1,h:.1,o:.2},{x:.6,y:.6,w:.2,h:.2,o:.4},{x:.55,y:.55,w:.3,h:.3,o:.6},{x:.5,y:.5,w:.4,h:.4,o:.8},{x:.45,y:.45,w:.5,h:.5,o:.9},{x:.4,y:.4,w:.6,h:.6,o:1}],A.types.slide=[{x:.4,y:1,w:.6,h:.6,o:1},{x:.4,y:.9,w:.6,h:.6,o:1},{x:.4,y:.9,w:.6,h:.6,o:1},{x:.4,y:.8,w:.6,h:.6,o:1},{x:.4,y:.7,w:.6,h:.6,o:1},{x:.4,y:.6,w:.6,h:.6,o:1},{x:.4,y:.5,w:.6,h:.6,o:1},{x:.4,y:.4,w:.6,h:.6,o:1}],A.run=function(e,t,n,r){var s=A.types[o()?"none":a.animation];if(r=!0===n?void 0!==r?r:s.length-1:void 0!==r?r:0,t=t||function(){},!(r<s.length&&r>=0))return void t();S[a.type](i(e,s[r])),b=setTimeout(function(){n?r-=1:r+=1,A.run(e,t,n,r)},A.duration),N.setIcon(c)},function(){a=i(w,e),a.bgColor=n(a.bgColor),a.textColor=n(a.textColor),a.position=a.position.toLowerCase(),a.animation=A.types[""+a.animation]?a.animation:w.animation,y=a.win.document;var t=a.position.indexOf("up")>-1,o=a.position.indexOf("left")>-1;if(t||o)for(var h in A.types)for(var f=0;f<A.types[h].length;f++){var p=A.types[h][f];t&&(p.y<.6?p.y=p.y-.4:p.y=p.y-2*p.y+(1-p.w)),o&&(p.x<.6?p.x=p.x-.4:p.x=p.x-2*p.x+(1-p.h)),A.types[h][f]=p}a.type=S[""+a.type]?a.type:w.type,r=N.getIcons(),c=document.createElement("canvas"),d=document.createElement("img");var g=r[r.length-1];g.hasAttribute("href")?(d.setAttribute("crossOrigin","anonymous"),d.onload=function(){s=d.height>0?d.height:32,l=d.width>0?d.width:32,c.height=s,c.width=l,u=c.getContext("2d"),L.ready()},d.setAttribute("src",g.getAttribute("href"))):(s=32,l=32,d.height=s,d.width=l,c.height=s,c.width=l,u=c.getContext("2d"),L.ready())}(),{badge:x,video:k,image:D,rawImageSrc:T,webcam:I,setOpt:M,reset:L.reset,browser:{supported:v.supported}}};void 0!==define&&define.amd?define("favico",[],function(){return e}):"undefined"!=typeof module&&module.exports?module.exports=e:this.Favico=e}(),function e(t,n,i){function o(r,s){if(!n[r]){if(!t[r]){var l="function"==typeof require&&require;if(!s&&l)return l(r,!0);if(a)return a(r,!0);var c=new Error("Cannot find module '"+r+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[r]={exports:{}};t[r][0].call(u.exports,function(e){var n=t[r][1][e];return o(n||e)},u,u.exports,e,t,n,i)}return n[r].exports}for(var a="function"==typeof require&&require,r=0;r<i.length;r++)o(i[r]);return o}({1:[function(e,t,n){"use strict";var i=e("../main");"function"==typeof define&&define.amd?define("perfect-scrollbar",i):(window.PerfectScrollbar=i,void 0===window.Ps&&(window.Ps=i))},{"../main":7}],2:[function(e,t,n){"use strict";function i(e,t){var n=e.className.split(" ");n.indexOf(t)<0&&n.push(t),e.className=n.join(" ")}function o(e,t){var n=e.className.split(" "),i=n.indexOf(t);i>=0&&n.splice(i,1),e.className=n.join(" ")}n.add=function(e,t){e.classList?e.classList.add(t):i(e,t)},n.remove=function(e,t){e.classList?e.classList.remove(t):o(e,t)},n.list=function(e){return e.classList?e.classList:e.className.split(" ")}},{}],3:[function(e,t,n){"use strict";function i(e,t){return window.getComputedStyle(e)[t]}function o(e,t,n){return"number"==typeof n&&(n=n.toString()+"px"),e.style[t]=n,e}function a(e,t){for(var n in t){var i=t[n];"number"==typeof i&&(i=i.toString()+"px"),e.style[n]=i}return e}n.e=function(e,t){var n=document.createElement(e);return n.className=t,n},n.appendTo=function(e,t){return t.appendChild(e),e},n.css=function(e,t,n){return"object"==typeof t?a(e,t):void 0===n?i(e,t):o(e,t,n)},n.matches=function(e,t){return void 0!==e.matches?e.matches(t):void 0!==e.matchesSelector?e.matchesSelector(t):void 0!==e.webkitMatchesSelector?e.webkitMatchesSelector(t):void 0!==e.mozMatchesSelector?e.mozMatchesSelector(t):void 0!==e.msMatchesSelector?e.msMatchesSelector(t):void 0},n.remove=function(e){void 0!==e.remove?e.remove():e.parentNode&&e.parentNode.removeChild(e)}},{}],4:[function(e,t,n){"use strict";var i=function(e){this.element=e,this.events={}};i.prototype.bind=function(e,t){void 0===this.events[e]&&(this.events[e]=[]),this.events[e].push(t),this.element.addEventListener(e,t,!1)},i.prototype.unbind=function(e,t){var n=void 0!==t;this.events[e]=this.events[e].filter(function(i){return!(!n||i===t)||(this.element.removeEventListener(e,i,!1),!1)},this)},i.prototype.unbindAll=function(){for(var e in this.events)this.unbind(e)};var o=function(){this.eventElements=[]};o.prototype.eventElement=function(e){var t=this.eventElements.filter(function(t){return t.element===e})[0];return void 0===t&&(t=new i(e),this.eventElements.push(t)),t},o.prototype.bind=function(e,t,n){this.eventElement(e).bind(t,n)},o.prototype.unbind=function(e,t,n){this.eventElement(e).unbind(t,n)},o.prototype.unbindAll=function(){for(var e=0;e<this.eventElements.length;e++)this.eventElements[e].unbindAll()},o.prototype.once=function(e,t,n){var i=this.eventElement(e),o=function(e){i.unbind(t,o),n(e)};i.bind(t,o)},t.exports=o},{}],5:[function(e,t,n){"use strict";t.exports=function(){function e(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return function(){return e()+e()+"-"+e()+"-"+e()+"-"+e()+"-"+e()+e()+e()}}()},{}],6:[function(e,t,n){"use strict";var i=e("./class"),o=e("./dom");n.toInt=function(e){return"string"==typeof e?parseInt(e,10):~~e},n.clone=function(e){if(null===e)return null;if("object"==typeof e){var t={};for(var n in e)t[n]=this.clone(e[n]);return t}return e},n.extend=function(e,t){var n=this.clone(e);for(var i in t)n[i]=this.clone(t[i]);return n},n.isEditable=function(e){return o.matches(e,"input,[contenteditable]")||o.matches(e,"select,[contenteditable]")||o.matches(e,"textarea,[contenteditable]")||o.matches(e,"button,[contenteditable]")},n.removePsClasses=function(e){for(var t=i.list(e),n=0;n<t.length;n++){var o=t[n];0===o.indexOf("ps-")&&i.remove(e,o)}},n.outerWidth=function(e){return this.toInt(o.css(e,"width"))+this.toInt(o.css(e,"paddingLeft"))+this.toInt(o.css(e,"paddingRight"))+this.toInt(o.css(e,"borderLeftWidth"))+this.toInt(o.css(e,"borderRightWidth"))},n.startScrolling=function(e,t){i.add(e,"ps-in-scrolling"),void 0!==t?i.add(e,"ps-"+t):(i.add(e,"ps-x"),i.add(e,"ps-y"))},n.stopScrolling=function(e,t){i.remove(e,"ps-in-scrolling"),void 0!==t?i.remove(e,"ps-"+t):(i.remove(e,"ps-x"),i.remove(e,"ps-y"))},n.env={isWebKit:"WebkitAppearance"in document.documentElement.style,supportsTouch:"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch,supportsIePointer:null!==window.navigator.msMaxTouchPoints}},{"./class":2,"./dom":3}],7:[function(e,t,n){"use strict";var i=e("./plugin/destroy"),o=e("./plugin/initialize"),a=e("./plugin/update");t.exports={initialize:o,update:a,destroy:i}},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":20}],8:[function(e,t,n){"use strict";t.exports={wheelSpeed:1,wheelPropagation:!1,swipePropagation:!0,minScrollbarLength:null,maxScrollbarLength:null,useBothWheelAxes:!1,useKeyboard:!0,suppressScrollX:!1,suppressScrollY:!1,scrollXMarginOffset:0,scrollYMarginOffset:0}},{}],9:[function(e,t,n){"use strict";var i=e("../lib/dom"),o=e("../lib/helper"),a=e("./instances");t.exports=function(e){
-var t=a.get(e);t.event.unbindAll(),i.remove(t.scrollbarX),i.remove(t.scrollbarY),i.remove(t.scrollbarXRail),i.remove(t.scrollbarYRail),o.removePsClasses(e),a.remove(e)}},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(e,t,n){"use strict";function i(e,t){function n(e){return e.getBoundingClientRect()}var i=window.Event.prototype.stopPropagation.bind;t.event.bind(t.scrollbarY,"click",i),t.event.bind(t.scrollbarYRail,"click",function(i){var a=o.toInt(t.scrollbarYHeight/2),s=i.pageY-n(t.scrollbarYRail).top-a,l=t.containerHeight-t.scrollbarYHeight,c=s/l;c<0?c=0:c>1&&(c=1),e.scrollTop=(t.contentHeight-t.containerHeight)*c,r(e)}),t.event.bind(t.scrollbarX,"click",i),t.event.bind(t.scrollbarXRail,"click",function(i){var a=o.toInt(t.scrollbarXWidth/2),s=i.pageX-n(t.scrollbarXRail).left-a;console.log(i.pageX,t.scrollbarXRail.offsetLeft);var l=t.containerWidth-t.scrollbarXWidth,c=s/l;c<0?c=0:c>1&&(c=1),e.scrollLeft=(t.contentWidth-t.containerWidth)*c,r(e)})}var o=e("../../lib/helper"),a=e("../instances"),r=e("../update-geometry");t.exports=function(e){i(e,a.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],11:[function(e,t,n){"use strict";function i(e,t){function n(n){var o=i+n,a=t.containerWidth-t.scrollbarXWidth;t.scrollbarXLeft=o<0?0:o>a?a:o;var s=r.toInt(t.scrollbarXLeft*(t.contentWidth-t.containerWidth)/(t.containerWidth-t.scrollbarXWidth));e.scrollLeft=s}var i=null,o=null,s=function(t){n(t.pageX-o),l(e),t.stopPropagation(),t.preventDefault()},c=function(){r.stopScrolling(e,"x"),t.event.unbind(t.ownerDocument,"mousemove",s)};t.event.bind(t.scrollbarX,"mousedown",function(n){o=n.pageX,i=r.toInt(a.css(t.scrollbarX,"left")),r.startScrolling(e,"x"),t.event.bind(t.ownerDocument,"mousemove",s),t.event.once(t.ownerDocument,"mouseup",c),n.stopPropagation(),n.preventDefault()})}function o(e,t){function n(n){var o=i+n,a=t.containerHeight-t.scrollbarYHeight;t.scrollbarYTop=o<0?0:o>a?a:o;var s=r.toInt(t.scrollbarYTop*(t.contentHeight-t.containerHeight)/(t.containerHeight-t.scrollbarYHeight));e.scrollTop=s}var i=null,o=null,s=function(t){n(t.pageY-o),l(e),t.stopPropagation(),t.preventDefault()},c=function(){r.stopScrolling(e,"y"),t.event.unbind(t.ownerDocument,"mousemove",s)};t.event.bind(t.scrollbarY,"mousedown",function(n){o=n.pageY,i=r.toInt(a.css(t.scrollbarY,"top")),r.startScrolling(e,"y"),t.event.bind(t.ownerDocument,"mousemove",s),t.event.once(t.ownerDocument,"mouseup",c),n.stopPropagation(),n.preventDefault()})}var a=e("../../lib/dom"),r=e("../../lib/helper"),s=e("../instances"),l=e("../update-geometry");t.exports=function(e){var t=s.get(e);i(e,t),o(e,t)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19}],12:[function(e,t,n){"use strict";function i(e,t){function n(n,i){var o=e.scrollTop;if(0===n){if(!t.scrollbarYActive)return!1;if(0===o&&i>0||o>=t.contentHeight-t.containerHeight&&i<0)return!t.settings.wheelPropagation}var a=e.scrollLeft;if(0===i){if(!t.scrollbarXActive)return!1;if(0===a&&n<0||a>=t.contentWidth-t.containerWidth&&n>0)return!t.settings.wheelPropagation}return!0}var i=!1;t.event.bind(e,"mouseenter",function(){i=!0}),t.event.bind(e,"mouseleave",function(){i=!1});var a=!1;t.event.bind(t.ownerDocument,"keydown",function(s){if((!s.isDefaultPrevented||!s.isDefaultPrevented())&&i){var l=document.activeElement?document.activeElement:t.ownerDocument.activeElement;if(l){for(;l.shadowRoot;)l=l.shadowRoot.activeElement;if(o.isEditable(l))return}var c=0,u=0;switch(s.which){case 37:c=-30;break;case 38:u=30;break;case 39:c=30;break;case 40:u=-30;break;case 33:u=90;break;case 32:case 34:u=-90;break;case 35:u=s.ctrlKey?-t.contentHeight:-t.containerHeight;break;case 36:u=s.ctrlKey?e.scrollTop:t.containerHeight;break;default:return}e.scrollTop=e.scrollTop-u,e.scrollLeft=e.scrollLeft+c,r(e),a=n(c,u),a&&s.preventDefault()}})}var o=e("../../lib/helper"),a=e("../instances"),r=e("../update-geometry");t.exports=function(e){i(e,a.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],13:[function(e,t,n){"use strict";function i(e,t){function n(n,i){var o=e.scrollTop;if(0===n){if(!t.scrollbarYActive)return!1;if(0===o&&i>0||o>=t.contentHeight-t.containerHeight&&i<0)return!t.settings.wheelPropagation}var a=e.scrollLeft;if(0===i){if(!t.scrollbarXActive)return!1;if(0===a&&n<0||a>=t.contentWidth-t.containerWidth&&n>0)return!t.settings.wheelPropagation}return!0}function i(e){var t=e.deltaX,n=-1*e.deltaY;return void 0!==t&&void 0!==n||(t=-1*e.wheelDeltaX/6,n=e.wheelDeltaY/6),e.deltaMode&&1===e.deltaMode&&(t*=10,n*=10),t!==t&&n!==n&&(t=0,n=e.wheelDelta),[t,n]}function a(t,n){var i=e.querySelector("textarea:hover");if(i){var o=i.scrollHeight-i.clientHeight;if(o>0&&!(0===i.scrollTop&&n>0||i.scrollTop===o&&n<0))return!0;var a=i.scrollLeft-i.clientWidth;if(a>0&&!(0===i.scrollLeft&&t<0||i.scrollLeft===a&&t>0))return!0}return!1}function s(s){if(o.env.isWebKit||!e.querySelector("select:focus")){var c=i(s),u=c[0],d=c[1];a(u,d)||(l=!1,t.settings.useBothWheelAxes?t.scrollbarYActive&&!t.scrollbarXActive?(e.scrollTop=d?e.scrollTop-d*t.settings.wheelSpeed:e.scrollTop+u*t.settings.wheelSpeed,l=!0):t.scrollbarXActive&&!t.scrollbarYActive&&(e.scrollLeft=u?e.scrollLeft+u*t.settings.wheelSpeed:e.scrollLeft-d*t.settings.wheelSpeed,l=!0):(e.scrollTop=e.scrollTop-d*t.settings.wheelSpeed,e.scrollLeft=e.scrollLeft+u*t.settings.wheelSpeed),r(e),(l=l||n(u,d))&&(s.stopPropagation(),s.preventDefault()))}}var l=!1;void 0!==window.onwheel?t.event.bind(e,"wheel",s):void 0!==window.onmousewheel&&t.event.bind(e,"mousewheel",s)}var o=e("../../lib/helper"),a=e("../instances"),r=e("../update-geometry");t.exports=function(e){i(e,a.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],14:[function(e,t,n){"use strict";function i(e,t){t.event.bind(e,"scroll",function(){a(e)})}var o=e("../instances"),a=e("../update-geometry");t.exports=function(e){i(e,o.get(e))}},{"../instances":18,"../update-geometry":19}],15:[function(e,t,n){"use strict";function i(e,t){function n(){var e=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===e.toString().length?null:e.getRangeAt(0).commonAncestorContainer}function i(){l||(l=setInterval(function(){if(!a.get(e))return void clearInterval(l);e.scrollTop=e.scrollTop+c.top,e.scrollLeft=e.scrollLeft+c.left,r(e)},50))}function s(){l&&(clearInterval(l),l=null),o.stopScrolling(e)}var l=null,c={top:0,left:0},u=!1;t.event.bind(t.ownerDocument,"selectionchange",function(){e.contains(n())?u=!0:(u=!1,s())}),t.event.bind(window,"mouseup",function(){u&&(u=!1,s())}),t.event.bind(window,"mousemove",function(t){if(u){var n={x:t.pageX,y:t.pageY},a={left:e.offsetLeft,right:e.offsetLeft+e.offsetWidth,top:e.offsetTop,bottom:e.offsetTop+e.offsetHeight};n.x<a.left+3?(c.left=-5,o.startScrolling(e,"x")):n.x>a.right-3?(c.left=5,o.startScrolling(e,"x")):c.left=0,n.y<a.top+3?(c.top=a.top+3-n.y<5?-5:-20,o.startScrolling(e,"y")):n.y>a.bottom-3?(c.top=n.y-a.bottom+3<5?5:20,o.startScrolling(e,"y")):c.top=0,0===c.top&&0===c.left?s():i()}})}var o=e("../../lib/helper"),a=e("../instances"),r=e("../update-geometry");t.exports=function(e){i(e,a.get(e))}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19}],16:[function(e,t,n){"use strict";function i(e,t,n,i){function r(n,i){var o=e.scrollTop,a=e.scrollLeft,r=Math.abs(n),s=Math.abs(i);if(s>r){if(i<0&&o===t.contentHeight-t.containerHeight||i>0&&0===o)return!t.settings.swipePropagation}else if(r>s&&(n<0&&a===t.contentWidth-t.containerWidth||n>0&&0===a))return!t.settings.swipePropagation;return!0}function s(t,n){e.scrollTop=e.scrollTop-n,e.scrollLeft=e.scrollLeft-t,a(e)}function l(){_=!0}function c(){_=!1}function u(e){return e.targetTouches?e.targetTouches[0]:e}function d(e){return!(!e.targetTouches||1!==e.targetTouches.length)||!(!e.pointerType||"mouse"===e.pointerType||e.pointerType===e.MSPOINTER_TYPE_MOUSE)}function h(e){if(d(e)){y=!0;var t=u(e);g.pageX=t.pageX,g.pageY=t.pageY,m=(new Date).getTime(),null!==b&&clearInterval(b),e.stopPropagation()}}function f(e){if(!_&&y&&d(e)){var t=u(e),n={pageX:t.pageX,pageY:t.pageY},i=n.pageX-g.pageX,o=n.pageY-g.pageY;s(i,o),g=n;var a=(new Date).getTime(),l=a-m;l>0&&(v.x=i/l,v.y=o/l,m=a),r(i,o)&&(e.stopPropagation(),e.preventDefault())}}function p(){!_&&y&&(y=!1,clearInterval(b),b=setInterval(function(){return o.get(e)?Math.abs(v.x)<.01&&Math.abs(v.y)<.01?void clearInterval(b):(s(30*v.x,30*v.y),v.x*=.8,void(v.y*=.8)):void clearInterval(b)},10))}var g={},m=0,v={},b=null,_=!1,y=!1;n&&(t.event.bind(window,"touchstart",l),t.event.bind(window,"touchend",c),t.event.bind(e,"touchstart",h),t.event.bind(e,"touchmove",f),t.event.bind(e,"touchend",p)),i&&(window.PointerEvent?(t.event.bind(window,"pointerdown",l),t.event.bind(window,"pointerup",c),t.event.bind(e,"pointerdown",h),t.event.bind(e,"pointermove",f),t.event.bind(e,"pointerup",p)):window.MSPointerEvent&&(t.event.bind(window,"MSPointerDown",l),t.event.bind(window,"MSPointerUp",c),t.event.bind(e,"MSPointerDown",h),t.event.bind(e,"MSPointerMove",f),t.event.bind(e,"MSPointerUp",p)))}var o=e("../instances"),a=e("../update-geometry");t.exports=function(e,t,n){i(e,o.get(e),t,n)}},{"../instances":18,"../update-geometry":19}],17:[function(e,t,n){"use strict";var i=e("../lib/class"),o=e("../lib/helper"),a=e("./instances"),r=e("./update-geometry"),s=e("./handler/click-rail"),l=e("./handler/drag-scrollbar"),c=e("./handler/keyboard"),u=e("./handler/mouse-wheel"),d=e("./handler/native-scroll"),h=e("./handler/selection"),f=e("./handler/touch");t.exports=function(e,t){t="object"==typeof t?t:{},i.add(e,"ps-container");var n=a.add(e);n.settings=o.extend(n.settings,t),s(e),l(e),u(e),d(e),h(e),(o.env.supportsTouch||o.env.supportsIePointer)&&f(e,o.env.supportsTouch,o.env.supportsIePointer),n.settings.useKeyboard&&c(e),r(e)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(e,t,n){"use strict";function i(e){var t=this;t.settings=d.clone(l),t.containerWidth=null,t.containerHeight=null,t.contentWidth=null,t.contentHeight=null,t.isRtl="rtl"===s.css(e,"direction"),t.event=new c,t.ownerDocument=e.ownerDocument||document,t.scrollbarXRail=s.appendTo(s.e("div","ps-scrollbar-x-rail"),e),t.scrollbarX=s.appendTo(s.e("div","ps-scrollbar-x"),t.scrollbarXRail),t.scrollbarXActive=null,t.scrollbarXWidth=null,t.scrollbarXLeft=null,t.scrollbarXBottom=d.toInt(s.css(t.scrollbarXRail,"bottom")),t.isScrollbarXUsingBottom=t.scrollbarXBottom===t.scrollbarXBottom,t.scrollbarXTop=t.isScrollbarXUsingBottom?null:d.toInt(s.css(t.scrollbarXRail,"top")),t.railBorderXWidth=d.toInt(s.css(t.scrollbarXRail,"borderLeftWidth"))+d.toInt(s.css(t.scrollbarXRail,"borderRightWidth")),t.railXMarginWidth=d.toInt(s.css(t.scrollbarXRail,"marginLeft"))+d.toInt(s.css(t.scrollbarXRail,"marginRight")),t.railXWidth=null,t.scrollbarYRail=s.appendTo(s.e("div","ps-scrollbar-y-rail"),e),t.scrollbarY=s.appendTo(s.e("div","ps-scrollbar-y"),t.scrollbarYRail),t.scrollbarYActive=null,t.scrollbarYHeight=null,t.scrollbarYTop=null,t.scrollbarYRight=d.toInt(s.css(t.scrollbarYRail,"right")),t.isScrollbarYUsingRight=t.scrollbarYRight===t.scrollbarYRight,t.scrollbarYLeft=t.isScrollbarYUsingRight?null:d.toInt(s.css(t.scrollbarYRail,"left")),t.scrollbarYOuterWidth=t.isRtl?d.outerWidth(t.scrollbarY):null,t.railBorderYWidth=d.toInt(s.css(t.scrollbarYRail,"borderTopWidth"))+d.toInt(s.css(t.scrollbarYRail,"borderBottomWidth")),t.railYMarginHeight=d.toInt(s.css(t.scrollbarYRail,"marginTop"))+d.toInt(s.css(t.scrollbarYRail,"marginBottom")),t.railYHeight=null}function o(e){return void 0===e.dataset?e.getAttribute("data-ps-id"):e.dataset.psId}function a(e,t){void 0===e.dataset?e.setAttribute("data-ps-id",t):e.dataset.psId=t}function r(e){void 0===e.dataset?e.removeAttribute("data-ps-id"):delete e.dataset.psId}var s=e("../lib/dom"),l=e("./default-setting"),c=e("../lib/event-manager"),u=e("../lib/guid"),d=e("../lib/helper"),h={};n.add=function(e){var t=u();return a(e,t),h[t]=new i(e),h[t]},n.remove=function(e){delete h[o(e)],r(e)},n.get=function(e){return h[o(e)]}},{"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(e,t,n){"use strict";function i(e,t){return e.settings.minScrollbarLength&&(t=Math.max(t,e.settings.minScrollbarLength)),e.settings.maxScrollbarLength&&(t=Math.min(t,e.settings.maxScrollbarLength)),t}function o(e,t){var n={width:t.railXWidth};t.isRtl?n.left=e.scrollLeft+t.containerWidth-t.contentWidth:n.left=e.scrollLeft,t.isScrollbarXUsingBottom?n.bottom=t.scrollbarXBottom-e.scrollTop:n.top=t.scrollbarXTop+e.scrollTop,r.css(t.scrollbarXRail,n);var i={top:e.scrollTop,height:t.railYHeight};t.isScrollbarYUsingRight?t.isRtl?i.right=t.contentWidth-e.scrollLeft-t.scrollbarYRight-t.scrollbarYOuterWidth:i.right=t.scrollbarYRight-e.scrollLeft:t.isRtl?i.left=e.scrollLeft+2*t.containerWidth-t.contentWidth-t.scrollbarYLeft-t.scrollbarYOuterWidth:i.left=t.scrollbarYLeft+e.scrollLeft,r.css(t.scrollbarYRail,i),r.css(t.scrollbarX,{left:t.scrollbarXLeft,width:t.scrollbarXWidth-t.railBorderXWidth}),r.css(t.scrollbarY,{top:t.scrollbarYTop,height:t.scrollbarYHeight-t.railBorderYWidth})}var a=e("../lib/class"),r=e("../lib/dom"),s=e("../lib/helper"),l=e("./instances");t.exports=function(e){var t=l.get(e);t.containerWidth=e.clientWidth,t.containerHeight=e.clientHeight,t.contentWidth=e.scrollWidth,t.contentHeight=e.scrollHeight,e.contains(t.scrollbarXRail)||r.appendTo(t.scrollbarXRail,e),e.contains(t.scrollbarYRail)||r.appendTo(t.scrollbarYRail,e),!t.settings.suppressScrollX&&t.containerWidth+t.settings.scrollXMarginOffset<t.contentWidth?(t.scrollbarXActive=!0,t.railXWidth=t.containerWidth-t.railXMarginWidth,t.scrollbarXWidth=i(t,s.toInt(t.railXWidth*t.containerWidth/t.contentWidth)),t.scrollbarXLeft=s.toInt(e.scrollLeft*(t.railXWidth-t.scrollbarXWidth)/(t.contentWidth-t.containerWidth))):(t.scrollbarXActive=!1,t.scrollbarXWidth=0,t.scrollbarXLeft=0,e.scrollLeft=0),!t.settings.suppressScrollY&&t.containerHeight+t.settings.scrollYMarginOffset<t.contentHeight?(t.scrollbarYActive=!0,t.railYHeight=t.containerHeight-t.railYMarginHeight,t.scrollbarYHeight=i(t,s.toInt(t.railYHeight*t.containerHeight/t.contentHeight)),t.scrollbarYTop=s.toInt(e.scrollTop*(t.railYHeight-t.scrollbarYHeight)/(t.contentHeight-t.containerHeight))):(t.scrollbarYActive=!1,t.scrollbarYHeight=0,t.scrollbarYTop=0,e.scrollTop=0),t.scrollbarXLeft>=t.railXWidth-t.scrollbarXWidth&&(t.scrollbarXLeft=t.railXWidth-t.scrollbarXWidth),t.scrollbarYTop>=t.railYHeight-t.scrollbarYHeight&&(t.scrollbarYTop=t.railYHeight-t.scrollbarYHeight),o(e,t),a[t.scrollbarXActive?"add":"remove"](e,"ps-active-x"),a[t.scrollbarYActive?"add":"remove"](e,"ps-active-y")}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18}],20:[function(e,t,n){"use strict";var i=e("../lib/dom"),o=e("./instances"),a=e("./update-geometry");t.exports=function(e){var t=o.get(e);i.css(t.scrollbarXRail,"display","none"),i.css(t.scrollbarYRail,"display","none"),a(e),i.css(t.scrollbarXRail,"display","block"),i.css(t.scrollbarYRail,"display","block")}},{"../lib/dom":3,"./instances":18,"./update-geometry":19}]},{},[1]),define("WoltLabSuite/Core/Date/Util",["Language"],function(e){"use strict";return{formatDate:function(t){return this.format(t,e.get("wcf.date.dateFormat"))},formatTime:function(t){return this.format(t,e.get("wcf.date.timeFormat"))},formatDateTime:function(t){return this.format(t,e.get("wcf.date.dateTimeFormat").replace(/%date%/,e.get("wcf.date.dateFormat")).replace(/%time%/,e.get("wcf.date.timeFormat")))},format:function(t,n){var i,o="";"c"===n&&(n="Y-m-dTH:i:sP");for(var a=0,r=n.length;a<r;a++){switch(n[a]){case"s":i=("0"+t.getSeconds().toString()).slice(-2);break;case"i":i=t.getMinutes(),i<10&&(i="0"+i);break;case"a":i=t.getHours()>11?"pm":"am";break;case"g":i=t.getHours(),0===i?i=12:i>12&&(i-=12);break;case"h":i=t.getHours(),0===i?i=12:i>12&&(i-=12),i=("0"+i.toString()).slice(-2);break;case"A":i=t.getHours()>11?"PM":"AM";break;case"G":i=t.getHours();break;case"H":i=t.getHours(),i=("0"+i.toString()).slice(-2);break;case"d":i=t.getDate(),i=("0"+i.toString()).slice(-2);break;case"j":i=t.getDate();break;case"l":i=e.get("__days")[t.getDay()];break;case"D":i=e.get("__daysShort")[t.getDay()];break;case"S":i="";break;case"m":i=t.getMonth()+1,i=("0"+i.toString()).slice(-2);break;case"n":i=t.getMonth()+1;break;case"F":i=e.get("__months")[t.getMonth()];break;case"M":i=e.get("__monthsShort")[t.getMonth()];break;case"y":i=t.getYear().toString().replace(/^\d{2}/,"");break;case"Y":i=t.getFullYear();break;case"P":var s=t.getTimezoneOffset();i=s>0?"-":"+",s=Math.abs(s),i+=("0"+(~~(s/60)).toString()).slice(-2),i+=":",i+=("0"+(s%60).toString()).slice(-2);break;case"r":i=t.toString();break;case"U":i=Math.round(t.getTime()/1e3);break;case"\\":i="",a+1<r&&(i=n[++a]);break;default:i=n[a]}o+=i}return o},gmdate:function(e){return e instanceof Date||(e=new Date),Math.round(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDay(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds())/1e3)},getTimeElement:function(t){var n=elCreate("time");n.className="datetime";var i=this.formatDate(t),o=this.formatTime(t);return elAttr(n,"datetime",this.format(t,"c")),elData(n,"timestamp",(t.getTime()-t.getMilliseconds())/1e3),elData(n,"date",i),elData(n,"time",o),elData(n,"offset",60*t.getTimezoneOffset()),t.getTime()>Date.now()&&(elData(n,"is-future-date","true"),n.textContent=e.get("wcf.date.dateTimeFormat").replace("%time%",o).replace("%date%",i)),n},getTimezoneDate:function(e,t){var n=new Date(e),i=6e4*n.getTimezoneOffset();return new Date(e+i+t)}}}),define("WoltLabSuite/Core/Timer/Repeating",[],function(){"use strict";function e(e,t){if("function"!=typeof e)throw new TypeError("Expected a valid callback as first argument.");if(t<0||t>864e5)throw new RangeError("Invalid delta "+t+". Delta must be in the interval [0, 86400000].");this._callback=e.bind(void 0,this),this._delta=t,this._timer=void 0,this.restart()}return e.prototype={restart:function(){this.stop(),this._timer=setInterval(this._callback,this._delta)},stop:function(){void 0!==this._timer&&(clearInterval(this._timer),this._timer=void 0)},setDelta:function(e){this._delta=e,this.restart()}},e}),define("WoltLabSuite/Core/Date/Time/Relative",["Dom/ChangeListener","Language","WoltLabSuite/Core/Date/Util","WoltLabSuite/Core/Timer/Repeating"],function(e,t,n,i){"use strict";var o=elByTag("time"),a=!0,r=!1,s=null;return{setup:function(){new i(this._refresh.bind(this),6e4),e.add("WoltLabSuite/Core/Date/Time/Relative",this._refresh.bind(this)),document.addEventListener("visibilitychange",this._onVisibilityChange.bind(this))},_onVisibilityChange:function(){document.hidden?(a=!1,r=!1):(a=!0,r&&(this._refresh(),r=!1))},_refresh:function(){if(!a)return void(r||(r=!0));var e=new Date,i=(e.getTime()-e.getMilliseconds())/1e3;null===s&&(s=i-window.TIME_NOW);for(var l=0,c=o.length;l<c;l++){var u=o[l];if(u.classList.contains("datetime")&&!elData(u,"is-future-date")){var d=~~elData(u,"timestamp")+s,h=elData(u,"date"),f=elData(u,"time"),p=elData(u,"offset");if(elAttr(u,"title")||elAttr(u,"title",t.get("wcf.date.dateTimeFormat").replace(/%date%/,h).replace(/%time%/,f)),d>=i||i<d+60)u.textContent=t.get("wcf.date.relative.now");else if(i<d+3540){var g=Math.max(Math.round((i-d)/60),1);u.textContent=t.get("wcf.date.relative.minutes",{minutes:g})}else if(i<d+86400){var m=Math.round((i-d)/3600);u.textContent=t.get("wcf.date.relative.hours",{hours:m})}else if(i<d+518400){var v=new Date(e.getFullYear(),e.getMonth(),e.getDate()),b=Math.ceil((v/1e3-d)/86400),_=n.getTimezoneDate(1e3*d,1e3*p),y=_.getDay(),w=t.get("__days")[y];u.textContent=t.get("wcf.date.relative.pastDays",{days:b,day:w,time:f})}else u.textContent=t.get("wcf.date.shortDateTimeFormat").replace(/%date%/,h).replace(/%time%/,f)}}}}}),define("WoltLabSuite/Core/Ui/Page/Menu/Abstract",["Core","Environment","EventHandler","Language","ObjectMap","Dom/Traverse","Dom/Util","Ui/Screen"],function(e,t,n,i,o,a,r,s){"use strict";function l(e,t,n){this.init(e,t,n)}var c=elById("pageContainer"),u="";return l.prototype={init:function(e,i,a){if("packageInstallationSetup"!==elData(document.body,"template")){this._activeList=[],this._depth=0,this._enabled=!0,this._eventIdentifier=e,this._items=new o,this._menu=elById(i),this._removeActiveList=!1;var s=this.open.bind(this);this._button=elBySel(a),this._button.addEventListener(WCF_CLICK_EVENT,s),this._initItems(),this._initHeader(),n.add(this._eventIdentifier,"open",s),n.add(this._eventIdentifier,"close",this.close.bind(this)),n.add(this._eventIdentifier,"updateButtonState",this._updateButtonState.bind(this));var l,c=elByClass("menuOverlayItemList",this._menu);this._menu.addEventListener("animationend",function(){if(!this._menu.classList.contains("open"))for(var e=0,t=c.length;e<t;e++)l=c[e],l.classList.remove("active"),l.classList.remove("hidden")}.bind(this)),this._menu.children[0].addEventListener("transitionend",function(){if(this._menu.classList.add("allowScroll"),this._removeActiveList){this._removeActiveList=!1;var e=this._activeList.pop();e&&e.classList.remove("activeList")}}.bind(this));var u=elCreate("div");u.className="menuOverlayMobileBackdrop",u.addEventListener(WCF_CLICK_EVENT,this.close.bind(this)),r.insertAfter(u,this._menu),this._updateButtonState(),"android"===t.platform()&&this._initializeAndroid()}},open:function(e){return!!this._enabled&&(e instanceof Event&&e.preventDefault(),this._menu.classList.add("open"),this._menu.classList.add("allowScroll"),this._menu.children[0].classList.add("activeList"),s.scrollDisable(),c.classList.add("menuOverlay-"+this._menu.id),document.documentElement.classList.add("pageOverlayActive"),!0)},close:function(e){return e instanceof Event&&e.preventDefault(),!!this._menu.classList.contains("open")&&(this._menu.classList.remove("open"),s.scrollEnable(),c.classList.remove("menuOverlay-"+this._menu.id),document.documentElement.classList.remove("pageOverlayActive"),!0)},enable:function(){this._enabled=!0},disable:function(){this._enabled=!1,this.close(!0)},_initializeAndroid:function(){var t,n,i;switch(this._menu.id){case"pageUserMenuMobile":t="right";break;case"pageMainMenuMobile":t="left";break;default:return}n=this._menu.nextElementSibling,i=null,document.addEventListener("touchstart",function(n){var o,a,r,s;if(o=n.touches,a=this._menu.classList.contains("open"),"left"===t?(r=!a&&o[0].clientX<20,s=a&&Math.abs(this._menu.offsetWidth-o[0].clientX)<20):"right"===t&&(r=a&&Math.abs(document.body.clientWidth-this._menu.offsetWidth-o[0].clientX)<20,s=!a&&document.body.clientWidth-o[0].clientX<20),o.length>1)return void(u&&e.triggerEvent(document,"touchend"));if(!u&&(r||s)){if(document.documentElement.classList.contains("pageOverlayActive")){for(var l=!1,d=0;d<c.classList.length;d++)c.classList[d]==="menuOverlay-"+this._menu.id&&(l=!0);if(!l)return}document.documentElement.classList.contains("redactorActive")||(i={x:o[0].clientX,y:o[0].clientY},r&&(u="left"),s&&(u="right"))}}.bind(this)),document.addEventListener("touchend",function(e){if(u&&null!==i){if(!this._menu.classList.contains("open"))return i=null,void(u="");var o;o=e?e.changedTouches[0].clientX:i.x,this._menu.classList.add("androidMenuTouchEnd"),this._menu.style.removeProperty("transform"),n.style.removeProperty(t),this._menu.addEventListener("transitionend",function(){this._menu.classList.remove("androidMenuTouchEnd")}.bind(this),{once:!0}),"left"===t?("left"===u&&o<i.x+100&&this.close(),"right"===u&&o<i.x-100&&this.close()):"right"===t&&("left"===u&&o>i.x+100&&this.close(),"right"===u&&o>i.x-100&&this.close()),i=null,u=""}}.bind(this)),document.addEventListener("touchmove",function(e){if(u&&null!==i){var o=e.touches,a=!1,r=!1;"left"===u&&(a=o[0].clientX>i.x+5),"right"===u&&(a=o[0].clientX<i.x-5),r=Math.abs(o[0].clientY-i.y)>20;var s=this._menu.classList.contains("open");if(s||!a||r||(this.open(),s=!0),s){var l=o[0].clientX;"right"===t&&(l=document.body.clientWidth-l),l>this._menu.offsetWidth&&(l=this._menu.offsetWidth),l<0&&(l=0),this._menu.style.setProperty("transform","translateX("+("left"===t?1:-1)*(l-this._menu.offsetWidth)+"px)"),n.style.setProperty(t,Math.min(this._menu.offsetWidth,l)+"px")}}}.bind(this))},_initItems:function(){elBySelAll(".menuOverlayItemLink",this._menu,this._initItem.bind(this))},_initItem:function(e){var t=e.parentNode,i=elData(t,"more");if(i)return void e.addEventListener(WCF_CLICK_EVENT,function(o){o.preventDefault(),o.stopPropagation(),n.fire(this._eventIdentifier,"more",{handler:this,identifier:i,item:e,parent:t})}.bind(this));var o,r=e.nextElementSibling;if(null!==r)if("OL"!==r.nodeName&&r.classList.contains("menuOverlayItemLinkIcon"))for(o=elCreate("span"),o.className="menuOverlayItemWrapper",t.insertBefore(o,e),o.appendChild(e);o.nextElementSibling;)o.appendChild(o.nextElementSibling);else{var s="#"!==elAttr(e,"href"),l=t.parentNode,c=elData(r,"title");this._items.set(e,{itemList:r,parentItemList:l}),""===c&&(c=a.childByClass(e,"menuOverlayItemTitle").textContent,elData(r,"title",c));var u=this._showItemList.bind(this,e);if(s){o=elCreate("span"),o.className="menuOverlayItemWrapper",t.insertBefore(o,e),o.appendChild(e);var d=elCreate("a");elAttr(d,"href","#"),d.className="menuOverlayItemLinkIcon"+(e.classList.contains("active")?" active":""),d.innerHTML='<span class="icon icon24 fa-angle-right"></span>',d.addEventListener(WCF_CLICK_EVENT,u),o.appendChild(d)}else e.classList.add("menuOverlayItemLinkMore"),e.addEventListener(WCF_CLICK_EVENT,u);var h=elCreate("li");h.className="menuOverlayHeader",o=elCreate("span"),o.className="menuOverlayItemWrapper";var f=elCreate("a");elAttr(f,"href","#"),f.className="menuOverlayItemLink menuOverlayBackLink",f.textContent=elData(l,"title"),f.addEventListener(WCF_CLICK_EVENT,this._hideItemList.bind(this,e));var p=elCreate("a");if(elAttr(p,"href","#"),p.className="menuOverlayItemLinkIcon",p.innerHTML='<span class="icon icon24 fa-times"></span>',p.addEventListener(WCF_CLICK_EVENT,this.close.bind(this)),o.appendChild(f),o.appendChild(p),h.appendChild(o),r.insertBefore(h,r.firstElementChild),!h.nextElementSibling.classList.contains("menuOverlayTitle")){var g=elCreate("li");g.className="menuOverlayTitle";var m=elCreate("span");m.textContent=c,g.appendChild(m),r.insertBefore(g,h.nextElementSibling)}}},_initHeader:function(){var e=elCreate("li");e.className="menuOverlayHeader";var t=elCreate("span");t.className="menuOverlayItemWrapper",e.appendChild(t);var n=elCreate("span");n.className="menuOverlayLogoWrapper",t.appendChild(n);var i=elCreate("span");i.className="menuOverlayLogo",i.style.setProperty("background-image",'url("'+elData(this._menu,"page-logo")+'")',""),n.appendChild(i);var o=elCreate("a");elAttr(o,"href","#"),o.className="menuOverlayItemLinkIcon",o.innerHTML='<span class="icon icon24 fa-times"></span>',o.addEventListener(WCF_CLICK_EVENT,this.close.bind(this)),t.appendChild(o);var r=a.childByClass(this._menu,"menuOverlayItemList");r.insertBefore(e,r.firstElementChild)},_hideItemList:function(e,t){t instanceof Event&&t.preventDefault(),this._menu.classList.remove("allowScroll"),this._removeActiveList=!0,this._items.get(e).parentItemList.classList.remove("hidden"),this._updateDepth(!1)},_showItemList:function(e,t){t instanceof Event&&t.preventDefault();var i=this._items.get(e),o=elData(i.itemList,"load");if(o&&!elDataBool(e,"loaded")){var a=t.currentTarget.firstElementChild;return a.classList.contains("fa-angle-right")&&(a.classList.remove("fa-angle-right"),a.classList.add("fa-spinner")),void n.fire(this._eventIdentifier,"load_"+o)}this._menu.classList.remove("allowScroll"),i.itemList.classList.add("activeList"),i.parentItemList.classList.add("hidden"),this._activeList.push(i.itemList),this._updateDepth(!0)},_updateDepth:function(e){this._depth+=e?1:-1;var t=-100*this._depth;"rtl"===i.get("wcf.global.pageDirection")&&(t*=-1),this._menu.children[0].style.setProperty("transform","translateX("+t+"%)","")},_updateButtonState:function(){var e=!1;elBySelAll(".badgeUpdate",this._menu,function(t){~~t.textContent>0&&(e=!0)}),this._button.classList[e?"add":"remove"]("pageMenuMobileButtonHasContent")}},l}),define("WoltLabSuite/Core/Ui/Page/Menu/Main",["Core","Language","Dom/Traverse","./Abstract"],function(e,t,n,i){"use strict";function o(){this.init()}var a=null,r=null,s=null,l=null;return e.inherit(o,i,{init:function(){o._super.prototype.init.call(this,"com.woltlab.wcf.MainMenuMobile","pageMainMenuMobile","#pageHeader .mainMenu"),a=elById("pageMainMenuMobilePageOptionsContainer"),null!==a&&(s=n.childByClass(a,"menuOverlayItemList"),l=elBySel(".jsPageNavigationIcons"),elRemove(n.childByClass(s,"jsMenuOverlayItemPlaceholder")),s.addEventListener("click",function(e){e.target!==s&&null!==n.parentByClass(e.target,"menuOverlayItem",s)&&(this.close(),e.stopPropagation())}.bind(this))),elAttr(this._button,"aria-label",t.get("wcf.menu.page")),elAttr(this._button,"role","button")},open:function(e){if(!o._super.prototype.open.call(this,e))return!1;if(null===a)return!0;if(r=l.childElementCount>0){for(var t,n;l.childElementCount;)t=l.children[0],t.classList.add("menuOverlayItem"),n=t.children[0],n.classList.add("menuOverlayItemLink"),n.classList.add("box24"),n.children[1].classList.remove("invisible"),n.children[1].classList.add("menuOverlayItemTitle"),s.appendChild(t);elShow(a)}else elHide(a);return!0},close:function(e){if(!o._super.prototype.close.call(this,e))return!1;if(r){elHide(a);for(var t,i,c=n.childByClass(s,"menuOverlayTitle");t=c.nextElementSibling;)t.classList.remove("menuOverlayItem"),i=t.children[0],i.classList.remove("menuOverlayItemLink"),i.classList.remove("box24"),i.children[1].classList.add("invisible"),i.children[1].classList.remove("menuOverlayItemTitle"),l.appendChild(t)}return!0}}),o}),define("WoltLabSuite/Core/Ui/Page/Menu/User",["Core","EventHandler","Language","./Abstract"],function(e,t,n,i){"use strict";function o(){this.init()}return e.inherit(o,i,{init:function(){var e=elBySel("#pageUserMenuMobile > .menuOverlayItemList");if(1===e.childElementCount&&e.children[0].classList.contains("menuOverlayTitle"))return void elBySel("#pageHeader .userPanel").classList.add("hideUserPanel");o._super.prototype.init.call(this,"com.woltlab.wcf.UserMenuMobile","pageUserMenuMobile","#pageHeader .userPanel"),t.add("com.woltlab.wcf.userMenu","updateBadge",function(e){elBySelAll(".menuOverlayItemBadge",this._menu,function(t){if(elData(t,"badge-identifier")===e.identifier){var n=elBySel(".badge",t);e.count?(null===n&&(n=elCreate("span"),n.className="badge badgeUpdate",t.appendChild(n)),n.textContent=e.count):null!==n&&elRemove(n),this._updateButtonState()}}.bind(this))}.bind(this)),elAttr(this._button,"aria-label",n.get("wcf.menu.user")),elAttr(this._button,"role","button")},close:function(e){var t=WCF.Dropdown.Interactive.Handler.getOpenDropdown();t?(e.preventDefault(),e.stopPropagation(),t.close()):o._super.prototype.close.call(this,e)}}),o}),define("WoltLabSuite/Core/Ui/Dropdown/Reusable",["Dictionary","Ui/SimpleDropdown"],function(e,t){"use strict";function n(e){if(!i.has(e))throw new Error("Unknown dropdown identifier '"+e+"'");return i.get(e)}var i=new e,o=0;return{init:function(e,n){if(!i.has(e)){var a=elCreate("div");a.id="reusableDropdownGhost"+o++,t.initFragment(a,n),i.set(e,a.id)}},getDropdownMenu:function(e){return t.getDropdownMenu(n(e))},registerCallback:function(e,i){t.registerCallback(n(e),i)},toggleDropdown:function(e,i){t.toggleDropdown(n(e),i)}}}),define("WoltLabSuite/Core/Ui/Mobile",["Core","Environment","EventHandler","Language","List","Dom/ChangeListener","Dom/Traverse","Ui/Alignment","Ui/CloseOverlay","Ui/Screen","./Page/Menu/Main","./Page/Menu/User","WoltLabSuite/Core/Ui/Dropdown/Reusable"],function(e,t,n,i,o,a,r,s,l,c,u,d,h){"use strict"
-;var f=elByClass("buttonGroupNavigation"),p=null,g=null,m=null,v=!1,b=new o,_=null,y=elByClass("message"),w={},C=null,L=null,S=null,E=[],x=!1;return{setup:function(n){w=e.extend({enableMobileMenu:!0},n),_=elById("main"),elBySelAll(".sidebar",void 0,function(e){E.push(e)}),t.touch()&&document.documentElement.classList.add("touch"),"desktop"!==t.platform()&&document.documentElement.classList.add("mobile");var i=elBySel(".messageGroupList");i&&(S=elByClass("messageGroup",i)),c.on("screen-md-down",{match:this.enable.bind(this),unmatch:this.disable.bind(this),setup:this._init.bind(this)}),c.on("screen-sm-down",{match:this.enableShadow.bind(this),unmatch:this.disableShadow.bind(this),setup:this.enableShadow.bind(this)}),c.on("screen-xs",{match:this._enableSidebarXS.bind(this),unmatch:this._disableSidebarXS.bind(this),setup:this._setupSidebarXS.bind(this)})},enable:function(){v=!0,w.enableMobileMenu&&(C.enable(),L.enable())},enableShadow:function(){S&&this.rebuildShadow(S,".messageGroupLink")},disable:function(){v=!1,w.enableMobileMenu&&(C.disable(),L.disable())},disableShadow:function(){S&&this.removeShadow(S),g&&p()},_init:function(){v=!0,this._initSearchBar(),this._initButtonGroupNavigation(),this._initMessages(),this._initMobileMenu(),l.add("WoltLabSuite/Core/Ui/Mobile",this._closeAllMenus.bind(this)),a.add("WoltLabSuite/Core/Ui/Mobile",function(){this._initButtonGroupNavigation(),this._initMessages()}.bind(this))},_initSearchBar:function(){var e=elById("pageHeaderSearch"),i=elById("pageHeaderSearchInput"),o=null;n.add("com.woltlab.wcf.MainMenuMobile","more",function(n){"com.woltlab.wcf.search"===n.identifier&&(n.handler.close(!0),"ios"===t.platform()&&(o=document.body.scrollTop,c.scrollDisable()),e.style.setProperty("top",elById("pageHeader").offsetHeight+"px",""),e.classList.add("open"),i.focus(),"ios"===t.platform()&&(document.body.scrollTop=0))}),_.addEventListener(WCF_CLICK_EVENT,function(){e&&e.classList.remove("open"),"ios"===t.platform()&&null!==o&&(c.scrollEnable(),document.body.scrollTop=o,o=null)})},_initButtonGroupNavigation:function(){for(var e=0,t=f.length;e<t;e++){var n=f[e];if(!n.classList.contains("jsMobileButtonGroupNavigation")){n.classList.add("jsMobileButtonGroupNavigation");var i=elBySel(".buttonList",n);if(0!==i.childElementCount){n.parentNode.classList.add("hasMobileNavigation");var o=elCreate("a");o.className="dropdownLabel";var a=elCreate("span");a.className="icon icon24 fa-ellipsis-v",o.appendChild(a),function(e,t,n){t.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),t.stopPropagation(),e.classList.toggle("open")}),n.addEventListener(WCF_CLICK_EVENT,function(t){t.stopPropagation(),e.classList.remove("open")})}(n,o,i),n.insertBefore(o,n.firstChild)}}}},_initMessages:function(){Array.prototype.forEach.call(y,function(e){if(!b.has(e)){var t=elBySel(".jsMobileNavigation",e);if(t){t.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation(),window.setTimeout(function(){t.classList.remove("open")},10)});var n=elBySel(".messageQuickOptions",e);n&&t.childElementCount&&(n.classList.add("active"),n.addEventListener(WCF_CLICK_EVENT,function(i){v&&"LABEL"!==i.target.nodeName&&"INPUT"!==i.target.nodeName&&(i.preventDefault(),i.stopPropagation(),this._toggleMobileNavigation(e,n,t))}.bind(this)))}b.add(e)}}.bind(this))},_initMobileMenu:function(){w.enableMobileMenu&&(C=new u,L=new d)},_closeAllMenus:function(){elBySelAll(".jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open",null,function(e){e.classList.remove("open")}),v&&g&&p()},rebuildShadow:function(e,t){for(var n,i,o,a=0,s=e.length;a<s;a++)n=e[a],i=n.parentNode,null===(o=r.childByClass(i,"mobileLinkShadow"))&&elBySel(t,n).href&&(o=elCreate("a"),o.className="mobileLinkShadow",o.href=elBySel(t,n).href,i.appendChild(o),i.classList.add("mobileLinkShadowContainer"))},removeShadow:function(e){for(var t,n,i,o=0,a=e.length;o<a;o++)t=e[o],n=t.parentNode,n.classList.contains("mobileLinkShadowContainer")&&(i=r.childByClass(n,"mobileLinkShadow"),null!==i&&elRemove(i),n.classList.remove("mobileLinkShadowContainer"))},_enableSidebarXS:function(){x=!0},_disableSidebarXS:function(){x=!1,E.forEach(function(e){e.classList.remove("open")})},_setupSidebarXS:function(){E.forEach(function(e){e.addEventListener("mousedown",function(t){x&&t.target===e&&(t.preventDefault(),e.classList.toggle("open"))})}),x=!0},_toggleMobileNavigation:function(e,t,n){if(null===g)g=elCreate("ul"),g.className="dropdownMenu",h.init("com.woltlab.wcf.jsMobileNavigation",g),p=function(){g.classList.remove("dropdownOpen")};else if(g.classList.contains("dropdownOpen")&&(p(),m===e))return;g.innerHTML="",l.execute(),this._rebuildMobileNavigation(n);var i=n.previousElementSibling;if(i&&i.classList.contains("messageFooterButtonsExtra")){var o=elCreate("li");o.className="dropdownDivider",g.appendChild(o),this._rebuildMobileNavigation(i)}s.set(g,t,{horizontal:"right",allowFlip:"vertical"}),g.classList.add("dropdownOpen"),m=e},_rebuildMobileNavigation:function(t){elBySelAll(".button",t,function(t){var n=elCreate("li");t.classList.contains("active")&&(n.className="active"),n.innerHTML='<a href="#">'+elBySel("span:not(.icon)",t).textContent+"</a>",n.children[0].addEventListener(WCF_CLICK_EVENT,function(n){n.preventDefault(),n.stopPropagation(),"A"===t.nodeName?t.click():e.triggerEvent(t,WCF_CLICK_EVENT),p()}),g.appendChild(n)})}}}),define("WoltLabSuite/Core/Ui/TabMenu/Simple",["Dictionary","EventHandler","Dom/Traverse","Dom/Util"],function(e,t,n,i){"use strict";function o(t){this._container=t,this._containers=new e,this._isLegacy=null,this._store=null,this._tabs=new e}return o.prototype={validate:function(){if(!this._container.classList.contains("tabMenuContainer"))return!1;var e=n.childByTag(this._container,"NAV");if(null===e)return!1;var t=elByTag("li",e);if(0===t.length)return!1;var o,a,r,s,l=n.childrenByTag(this._container,"DIV");for(r=0,s=l.length;r<s;r++)o=l[r],a=elData(o,"name"),a||(a=i.identify(o)),elData(o,"name",a),this._containers.set(a,o);var c,u=this._container.id;for(r=0,s=t.length;r<s;r++)if(c=t[r],a=this._getTabName(c)){if(this._tabs.has(a))throw new Error("Tab names must be unique, li[data-name='"+a+"'] (tab menu id: '"+u+"') exists more than once.");if(void 0===(o=this._containers.get(a)))throw new Error("Expected content element for li[data-name='"+a+"'] (tab menu id: '"+u+"').");if(o.parentNode!==this._container)throw new Error("Expected content element '"+a+"' (tab menu id: '"+u+"') to be a direct children.");if(1!==c.childElementCount||"A"!==c.children[0].nodeName)throw new Error("Expected exactly one <a> as children for li[data-name='"+a+"'] (tab menu id: '"+u+"').");this._tabs.set(a,c)}if(!this._tabs.size)throw new Error("Expected at least one tab (tab menu id: '"+u+"').");return this._isLegacy&&(elData(this._container,"is-legacy",!0),this._tabs.forEach(function(e,t){elAttr(e,"aria-controls",t)})),!0},init:function(e){e=e||null,this._tabs.forEach(function(t){e&&e.get(elData(t,"name"))===t||t.children[0].addEventListener(WCF_CLICK_EVENT,this._onClick.bind(this))}.bind(this));var t=null;if(!e){var n=o.getIdentifierFromHash(),i=null;if(""!==n&&(i=this._tabs.get(n))&&this._container.parentNode.classList.contains("tabMenuContainer")&&(t=this._container),!i){var a=elData(this._container,"preselect")||elData(this._container,"active");"true"!==a&&a||(a=!0),!0===a?this._tabs.forEach(function(e){i||e.previousElementSibling||(i=e)}):"false"!==a&&(i=this._tabs.get(a))}i&&(this._containers.forEach(function(e){e.classList.add("hidden")}),this.select(null,i,!0));var r=elData(this._container,"store");if(r){var s=elCreate("input");s.type="hidden",s.name=r,s.value=elData(this.getActiveTab(),"name"),this._container.appendChild(s),this._store=s}}return t},select:function(e,n,i){if(!(n=n||this._tabs.get(e))){if(~~e==e){e=~~e;var a=0;this._tabs.forEach(function(t){a===e&&(n=t),a++})}if(!n)throw new Error("Expected a valid tab name, '"+e+"' given (tab menu id: '"+this._container.id+"').")}e=e||elData(n,"name");var r=this.getActiveTab(),s=null;if(r){var l=elData(r,"name");if(l===e)return;i||t.fire("com.woltlab.wcf.simpleTabMenu_"+this._container.id,"beforeSelect",{tab:r,tabName:l}),r.classList.remove("active"),s=this._containers.get(elData(r,"name")),s.classList.remove("active"),s.classList.add("hidden"),this._isLegacy&&(r.classList.remove("ui-state-active"),s.classList.remove("ui-state-active"))}n.classList.add("active");var c=this._containers.get(e);if(c.classList.add("active"),c.classList.remove("hidden"),this._isLegacy&&(n.classList.add("ui-state-active"),c.classList.add("ui-state-active")),this._store&&(this._store.value=e),!i){t.fire("com.woltlab.wcf.simpleTabMenu_"+this._container.id,"select",{active:n,activeName:e,previous:r,previousName:r?elData(r,"name"):null});var u=this._isLegacy&&"function"==typeof window.jQuery?window.jQuery:null;u&&u(this._container).trigger("wcftabsbeforeactivate",{newTab:u(n),oldTab:u(r),newPanel:u(c),oldPanel:u(s)});var d=window.location.href.replace(/#+[^#]*$/,"");o.getIdentifierFromHash()===e?d+=window.location.hash:d+="#"+e,window.history.replaceState(void 0,void 0,d)}require(["WoltLabSuite/Core/Ui/TabMenu"],function(e){e.scrollToTab(n)})},rebuild:function(){var t=new e;t.merge(this._tabs),this.validate(),this.init(t)},hasTab:function(e){return this._tabs.has(e)},_onClick:function(e){e.preventDefault(),this.select(null,e.currentTarget.parentNode)},_getTabName:function(e){var t=elData(e,"name");return t||1===e.childElementCount&&"A"===e.children[0].nodeName&&e.children[0].href.match(/#([^#]+)$/)&&(t=RegExp.$1,null===elById(t)?t=null:(this._isLegacy=!0,elData(e,"name",t))),t},getActiveTab:function(){return elBySel("#"+this._container.id+" > nav > ul > li.active")},getContainers:function(){return this._containers},getTabs:function(){return this._tabs}},o.getIdentifierFromHash=function(){return window.location.hash.match(/^#+([^\/]+)+(?:\/.+)?/)?RegExp.$1:""},o}),define("WoltLabSuite/Core/Ui/TabMenu",["Dictionary","EventHandler","Dom/ChangeListener","Dom/Util","Ui/CloseOverlay","Ui/Screen","./TabMenu/Simple"],function(e,t,n,i,o,a,r){"use strict";var s=null,l=!1,c=new e;return{setup:function(){this._init(),this._selectErroneousTabs(),n.add("WoltLabSuite/Core/Ui/TabMenu",this._init.bind(this)),o.add("WoltLabSuite/Core/Ui/TabMenu",function(){s&&(s.classList.remove("active"),s=null)}),a.on("screen-sm-down",{enable:this._scrollEnable.bind(this,!1),disable:this._scrollDisable.bind(this),setup:this._scrollEnable.bind(this,!0)}),window.addEventListener("hashchange",function(){var e=r.getIdentifierFromHash(),t=e?elById(e):null;null!==t&&t.classList.contains("tabMenuContent")&&c.forEach(function(t){t.hasTab(e)&&t.select(e)})});var e=r.getIdentifierFromHash();e&&window.setTimeout(function(){var t=elById(e);if(t&&t.classList.contains("tabMenuContent")){var n=window.scrollY||window.pageYOffset;if(n>0){var o=t.parentNode,a=o.offsetTop-50;if(a<0&&(a=0),n>a){var r=i.offset(o).top;r<=50?r=0:r-=50,window.scrollTo(0,r)}}}},100)},_init:function(){for(var e,t,n,o,a,l=elBySelAll(".tabMenuContainer:not(.staticTabMenuContainer)"),u=0,d=l.length;u<d;u++)e=l[u],t=i.identify(e),c.has(t)||(a=new r(e),a.validate()&&(o=a.init(),c.set(t,a),o instanceof Element&&(a=this.getTabMenu(o.parentNode.id),a.select(o.id,null,!0)),n=elBySel("#"+t+" > nav > ul"),function(e){e.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),t.stopPropagation(),t.target===e?(e.classList.add("active"),s=e):(e.classList.remove("active"),s=null)})}(n),elBySelAll(".tabMenu, .menu",e,function(e){var t=this._rebuildMenuOverflow.bind(this,e),n=null;elBySel("ul",e).addEventListener("scroll",function(){null!==n&&window.clearTimeout(n),n=window.setTimeout(t,10)})}.bind(this))))},_selectErroneousTabs:function(){c.forEach(function(e){var t=!1;e.getContainers().forEach(function(n){!t&&elByClass("formError",n).length&&(t=!0,e.select(n.id))})})},getTabMenu:function(e){return c.get(e)},_scrollEnable:function(e){l=!0,c.forEach(function(t){var n=t.getActiveTab();e?this._rebuildMenuOverflow(n.closest(".menu, .tabMenu")):this.scrollToTab(n)}.bind(this))},_scrollDisable:function(){l=!1},scrollToTab:function(e){if(l){var t=e.closest("ul"),n=t.clientWidth,i=t.scrollLeft,o=t.scrollWidth;if(n!==o){var a=e.offsetLeft,r=!1;a<i&&(r=!0);var s=!1;if(!r){var c=n-(a-i),u=e.clientWidth;null!==e.nextElementSibling&&(s=!0,u+=20),c<u&&(r=!0)}r&&this._scrollMenu(t,a,i,o,n,s)}}},_scrollMenu:function(e,t,n,i,o,a){a?t-=15:t>0&&(t-=15),t=t<0?0:Math.min(t,i-o),n!==t&&(e.classList.add("enableAnimation"),n<t?e.firstElementChild.style.setProperty("margin-left",n-t+"px",""):e.style.setProperty("padding-left",n-t+"px",""),setTimeout(function(){e.classList.remove("enableAnimation"),e.firstElementChild.style.removeProperty("margin-left"),e.style.removeProperty("padding-left"),e.scrollLeft=t},300))},_rebuildMenuOverflow:function(e){if(l){var t=e.clientWidth,n=elBySel("ul",e),i=n.scrollLeft,o=n.scrollWidth,a=i>0,r=elBySel(".tabMenuOverlayLeft",e);a?(null===r&&(r=elCreate("span"),r.className="tabMenuOverlayLeft icon icon24 fa-angle-left",r.addEventListener(WCF_CLICK_EVENT,function(){var e=n.clientWidth;this._scrollMenu(n,n.scrollLeft-~~(e/2),n.scrollLeft,n.scrollWidth,e,0)}.bind(this)),e.insertBefore(r,e.firstChild)),r.classList.add("active")):null!==r&&r.classList.remove("active");var s=t+i<o,c=elBySel(".tabMenuOverlayRight",e);s?(null===c&&(c=elCreate("span"),c.className="tabMenuOverlayRight icon icon24 fa-angle-right",c.addEventListener(WCF_CLICK_EVENT,function(){var e=n.clientWidth;this._scrollMenu(n,n.scrollLeft+~~(e/2),n.scrollLeft,n.scrollWidth,e,0)}.bind(this)),e.appendChild(c)),c.classList.add("active")):null!==c&&c.classList.remove("active")}}}}),define("WoltLabSuite/Core/Ui/FlexibleMenu",["Core","Dictionary","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/SimpleDropdown"],function(e,t,n,i,o,a){"use strict";var r=new t,s=new t,l=new t,c=new t;return{setup:function(){null!==elById("mainMenu")&&this.register("mainMenu");var e=elBySel(".navigationHeader");null!==e&&this.register(o.identify(e)),window.addEventListener("resize",this.rebuildAll.bind(this)),n.add("WoltLabSuite/Core/Ui/FlexibleMenu",this.registerTabMenus.bind(this))},register:function(e){var t=elById(e);if(null===t)throw"Expected a valid element id, '"+e+"' does not exist.";if(!r.has(e)){var n=i.childByTag(t,"UL");if(null===n)throw"Expected an <ul> element as child of container '"+e+"'.";r.set(e,t),c.set(e,n),this.rebuild(e)}},registerTabMenus:function(){for(var e=elBySelAll(".tabMenuContainer:not(.jsFlexibleMenuEnabled), .messageTabMenu:not(.jsFlexibleMenuEnabled)"),t=0,n=e.length;t<n;t++){var a=e[t],r=i.childByTag(a,"NAV");null!==r&&(a.classList.add("jsFlexibleMenuEnabled"),this.register(o.identify(r)))}},rebuildAll:function(){r.forEach(function(e,t){this.rebuild(t)}.bind(this))},rebuild:function(t){var n=r.get(t);if(void 0===n)throw"Expected a valid element id, '"+t+"' is unknown.";var u=window.getComputedStyle(n),d=n.parentNode.clientWidth;d-=o.styleAsInt(u,"margin-left"),d-=o.styleAsInt(u,"margin-right");var h=c.get(t),f=i.childrenByTag(h,"LI"),p=s.get(t),g=0;if(void 0!==p){for(var m=0,v=f.length;m<v;m++){var b=f[m];b.classList.contains("dropdown")||elShow(b)}null!==p.parentNode&&(g=o.outerWidth(p))}var _=h.scrollWidth-g,y=[];if(_>d)for(var m=f.length-1;m>=0;m--){var b=f[m];if(!(b.classList.contains("dropdown")||b.classList.contains("active")||b.classList.contains("ui-state-active"))&&(y.push(b),elHide(b),h.scrollWidth<d))break}if(y.length){var w;if(void 0===p){p=elCreate("li"),p.className="dropdown jsFlexibleMenuDropdown";var C=elCreate("a");C.className="icon icon16 fa-list",p.appendChild(C),w=elCreate("ul"),w.classList.add("dropdownMenu"),p.appendChild(w),s.set(t,p),l.set(t,w),a.init(C)}else w=l.get(t);null===p.parentNode&&h.appendChild(p);var L=document.createDocumentFragment(),S=this;y.forEach(function(n){var i=elCreate("li");i.innerHTML=n.innerHTML,i.addEventListener(WCF_CLICK_EVENT,function(i){i.preventDefault(),e.triggerEvent(elBySel("a",n),WCF_CLICK_EVENT),setTimeout(function(){S.rebuild(t)},59)}.bind(this)),L.appendChild(i)}),w.innerHTML="",w.appendChild(L)}else void 0!==p&&null!==p.parentNode&&elRemove(p)}}}),define("WoltLabSuite/Core/Ui/Tooltip",["Environment","Dom/ChangeListener","Ui/Alignment"],function(e,t,n){"use strict";var i=null,o=null,a=null,r=null,s=null,l=null;return{setup:function(){"desktop"===e.platform()&&(l=elCreate("div"),elAttr(l,"id","balloonTooltip"),l.classList.add("balloonTooltip"),l.addEventListener("transitionend",function(){l.classList.contains("active")||["bottom","left","right","top"].forEach(function(e){l.style.removeProperty(e)})}),s=elCreate("span"),elAttr(s,"id","balloonTooltipText"),l.appendChild(s),r=elCreate("span"),r.classList.add("elementPointer"),r.appendChild(elCreate("span")),l.appendChild(r),document.body.appendChild(l),a=elByClass("jsTooltip"),i=this._mouseEnter.bind(this),o=this._mouseLeave.bind(this),this.init(),t.add("WoltLabSuite/Core/Ui/Tooltip",this.init.bind(this)),window.addEventListener("scroll",this._mouseLeave.bind(this)))},init:function(){0!==a.length&&elBySelAll(".jsTooltip",void 0,function(e){e.classList.remove("jsTooltip");var t=elAttr(e,"title").trim();t.length&&(elData(e,"tooltip",t),e.removeAttribute("title"),e.addEventListener("mouseenter",i),e.addEventListener("mouseleave",o),e.addEventListener(WCF_CLICK_EVENT,o))})},_mouseEnter:function(e){var t=e.currentTarget,i=elAttr(t,"title");if(i="string"==typeof i?i.trim():"",""!==i&&(elData(t,"tooltip",i),t.removeAttribute("title")),i=elData(t,"tooltip"),l.style.removeProperty("top"),l.style.removeProperty("left"),!i.length)return void l.classList.remove("active");l.classList.add("active"),s.textContent=i,n.set(l,t,{horizontal:"center",verticalOffset:4,pointer:!0,pointerClassNames:["inverse"],vertical:"top"})},_mouseLeave:function(){l.classList.remove("active")}}}),define("WoltLabSuite/Core/Date/Picker",["DateUtil","Language","ObjectMap","Dom/ChangeListener","Ui/Alignment","WoltLabSuite/Core/Ui/CloseOverlay"],function(e,t,n,i,o,a){"use strict";var r=!1,s=0,l=new n,c=null,u=0,d=0,h=[],f=null,p=null,g=null,m=null,v=null,b=null,_=null,y=null,w=null,C=null,L={init:function(){this._setup();for(var t=elBySelAll('input[type="date"]:not(.inputDatePicker), input[type="datetime"]:not(.inputDatePicker)'),n=new Date,i=0,o=t.length;i<o;i++){var a=t[i];a.classList.add("inputDatePicker"),a.readOnly=!0;var r="datetime"===elAttr(a,"type"),s=r&&elDataBool(a,"time-only"),c=elDataBool(a,"disable-clear"),u=r&&elDataBool(a,"ignore-timezone"),d=a.classList.contains("birthday");elData(a,"is-date-time",r),elData(a,"is-time-only",s);var h=null,f=elAttr(a,"value"),p=/^\d+-\d+-\d+$/.test(f);if(elAttr(a,"value")){if(s){h=new Date;var g=f.split(":");h.setHours(g[0],g[1])}else{if(u||d||p){var m=new Date(f).getTimezoneOffset(),v=m>0?"-":"+";m=Math.abs(m);var b=Math.floor(m/60).toString(),_=(m%60).toString();v+=2===b.length?b:"0"+b,v+=":",v+=2===_.length?_:"0"+_,d||p?f+="T00:00:00"+v:f=f.replace(/[+-][0-9]{2}:[0-9]{2}$/,v)}h=new Date(f)}var y=h.getTime();if(isNaN(y))f="";else{elData(a,"value",y);f=e[s?"formatTime":"formatDate"+(r?"Time":"")](h)}}var w=0===f.length;if(d?(elData(a,"min-date","120"),elData(a,"max-date",(new Date).getFullYear()+"-12-31")):(a.min&&elData(a,"min-date",a.min),a.max&&elData(a,"max-date",a.max)),this._initDateRange(a,n,!0),this._initDateRange(a,n,!1),elData(a,"min-date")===elData(a,"max-date"))throw new Error("Minimum and maximum date cannot be the same (element id '"+a.id+"').");a.type="text",a.value=f,elData(a,"empty",w),elData(a,"placeholder")&&elAttr(a,"placeholder",elData(a,"placeholder"));var L=elCreate("input");L.id=a.id+"DatePicker",L.name=a.name,L.type="hidden",null!==h&&(L.value=s?e.format(h,"H:i"):u?e.format(h,"Y-m-dTH:i:s"):e.format(h,r?"c":"Y-m-d")),a.parentNode.insertBefore(L,a),a.removeAttribute("name"),a.addEventListener(WCF_CLICK_EVENT,C);var S=elCreate("div");S.className="inputAddon";var E=elCreate("a");E.className="inputSuffix button",E.addEventListener(WCF_CLICK_EVENT,C),S.appendChild(E);var x=elCreate("span");x.className="icon icon16 fa-calendar",E.appendChild(x),a.parentNode.insertBefore(S,a),S.insertBefore(a,E),c||(E=elCreate("a"),E.className="inputSuffix button",E.addEventListener(WCF_CLICK_EVENT,this.clear.bind(this,a)),w&&E.style.setProperty("visibility","hidden",""),S.appendChild(E),x=elCreate("span"),x.className="icon icon16 fa-times",E.appendChild(x));for(var D=!1,T=["tiny","short","medium","long"],k=0;k<4;k++)a.classList.contains(T[k])&&(D=!0);D||a.classList.add("short"),l.set(a,{clearButton:E,shadow:L,disableClear:c,isDateTime:r,isEmpty:w,isTimeOnly:s,ignoreTimezone:u,onClose:null})}},_initDateRange:function(e,t,n){var i="data-"+(n?"min":"max")+"-date",o=e.hasAttribute(i)?elAttr(e,i).trim():"";if(o.match(/^(\d{4})-(\d{2})-(\d{2})$/))o=new Date(o).getTime();else if("now"===o)o=t.getTime();else if(o.match(/^\d{1,3}$/)){var a=new Date(t.getTime());a.setFullYear(a.getFullYear()+~~o*(n?-1:1)),o=a.getTime()}else if(o.match(/^datePicker-(.+)$/)){if(o=RegExp.$1,null===elById(o))throw new Error("Reference date picker identified by '"+o+"' does not exists (element id: '"+e.id+"').")}else o=/^\d{4}\-\d{2}\-\d{2}T/.test(o)?new Date(o).getTime():new Date(n?1970:2038,0,1).getTime();elAttr(e,i,o)},_setup:function(){r||(r=!0,s=~~t.get("wcf.date.firstDayOfTheWeek"),C=this._open.bind(this),i.add("WoltLabSuite/Core/Date/Picker",this.init.bind(this)),a.add("WoltLabSuite/Core/Date/Picker",this._close.bind(this)))},_open:function(e){e.preventDefault(),e.stopPropagation(),this._createPicker();var t="INPUT"===e.currentTarget.nodeName?e.currentTarget:e.currentTarget.previousElementSibling;if(t!==c){c=t;var n,i=l.get(c),a=elData(c,"value");a?(n=new Date(+a),"Invalid Date"===n.toString()&&(n=new Date)):n=new Date,d=elData(c,"min-date"),d.match(/^datePicker-(.+)$/)&&(d=elData(elById(RegExp.$1),"value")),d=new Date(+d),u=elData(c,"max-date"),u.match(/^datePicker-(.+)$/)&&(u=elData(elById(RegExp.$1),"value")),u=new Date(+u),i.isDateTime?(p.value=n.getHours(),g.value=n.getMinutes(),w.classList.add("datePickerTime")):w.classList.remove("datePickerTime"),w.classList[i.isTimeOnly?"add":"remove"]("datePickerTimeOnly"),this._renderPicker(n.getDate(),n.getMonth(),n.getFullYear()),o.set(w,c)}},_close:function(){if(null!==w&&w.classList.contains("active")){w.classList.remove("active");var e=l.get(c);"function"==typeof e.onClose&&e.onClose(),c=null,d=0,u=0}},_renderPicker:function(e,t,n){this._renderGrid(e,t,n);for(var i="",o=d.getFullYear(),a=u.getFullYear();o<=a;o++)i+='<option value="'+o+'">'+o+"</option>";y.innerHTML=i,y.value=n,m.value=t,w.classList.add("active")},_renderGrid:function(e,t,n){var i,o,a=void 0!==e,r=void 0!==t;if(e=~~e||~~elData(f,"day"),t=~~t,n=~~n,r||n){var l=0!==n,c=document.createDocumentFragment();c.appendChild(f),r||(t=~~elData(f,"month")),n=n||~~elData(f,"year");var p=new Date(n+"-"+("0"+(t+1).toString()).slice(-2)+"-"+("0"+e.toString()).slice(-2));for(p<d?(n=d.getFullYear(),t=d.getMonth(),e=d.getDate(),m.value=t,y.value=n,l=!0):p>u&&(n=u.getFullYear(),t=u.getMonth(),e=u.getDate(),m.value=t,y.value=n,l=!0),p=new Date(n+"-"+("0"+(t+1).toString()).slice(-2)+"-01");p.getDay()!==s;)p.setDate(p.getDate()-1);elShow(h[35].parentNode);var g,C=new Date(d.getFullYear(),d.getMonth(),d.getDate());for(o=0;o<42;o++){if(35===o&&p.getMonth()!==t){elHide(h[35].parentNode);break}i=h[o],i.textContent=p.getDate(),g=p.getMonth()===t,g&&(p<C?g=!1:p>u&&(g=!1)),i.classList[g?"remove":"add"]("otherMonth"),p.setDate(p.getDate()+1)}if(elData(f,"month",t),elData(f,"year",n),w.insertBefore(c,_),!a&&(p=new Date(n,t,e),p.getDate()!==e)){for(;p.getMonth()!==t;)p.setDate(p.getDate()-1);e=p.getDate()}if(l){for(o=0;o<12;o++){var L=m.children[o];L.disabled=n===d.getFullYear()&&L.value<d.getMonth()||n===u.getFullYear()&&L.value>u.getMonth()}var S=new Date(n+"-"+("0"+(t+1).toString()).slice(-2)+"-01");S.setMonth(S.getMonth()+1),v.classList[S<u?"add":"remove"]("active");var E=new Date(n+"-"+("0"+(t+1).toString()).slice(-2)+"-01");E.setDate(E.getDate()-1),b.classList[E>d?"add":"remove"]("active")}}if(e){for(o=0;o<35;o++)i=h[o],i.classList[i.classList.contains("otherMonth")||~~i.textContent!==e?"remove":"add"]("active");elData(f,"day",e)}this._formatValue()},_formatValue:function(){var t,n,i,o=l.get(c);"true"!==elData(c,"empty")&&(o.isDateTime?(t=new Date(elData(f,"year"),elData(f,"month"),elData(f,"day"),p.value,g.value),o.isTimeOnly?(n=e.formatTime(t),i=e.format(t,"H:i")):o.ignoreTimezone?(n=e.formatDateTime(t),i=e.format(t,"Y-m-dTH:i:s")):(n=e.formatDateTime(t),i=e.format(t,"c"))):(t=new Date(elData(f,"year"),elData(f,"month"),elData(f,"day")),n=e.formatDate(t),i=e.format(t,"Y-m-d")),c.value=n,elData(c,"value",t.getTime()),o.disableClear||o.clearButton.style.removeProperty("visibility"),o.shadow.value=i)},_createPicker:function(){if(null===w){w=elCreate("div"),w.className="datePicker",w.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation()});var n=elCreate("header");w.appendChild(n),b=elCreate("a"),b.className="previous",b.innerHTML='<span class="icon icon16 fa-arrow-left"></span>',b.addEventListener(WCF_CLICK_EVENT,this.previousMonth.bind(this)),n.appendChild(b);var i=elCreate("span");n.appendChild(i),m=elCreate("select"),m.className="month",m.addEventListener("change",this._changeMonth.bind(this)),i.appendChild(m);var o,a="",r=t.get("__monthsShort");for(o=0;o<12;o++)a+='<option value="'+o+'">'+r[o]+"</option>";m.innerHTML=a,y=elCreate("select"),y.className="year",y.addEventListener("change",this._changeYear.bind(this)),i.appendChild(y),v=elCreate("a"),v.className="next",v.innerHTML='<span class="icon icon16 fa-arrow-right"></span>',v.addEventListener(WCF_CLICK_EVENT,this.nextMonth.bind(this)),n.appendChild(v),f=elCreate("ul"),w.appendChild(f);var l=elCreate("li");l.className="weekdays",f.appendChild(l);var c,u=t.get("__daysShort");for(o=0;o<7;o++){var d=o+s;d>6&&(d-=7),c=elCreate("span"),c.textContent=u[d],l.appendChild(c)}var C,L,S=this._click.bind(this);for(o=0;o<6;o++){L=elCreate("li"),f.appendChild(L);for(var E=0;E<7;E++)C=elCreate("a"),C.addEventListener(WCF_CLICK_EVENT,S),h.push(C),L.appendChild(C)}_=elCreate("footer"),w.appendChild(_),p=elCreate("select"),p.className="hour",p.addEventListener("change",this._formatValue.bind(this));var x="",D=new Date(2e3,0,1),T=t.get("wcf.date.timeFormat").replace(/:/,"").replace(/[isu]/g,"");for(o=0;o<24;o++)D.setHours(o),x+='<option value="'+o+'">'+e.format(D,T)+"</option>";for(p.innerHTML=x,_.appendChild(p),_.appendChild(document.createTextNode(" : ")),g=elCreate("select"),g.className="minute",g.addEventListener("change",this._formatValue.bind(this)),x="",o=0;o<60;o++)x+='<option value="'+o+'">'+(o<10?"0"+o.toString():o)+"</option>";g.innerHTML=x,_.appendChild(g),document.body.appendChild(w)}},previousMonth:function(){"0"===m.value?(m.value=11,y.value=~~y.value-1):m.value=~~m.value-1,this._renderGrid(void 0,m.value,y.value)},nextMonth:function(){"11"===m.value?(m.value=0,y.value=1+~~y.value):m.value=1+~~m.value,this._renderGrid(void 0,m.value,y.value)},_changeMonth:function(e){this._renderGrid(void 0,e.currentTarget.value)},_changeYear:function(e){this._renderGrid(void 0,void 0,e.currentTarget.value)},_click:function(e){e.currentTarget.classList.contains("otherMonth")||(elData(c,"empty",!1),this._renderGrid(e.currentTarget.textContent),this._close())},getDate:function(e){return e=this._getElement(e),e.hasAttribute("data-value")?new Date(+elData(e,"value")):null},setDate:function(t,n){t=this._getElement(t);var i=l.get(t);elData(t,"value",n.getTime()),t.value=e["formatDate"+(i.isDateTime?"Time":"")](n);var o="";o=i.ignoreTimezone?"Y-m-dTH:i:s":i.isDateTime?"c":"Y-m-d",i.shadow.value=e.format(n,o)},getValue:function(e){e=this._getElement(e);var t=l.get(e);return t?t.shadow.value:""},clear:function(e){e=this._getElement(e);var t=l.get(e);e.removeAttribute("data-value"),e.value="",t.disableClear||t.clearButton.style.setProperty("visibility","hidden",""),t.isEmpty=!0,t.shadow.value=""},destroy:function(e){e=this._getElement(e);var t=l.get(e),n=e.parentNode;n.parentNode.insertBefore(e,n),elRemove(n),elAttr(e,"type","date"+(t.isDateTime?"time":"")),e.name=t.shadow.name,e.value=t.shadow.value,e.removeAttribute("data-value"),e.removeEventListener(WCF_CLICK_EVENT,C),elRemove(t.shadow),e.classList.remove("inputDatePicker"),e.readOnly=!1,l.delete(e)},setCloseCallback:function(e,t){e=this._getElement(e),l.get(e).onClose=t},_getElement:function(e){if("string"==typeof e&&(e=elById(e)),!(e instanceof Element&&e.classList.contains("inputDatePicker")&&l.has(e)))throw new Error("Expected a valid date picker input element or id.");return e}};return window.__wcf_bc_datePicker=L,L}),define("WoltLabSuite/Core/Ui/Page/Action",["Dictionary","Dom/Util"],function(e,t){"use strict";var n=new e,i=null,o=!1;return{setup:function(){o=!0,i=elCreate("ul"),i.className="pageAction",document.body.appendChild(i)},add:function(e,a,r){!1===o&&this.setup();var s=elCreate("li");if(a.classList.add("button"),a.classList.add("buttonPrimary"),s.appendChild(a),elAttr(s,"aria-hidden","toTop"===e?"true":"false"),elData(s,"name",e),"toTop"===e)s.className="toTop initiallyHidden",i.appendChild(s);else{var l=null;r&&void 0!==(l=n.get(r))&&(l=l.parentNode),null===l&&i.childElementCount&&(l=i.children[0]),null===l?t.prepend(s,i):i.insertBefore(s,l)}n.set(e,a),this._renderContainer()},has:function(e){return n.has(e)},get:function(e){return n.get(e)},remove:function(e){var t=n.get(e);if(void 0!==t){var o=t.parentNode;o.addEventListener("animationend",function(){try{i.removeChild(o),n.delete(e)}catch(e){}}),this.hide(e)}},hide:function(e){var t=n.get(e);t&&(elAttr(t.parentNode,"aria-hidden","true"),this._renderContainer())},show:function(e){var t=n.get(e);t&&(t.parentNode.classList.contains("initiallyHidden")&&t.parentNode.classList.remove("initiallyHidden"),elAttr(t.parentNode,"aria-hidden","false"),this._renderContainer())},_renderContainer:function(){var e=!1;if(i.childElementCount)for(var t=0,n=i.childElementCount;t<n;t++)if("false"===elAttr(i.children[t],"aria-hidden")){e=!0;break}i.classList[e?"add":"remove"]("active")}}}),define("WoltLabSuite/Core/Ui/Page/JumpToTop",["Environment","Language","./Action"],function(e,t,n){"use strict";function i(){this.init()}return i.prototype={init:function(){if("desktop"===e.platform()){this._callbackScrollEnd=this._afterScroll.bind(this),this._timeoutScroll=null;var i=elCreate("a");i.className="jsTooltip",i.href="#",elAttr(i,"title",t.get("wcf.global.scrollUp")),i.innerHTML='<span class="icon icon32 fa-angle-up"></span>',i.addEventListener(WCF_CLICK_EVENT,this._jump.bind(this)),n.add("toTop",i),window.addEventListener("scroll",this._scroll.bind(this)),this._afterScroll()}},_jump:function(e){e.preventDefault(),elById("top").scrollIntoView({behavior:"smooth"})},_scroll:function(){null!==this._timeoutScroll&&window.clearTimeout(this._timeoutScroll),this._timeoutScroll=window.setTimeout(this._callbackScrollEnd,100)},_afterScroll:function(){this._timeoutScroll=null,n[window.pageYOffset>=300?"show":"hide"]("toTop")}},i}),define("WoltLabSuite/Core/Bootstrap",["favico","enquire","perfect-scrollbar","WoltLabSuite/Core/Date/Time/Relative","Ui/SimpleDropdown","WoltLabSuite/Core/Ui/Mobile","WoltLabSuite/Core/Ui/TabMenu","WoltLabSuite/Core/Ui/FlexibleMenu","Ui/Dialog","WoltLabSuite/Core/Ui/Tooltip","WoltLabSuite/Core/Language","WoltLabSuite/Core/Environment","WoltLabSuite/Core/Date/Picker","EventHandler","Core","WoltLabSuite/Core/Ui/Page/JumpToTop","Devtools"],function(e,t,n,i,o,a,r,s,l,c,u,d,h,f,p,g,m){"use strict";return window.Favico=e,window.enquire=t,null==window.WCF&&(window.WCF={}),null==window.WCF.Language&&(window.WCF.Language={}),window.WCF.Language.get=u.get,window.WCF.Language.add=u.add,window.WCF.Language.addObject=u.addObject,window.__wcf_bc_eventHandler=f,{setup:function(e){e=p.extend({enableMobileMenu:!0},e),window.ENABLE_DEVELOPER_TOOLS&&m._internal_.enable(),d.setup(),i.setup(),h.init(),o.setup(),
-a.setup({enableMobileMenu:e.enableMobileMenu}),r.setup(),l.setup(),c.setup();for(var t=elBySelAll("form[method=get]"),n=0,s=t.length;n<s;n++)t[n].setAttribute("method","post");"microsoft"===d.browser()&&(window.onbeforeunload=function(){});var u=0;u=window.setInterval(function(){"function"==typeof window.jQuery&&(window.clearInterval(u),window.jQuery(function(){new g}),window.jQuery.holdReady(!1))},20)}}}),define("WoltLabSuite/Core/Controller/Style/Changer",["Ajax","Language","Ui/Dialog"],function(e,t,n){"use strict";return{setup:function(){var e=elBySel(".jsButtonStyleChanger");e&&e.addEventListener(WCF_CLICK_EVENT,this.showDialog.bind(this))},showDialog:function(e){e.preventDefault(),n.open(this)},_dialogSetup:function(){return{id:"styleChanger",options:{disableContentPadding:!0,title:t.get("wcf.style.changeStyle")},source:{data:{actionName:"getStyleChooser",className:"wcf\\data\\style\\StyleAction"},after:function(e){for(var t=elBySelAll(".styleList > li",e),n=0,i=t.length;n<i;n++){var o=t[n];o.classList.add("pointer"),o.addEventListener(WCF_CLICK_EVENT,this._click.bind(this))}}.bind(this)}}},_click:function(t){t.preventDefault(),e.apiOnce({data:{actionName:"changeStyle",className:"wcf\\data\\style\\StyleAction",objectIDs:[elData(t.currentTarget,"style-id")]},success:function(){window.location.reload()}})}}}),define("WoltLabSuite/Core/Controller/Popover",["Ajax","Dictionary","Environment","Dom/ChangeListener","Dom/Util","Ui/Alignment"],function(e,t,n,i,o,a){"use strict";var r=null,s=new t,l=new t,c=new t,u=null,d=!1,h=null,f=null,p=null,g=null,m=null,v=null,b=null,_=null;return{_setup:function(){if(null===p){p=elCreate("div"),p.className="popover forceHide",g=elCreate("div"),g.className="popoverContent",p.appendChild(g);var e=elCreate("span");e.className="elementPointer",e.appendChild(elCreate("span")),p.appendChild(e),document.body.appendChild(p),m=this._hide.bind(this),b=this._mouseEnter.bind(this),_=this._mouseLeave.bind(this),p.addEventListener("mouseenter",this._popoverMouseEnter.bind(this)),p.addEventListener("mouseleave",_),p.addEventListener("animationend",this._clearContent.bind(this)),window.addEventListener("beforeunload",function(){d=!0,null!==h&&window.clearTimeout(h),this._hide(!0)}.bind(this)),i.add("WoltLabSuite/Core/Controller/Popover",this._init.bind(this))}},init:function(e){"desktop"===n.platform()&&(e.attributeName=e.attributeName||"data-object-id",e.legacy=!0===e.legacy,this._setup(),c.has(e.identifier)||(c.set(e.identifier,{attributeName:e.attributeName,elements:e.legacy?e.className:elByClass(e.className),legacy:e.legacy,loadCallback:e.loadCallback}),this._init(e.identifier)))},_init:function(e){"string"==typeof e&&e.length?this._initElements(c.get(e),e):c.forEach(this._initElements.bind(this))},_initElements:function(e,t){for(var n=e.legacy?elBySelAll(e.elements):e.elements,i=0,a=n.length;i<a;i++){var r=n[i],c=o.identify(r);if(s.has(c))return;if(null!==r.closest(".popover"))return void s.set(c,{content:null,state:0});var u=e.legacy?c:~~r.getAttribute(e.attributeName);if(0!==u){r.addEventListener("mouseenter",b),r.addEventListener("mouseleave",_),"A"===r.nodeName&&elAttr(r,"href")&&r.addEventListener(WCF_CLICK_EVENT,m);var d=t+"-"+u;elData(r,"cache-id",d),l.set(c,{element:r,identifier:t,objectId:u}),s.has(d)||s.set(t+"-"+u,{content:null,state:0})}}},setContent:function(e,t,n){var i=e+"-"+t,a=s.get(i);if(void 0===a)throw new Error("Unable to find element for object id '"+t+"' (identifier: '"+e+"').");var c=o.createFragmentFromHtml(n);if(c.childElementCount||(c=o.createFragmentFromHtml("<p>"+n+"</p>")),a.content=c,a.state=2,r){var u=l.get(r).element;elData(u,"cache-id")===i&&this._show()}},_mouseEnter:function(e){if(!d){null!==h&&(window.clearTimeout(h),h=null);var t=o.identify(e.currentTarget);r===t&&null!==f&&(window.clearTimeout(f),f=null),u=t,h=window.setTimeout(function(){h=null,u===t&&this._show()}.bind(this),800)}},_mouseLeave:function(){u=null,null===f&&(null===v&&(v=this._hide.bind(this)),null!==f&&window.clearTimeout(f),f=window.setTimeout(v,500))},_popoverMouseEnter:function(){null!==f&&(window.clearTimeout(f),f=null)},_show:function(){null!==f&&(window.clearTimeout(f),f=null);var e=!1;p.classList.contains("active")?r!==u&&(this._hide(),e=!0):g.childElementCount&&(e=!0),e&&(p.classList.add("forceHide"),p.offsetTop,this._clearContent(),p.classList.remove("forceHide")),r=u;var t=l.get(r);if(void 0!==t){var n=s.get(elData(t.element,"cache-id"));2===n.state?(g.appendChild(n.content),this._rebuild(r)):0===n.state&&(n.state=1,c.get(t.identifier).loadCallback(t.objectId,this))}},_hide:function(){null!==f&&(window.clearTimeout(f),f=null),p.classList.remove("active")},_clearContent:function(){if(r&&g.childElementCount&&!p.classList.contains("active"))for(var e=s.get(elData(l.get(r).element,"cache-id"));g.childNodes.length;)e.content.appendChild(g.childNodes[0])},_rebuild:function(){p.classList.contains("active")||(p.classList.remove("forceHide"),p.classList.add("active"),a.set(p,l.get(r).element,{pointer:!0,vertical:"top"}))},_ajaxSetup:function(){return{silent:!0}},ajaxApi:function(t,n,i){if("function"!=typeof n)throw new TypeError("Expected a valid callback for parameter 'success'.");e.api(this,t,n,i)}}}),define("WoltLabSuite/Core/Ui/User/Ignore",["List","Dom/ChangeListener"],function(e,t){"use strict";var n=function(){};return n.prototype={init:function(){},_rebuild:function(){},_removeClass:function(){}},n}),define("WoltLabSuite/Core/Ui/Page/Header/Menu",["Environment","Ui/Screen"],function(e,t){"use strict";var n,i,o,a,r=!1,s=0,l=[],c=[];return{init:function(){if(a=elBySel(".mainMenu .boxMenu"),null===(o=a&&a.childElementCount?a.children[0]:null))throw new Error("Unable to find the menu.");t.on("screen-lg",{enable:this._enable.bind(this),disable:this._disable.bind(this),setup:this._setup.bind(this)})},_enable:function(){r=!0,"safari"===e.browser()?window.setTimeout(this._rebuildVisibility.bind(this),1e3):(this._rebuildVisibility(),window.setTimeout(this._rebuildVisibility.bind(this),1e3))},_disable:function(){r=!1},_showNext:function(e){if(e.preventDefault(),c.length){var t=c.slice(0,3).pop();this._setMarginLeft(a.clientWidth-(t.offsetLeft+t.clientWidth)),a.lastElementChild===t&&n.classList.remove("active"),i.classList.add("active")}},_showPrevious:function(e){if(e.preventDefault(),l.length){var t=l.slice(-3)[0];this._setMarginLeft(-1*t.offsetLeft),a.firstElementChild===t&&i.classList.remove("active"),n.classList.add("active")}},_setMarginLeft:function(e){s=Math.min(s+e,0),o.style.setProperty("margin-left",s+"px","")},_rebuildVisibility:function(){if(r){l=[],c=[];var e=a.clientWidth;if(a.scrollWidth>e||s<0)for(var t,o=0,u=a.childElementCount;o<u;o++){t=a.children[o];var d=t.offsetLeft;d<0?l.push(t):d+t.clientWidth>e&&c.push(t)}i.classList[l.length?"add":"remove"]("active"),n.classList[c.length?"add":"remove"]("active")}},_setup:function(){n=elCreate("a"),n.className="mainMenuShowNext",n.href="#",n.innerHTML='<span class="icon icon32 fa-angle-right"></span>',n.addEventListener(WCF_CLICK_EVENT,this._showNext.bind(this)),a.parentNode.appendChild(n),i=elCreate("a"),i.className="mainMenuShowPrevious",i.href="#",i.innerHTML='<span class="icon icon32 fa-angle-left"></span>',i.addEventListener(WCF_CLICK_EVENT,this._showPrevious.bind(this)),a.parentNode.insertBefore(i,a.parentNode.firstChild);var e=this._rebuildVisibility.bind(this);o.addEventListener("transitionend",e),window.addEventListener("resize",function(){o.style.setProperty("margin-left","0px",""),s=0,e()}),this._enable()}}}),define("WoltLabSuite/Core/BootstrapFrontend",["WoltLabSuite/Core/BackgroundQueue","WoltLabSuite/Core/Bootstrap","WoltLabSuite/Core/Controller/Style/Changer","WoltLabSuite/Core/Controller/Popover","WoltLabSuite/Core/Ui/User/Ignore","WoltLabSuite/Core/Ui/Page/Header/Menu"],function(e,t,n,i,o,a){"use strict";return{setup:function(i){i.backgroundQueue.url=WSC_API_URL+i.backgroundQueue.url.substr(WCF_PATH.length),t.setup(),a.init(),i.styleChanger&&n.setup(),i.enableUserPopover&&this._initUserPopover(),e.setUrl(i.backgroundQueue.url),(Math.random()<.1||i.backgroundQueue.force)&&e.invoke()},_initUserPopover:function(){i.init({attributeName:"data-user-id",className:"userLink",identifier:"com.woltlab.wcf.user",loadCallback:function(e,t){var n=function(n){t.setContent("com.woltlab.wcf.user",e,n.returnValues.template)};t.ajaxApi({actionName:"getUserProfile",className:"wcf\\data\\user\\UserProfileAction",objectIDs:[e]},n,n)}})}}}),define("WoltLabSuite/Core/ColorUtil",[],function(){"use strict";var e={hsvToRgb:function(e,t,n){var i,o,a,r,s,l={r:0,g:0,b:0};if(i=Math.floor(e/60),o=e/60-i,t/=100,n/=100,a=n*(1-t),r=n*(1-t*o),s=n*(1-t*(1-o)),0==t)l.r=l.g=l.b=n;else switch(i){case 1:l.r=r,l.g=n,l.b=a;break;case 2:l.r=a,l.g=n,l.b=s;break;case 3:l.r=a,l.g=r,l.b=n;break;case 4:l.r=s,l.g=a,l.b=n;break;case 5:l.r=n,l.g=a,l.b=r;break;case 0:case 6:l.r=n,l.g=s,l.b=a}return{r:Math.round(255*l.r),g:Math.round(255*l.g),b:Math.round(255*l.b)}},rgbToHsv:function(e,t,n){var i,o,a,r,s,l;if(e/=255,t/=255,n/=255,r=Math.max(Math.max(e,t),n),s=Math.min(Math.min(e,t),n),l=r-s,i=0,r!==s){switch(r){case e:i=(t-n)/l*60;break;case t:i=60*(2+(n-e)/l);break;case n:i=60*(4+(e-t)/l)}i<0&&(i+=360)}return o=0===r?0:l/r,a=r,{h:Math.round(i),s:Math.round(100*o),v:Math.round(100*a)}},hexToRgb:function(e){if(/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(e)){var t=e.split("");return"#"===t[0]&&t.shift(),3===t.length?{r:parseInt(t[0]+""+t[0],16),g:parseInt(t[1]+""+t[1],16),b:parseInt(t[2]+""+t[2],16)}:{r:parseInt(t[0]+""+t[1],16),g:parseInt(t[2]+""+t[3],16),b:parseInt(t[4]+""+t[5],16)}}return Number.NaN},rgbToHex:function(e,t,n){var i="0123456789ABCDEF";return void 0===t&&e.toString().match(/^rgba?\((\d+), ?(\d+), ?(d\+)(?:, ?[0-9.]+)?\)$/)&&(e=RegExp.$1,t=RegExp.$2,n=RegExp.$3),i.charAt((e-e%16)/16)+""+i.charAt(e%16)+i.charAt((t-t%16)/16)+i.charAt(t%16)+i.charAt((n-n%16)/16)+i.charAt(n%16)}};return window.__wcf_bc_colorUtil=e,e}),define("WoltLabSuite/Core/FileUtil",["Dictionary","StringUtil"],function(e,t){"use strict";var n=e.fromObject({zip:"archive",rar:"archive",tar:"archive",gz:"archive",mp3:"audio",ogg:"audio",wav:"audio",php:"code",html:"code",htm:"code",tpl:"code",js:"code",xls:"excel",ods:"excel",xlsx:"excel",gif:"image",jpg:"image",jpeg:"image",png:"image",bmp:"image",avi:"video",wmv:"video",mov:"video",mp4:"video",mpg:"video",mpeg:"video",flv:"video",pdf:"pdf",ppt:"powerpoint",pptx:"powerpoint",txt:"text",doc:"word",docx:"word",odt:"word"});return{formatFilesize:function(e,n){void 0===n&&(n=2);var i="Byte";return e>=1e3&&(e/=1e3,i="kB"),e>=1e3&&(e/=1e3,i="MB"),e>=1e3&&(e/=1e3,i="GB"),e>=1e3&&(e/=1e3,i="TB"),t.formatNumeric(e,-n)+" "+i},getIconNameByFilename:function(e){var t=e.lastIndexOf(".");if(!1!==t){var i=e.substr(t+1);if(n.has(i))return n.get(i)}return""}}}),define("WoltLabSuite/Core/Permission",["Dictionary"],function(e){"use strict";var t=new e;return{add:function(e,n){if("boolean"!=typeof n)throw new TypeError("Permission value has to be boolean.");t.set(e,n)},addObject:function(e){for(var t in e)objOwns(e,t)&&this.add(t,e[t])},get:function(e){return!!t.has(e)&&t.get(e)}}}),define("WoltLabSuite/Core/Upload",["AjaxRequest","Core","Dom/ChangeListener","Language","Dom/Util","Dom/Traverse"],function(e,t,n,i,o,a){"use strict";var r=function(){};return r.prototype={_createButton:function(){},_createFileElement:function(){},_createFileElements:function(){},_failure:function(){},_getParameters:function(){},_insertButton:function(){},_progress:function(){},_removeButton:function(){},_success:function(){},_upload:function(){},_uploadFiles:function(){}},r}),define("WoltLabSuite/Core/User",[],function(){"use strict";var e,t=!1;return{getLink:function(){return e},init:function(n,i,o){if(t)throw new Error("User has already been initialized.");Object.defineProperty(this,"userId",{value:n,writable:!1}),Object.defineProperty(this,"username",{value:i,writable:!1}),e=o,t=!0}}}),define("WoltLabSuite/Core/Ajax/Jsonp",["Core"],function(e){"use strict";return{send:function(t,n,i,o){if(t="string"==typeof t?t.trim():"",0===t.length)throw new Error("Expected a non-empty string for parameter 'url'.");if("function"!=typeof n)throw new TypeError("Expected a valid callback function for parameter 'success'.");o=e.extend({parameterName:"callback",timeout:10},o||{});var a,r="wcf_jsonp_"+e.getUuid().replace(/-/g,"").substr(0,8),s=window.setTimeout(function(){"function"==typeof i&&i(),window[r]=void 0,elRemove(a)},1e3*(~~o.timeout||10));window[r]=function(){window.clearTimeout(s),n.apply(null,arguments),window[r]=void 0,elRemove(a)},t+=-1===t.indexOf("?")?"?":"&",t+=o.parameterName+"="+r,a=elCreate("script"),a.async=!0,elAttr(a,"src",t),document.head.appendChild(a)}}}),define("WoltLabSuite/Core/Bbcode/Collapsible",[],function(){"use strict";var e=elByClass("jsCollapsibleBbcode");return{observe:function(){for(var t,n;e.length;)t=e[0],n=null,elBySelAll(".toggleButton:not(.jsToggleButtonEnabled)",t,function(e){e.closest(".jsCollapsibleBbcode")===t&&(n=e)}),n&&function(e,t){var n=function(n){if(e.classList.toggle("collapsed")){if(t.textContent=elData(t,"title-expand"),n instanceof Event){var i=e.getBoundingClientRect().top;if(i<0){var o=window.pageYOffset+(i-100);o<0&&(o=0),window.scrollTo(window.pageXOffset,o)}}}else t.textContent=elData(t,"title-collapse")};t.classList.add("jsToggleButtonEnabled"),t.addEventListener(WCF_CLICK_EVENT,n),0!==e.scrollTop&&n()}(t,n),t.classList.remove("jsCollapsibleBbcode")}}}),define("WoltLabSuite/Core/Controller/Captcha",["Dictionary"],function(e){"use strict";var t=new e;return{add:function(e,n){if(t.has(e))throw new Error("Captcha with id '"+e+"' is already registered.");if("function"!=typeof n)throw new TypeError("Expected a valid callback for parameter 'callback'.");t.set(e,n)},delete:function(e){if(!t.has(e))throw new Error("Unknown captcha with id '"+e+"'.");t.delete(e)()},has:function(e){return t.has(e)},getData:function(e){if(!t.has(e))throw new Error("Unknown captcha with id '"+e+"'.");return t.get(e)()}}}),define("WoltLabSuite/Core/Controller/Clipboard",["Ajax","Core","Dictionary","EventHandler","Language","List","ObjectMap","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Confirmation","Ui/SimpleDropdown","WoltLabSuite/Core/Ui/Page/Action"],function(e,t,n,i,o,a,r,s,l,c,u,d,h){"use strict";return{setup:function(){},reload:function(){},_initContainers:function(){},_loadMarkedItems:function(){},_markAll:function(){},_mark:function(){},_saveState:function(){},_executeAction:function(){},_executeProxyAction:function(){},_unmarkAll:function(){},_ajaxSetup:function(){},_ajaxSuccess:function(){},_rebuildMarkings:function(){},hideEditor:function(){},showEditor:function(){},unmark:function(){}}}),define("WoltLabSuite/Core/Language/Chooser",["Dictionary","Language","Dom/Traverse","Dom/Util","ObjectMap","Ui/SimpleDropdown"],function(e,t,n,i,o,a){"use strict";var r=new e,s=!1,l=new o,c=null;return{init:function(e,t,n,i,o,a){if(!r.has(t)){var s=elById(e);if(null===s)throw new Error("Expected a valid container id, cannot find '"+t+"'.");var l=elById(t);null===l&&(l=elCreate("input"),elAttr(l,"type","hidden"),elAttr(l,"id",t),elAttr(l,"name",t),elAttr(l,"value",n),s.appendChild(l)),this._initElement(t,l,n,i,o,a)}},_setup:function(){s||(s=!0,c=this._submit.bind(this))},_initElement:function(e,i,o,s,u,d){var h;"DD"===i.parentNode.nodeName?(h=elCreate("div"),h.className="dropdown",i.parentNode.insertBefore(h,i)):(h=i.parentNode,h.classList.add("dropdown")),elHide(i);var f=elCreate("a");f.className="dropdownToggle dropdownIndicator boxFlag box24 inputPrefix"+("DD"===i.parentNode.nodeName?" button":""),h.appendChild(f);var p=elCreate("ul");p.className="dropdownMenu",h.appendChild(p);var g,m,v,b,_=function(t){var i=~~elData(t.currentTarget,"language-id"),o=n.childByClass(p,"active");null!==o&&o.classList.remove("active"),i&&t.currentTarget.classList.add("active"),this._select(e,i,t.currentTarget)}.bind(this);for(var y in s)if(s.hasOwnProperty(y)){var w=s[y];v=elCreate("li"),v.className="boxFlag",v.addEventListener(WCF_CLICK_EVENT,_),elData(v,"language-id",y),void 0!==w.languageCode&&elData(v,"language-code",w.languageCode),p.appendChild(v),g=elCreate("a"),g.className="box24",v.appendChild(g),m=elCreate("img"),elAttr(m,"src",w.iconPath),elAttr(m,"alt",""),m.className="iconFlag",g.appendChild(m),b=elCreate("span"),b.textContent=w.languageName,g.appendChild(b),y==o&&(f.innerHTML=v.firstChild.innerHTML)}if(d)v=elCreate("li"),v.className="dropdownDivider",p.appendChild(v),v=elCreate("li"),elData(v,"language-id",0),v.addEventListener(WCF_CLICK_EVENT,_),p.appendChild(v),g=elCreate("a"),g.textContent=t.get("wcf.global.language.noSelection"),v.appendChild(g),0===o&&(f.innerHTML=v.firstChild.innerHTML),v.addEventListener(WCF_CLICK_EVENT,_);else if(0===o){f.innerHTML=null;var C=elCreate("div");f.appendChild(C),b=elCreate("span"),b.className="icon icon24 fa-question",C.appendChild(b),b=elCreate("span"),b.textContent=t.get("wcf.global.language.noSelection"),C.appendChild(b)}a.init(f),r.set(e,{callback:u,dropdownMenu:p,dropdownToggle:f,element:i});var L=n.parentByTag(i,"FORM");if(null!==L){L.addEventListener("submit",c);var S=l.get(L);void 0===S&&(S=[],l.set(L,S)),S.push(e)}},_select:function(e,t,n){var i=r.get(e);if(void 0===n){for(var o=i.dropdownMenu.childNodes,a=0,s=o.length;a<s;a++){var l=o[a];if(~~elData(l,"language-id")===t){n=l;break}}if(void 0===n)throw new Error("Cannot select unknown language id '"+t+"'")}i.element.value=t,i.dropdownToggle.innerHTML=n.firstChild.innerHTML,r.set(e,i),"function"==typeof i.callback&&i.callback(n)},_submit:function(e){for(var t,n=l.get(e.currentTarget),i=0,o=n.length;i<o;i++)t=elCreate("input"),t.type="hidden",t.name=n[i],t.value=this.getLanguageId(n[i]),e.currentTarget.appendChild(t)},getChooser:function(e){var t=r.get(e);if(void 0===t)throw new Error("Expected a valid language chooser input element, '"+e+"' is not i18n input field.");return t},getLanguageId:function(e){return~~this.getChooser(e).element.value},removeChooser:function(e){r.has(e)&&r.delete(e)},setLanguageId:function(e,t){if(void 0===r.get(e))throw new Error("Expected a valid input element, '"+e+"' is not i18n input field.");this._select(e,t)}}}),define("WoltLabSuite/Core/Language/Input",["Core","Dictionary","Language","ObjectMap","StringUtil","Dom/Traverse","Dom/Util","Ui/SimpleDropdown"],function(e,t,n,i,o,a,r,s){"use strict";var l=new t,c=!1,u=new i,d=new t,h=null,f=null;return{init:function(e,n,i,a){if(!d.has(e)){var r=elById(e);if(null===r)throw new Error("Expected a valid element id, cannot find '"+e+"'.");this._setup();var s=new t;for(var l in n)n.hasOwnProperty(l)&&s.set(~~l,o.unescapeHTML(n[l]));d.set(e,s),this._initElement(e,r,s,i,a)}},registerCallback:function(e,t,n){if(!d.has(e))throw new Error("Unknown element id '"+e+"'.");l.get(e).callbacks.set(t,n)},_setup:function(){c||(c=!0,h=this._dropdownToggle.bind(this),f=this._submit.bind(this))},_initElement:function(e,i,o,c,d){var p=i.parentNode;p.classList.contains("inputAddon")||(p=elCreate("div"),p.className="inputAddon"+("TEXTAREA"===i.nodeName?" inputAddonTextarea":""),elData(p,"input-id",e),i.parentNode.insertBefore(p,i),p.appendChild(i)),p.classList.add("dropdown");var g=elCreate("span");g.className="button dropdownToggle inputPrefix";var m=elCreate("span");m.textContent=n.get("wcf.global.button.disabledI18n"),g.appendChild(m),p.insertBefore(g,i);var v=elCreate("ul");v.className="dropdownMenu",r.insertAfter(v,g);var b,_=function(t,n){var i=~~elData(t.currentTarget,"language-id"),o=a.childByClass(v,"active");null!==o&&o.classList.remove("active"),i&&t.currentTarget.classList.add("active"),this._select(e,i,n||!1)}.bind(this);for(var y in c)c.hasOwnProperty(y)&&(b=elCreate("li"),elData(b,"language-id",y),m=elCreate("span"),m.textContent=c[y],b.appendChild(m),b.addEventListener(WCF_CLICK_EVENT,_),v.appendChild(b));!0!==d&&(b=elCreate("li"),b.className="dropdownDivider",v.appendChild(b),b=elCreate("li"),elData(b,"language-id",0),m=elCreate("span"),m.textContent=n.get("wcf.global.button.disabledI18n"),b.appendChild(m),b.addEventListener(WCF_CLICK_EVENT,_),v.appendChild(b));var w=null;if(!0===d||o.size)for(var C=0,L=v.childElementCount;C<L;C++)if(~~elData(v.children[C],"language-id")===LANGUAGE_ID){w=v.children[C];break}s.init(g),s.registerCallback(p.id,h),l.set(e,{buttonLabel:g.children[0],callbacks:new t,element:i,languageId:0,isEnabled:!0,forceSelection:d});var S=a.parentByTag(i,"FORM");if(null!==S){S.addEventListener("submit",f);var E=u.get(S);void 0===E&&(E=[],u.set(S,E)),E.push(e)}null!==w&&_({currentTarget:w},!0)},_select:function(e,n,i){for(var o,a=l.get(e),r=s.getDropdownMenu(a.element.closest(".inputAddon").id),c="",u=0,h=r.childElementCount;u<h;u++){o=r.children[u];var f=elData(o,"language-id");f.length&&n===~~f&&(c=o.children[0].textContent)}if(a.languageId!==n){var p=d.get(e);a.languageId&&p.set(a.languageId,a.element.value),0===n?d.set(e,new t):(a.buttonLabel.classList.contains("active")||!0===i)&&(a.element.value=p.has(n)?p.get(n):""),a.buttonLabel.textContent=c,a.buttonLabel.classList[n?"add":"remove"]("active"),a.languageId=n}i||(a.element.blur(),a.element.focus()),a.callbacks.has("select")&&a.callbacks.get("select")(a.element)},_dropdownToggle:function(e,t){if("open"===t)for(var n,i,o=s.getDropdownMenu(e),a=elData(elById(e),"input-id"),r=l.get(a),c=d.get(a),u=0,h=o.childElementCount;u<h;u++)if(n=o.children[u],i=~~elData(n,"language-id")){var f=!1;r.languageId&&(f=i===r.languageId?""===r.element.value.trim():!c.get(i)),n.classList[f?"add":"remove"]("missingValue")}},_submit:function(e){for(var t,n,i,o,a=u.get(e.currentTarget),r=0,s=a.length;r<s;r++)n=a[r],t=l.get(n),t.isEnabled&&(o=d.get(n),t.callbacks.has("submit")&&t.callbacks.get("submit")(t.element),t.languageId&&o.set(t.languageId,t.element.value),o.size&&(o.forEach(function(t,o){i=elCreate("input"),i.type="hidden",i.name=n+"_i18n["+o+"]",i.value=t,e.currentTarget.appendChild(i)}),t.element.removeAttribute("name")))},getValues:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");var n=d.get(e);return n.set(t.languageId,t.element.value),n},setValues:function(n,i){var o=l.get(n);if(void 0===o)throw new Error("Expected a valid i18n input element, '"+n+"' is not i18n input field.");if(e.isPlainObject(i)&&(i=t.fromObject(i)),o.element.value="",i.has(0))return o.element.value=i.get(0),i.delete(0),d.set(n,i),void this._select(n,0,!0);d.set(n,i),o.languageId=0,this._select(n,LANGUAGE_ID,!0)},disable:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid element, '"+e+"' is not an i18n input field.");if(t.isEnabled){t.isEnabled=!1,elHide(t.buttonLabel.parentNode);var n=t.buttonLabel.parentNode.parentNode;n.classList.remove("inputAddon"),n.classList.remove("dropdown")}},enable:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");if(!t.isEnabled){t.isEnabled=!0,elShow(t.buttonLabel.parentNode);var n=t.buttonLabel.parentNode.parentNode;n.classList.add("inputAddon"),n.classList.add("dropdown")}},isEnabled:function(e){var t=l.get(e);if(void 0===t)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");return t.isEnabled},validate:function(e,t){var n=l.get(e);if(void 0===n)throw new Error("Expected a valid i18n input element, '"+e+"' is not i18n input field.");if(!n.isEnabled)return!0;var i=d.get(e),o=s.getDropdownMenu(n.element.parentNode.id);n.languageId&&i.set(n.languageId,n.element.value);for(var a,r,c=!1,u=!1,h=0,f=o.childElementCount;h<f;h++)if(a=o.children[h],r=~~elData(a,"language-id"))if(i.has(r)&&0!==i.get(r).length){if(c)return!1;u=!0}else{if(u)return!1;c=!0}return!c||t}}}),define("WoltLabSuite/Core/Language/Text",["Core","./Input"],function(e,t){"use strict";return{init:function(e,n,i,o){var a=elById(e);if(!a||"TEXTAREA"!==a.nodeName||!a.classList.contains("wysiwygTextarea"))throw new Error('Expected <textarea class="wysiwygTextarea" /> for id \''+e+"'.");t.init(e,n,i,o),t.registerCallback(e,"select",this._callbackSelect.bind(this)),t.registerCallback(e,"submit",this._callbackSubmit.bind(this))},_callbackSelect:function(e){void 0!==window.jQuery&&window.jQuery(e).redactor("code.set",e.value)},_callbackSubmit:function(e){void 0!==window.jQuery&&(e.value=window.jQuery(e).redactor("code.get"))}}}),define("WoltLabSuite/Core/Ui/Notification",["Language"],function(e){"use strict";var t=!1,n=null,i=null,o=null,a=null,r=null;return{show:function(s,l,c){t||(this._init(),n="function"==typeof l?l:null,i.className=c||"success",i.textContent=e.get(s||"wcf.global.success"),t=!0,o.classList.add("active"),a=setTimeout(r,2e3))},_init:function(){null===o&&(r=this._hide.bind(this),o=elCreate("div"),o.id="systemNotification",i=elCreate("p"),i.addEventListener(WCF_CLICK_EVENT,r),o.appendChild(i),document.body.appendChild(o))},_hide:function(){clearTimeout(a),o.classList.remove("active"),null!==n&&n(),t=!1}}}),define("WoltLabSuite/Core/Media/Editor",["Ajax","Core","Dictionary","Dom/ChangeListener","Dom/Traverse","Language","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Language/Chooser","WoltLabSuite/Core/Language/Input","EventKey"],function(e,t,n,i,o,a,r,s,l,c,u){"use strict";var d=function(){};return d.prototype={_ajaxSetup:function(){},_ajaxSuccess:function(){},_close:function(){},_keyPress:function(){},_saveData:function(){},_updateLanguageFields:function(){},edit:function(){}},d}),define("WoltLabSuite/Core/Media/Upload",["Core","DateUtil","Dom/ChangeListener","Dom/Traverse","Dom/Util","EventHandler","Language","Permission","Upload","User","WoltLabSuite/Core/FileUtil"],function(e,t,n,i,o,a,r,s,l,c,u){"use strict";var d=function(){};return d.prototype={_createFileElement:function(){},_getParameters:function(){},_success:function(){},_uploadFiles:function(){},_createButton:function(){},_createFileElements:function(){},_failure:function(){},_insertButton:function(){},_progress:function(){},_removeButton:function(){},_upload:function(){}},d}),define("WoltLabSuite/Core/Notification/Handler",["Ajax","Core","EventHandler","StringUtil"],function(e,t,n,i){"use strict";if(!("Promise"in window&&"Notification"in window))return{setup:function(){}};var o=!1,a="",r=0,s=window.TIME_NOW,l=null,c=0;return{setup:function(e){if(e=t.extend({enableNotifications:!1,icon:"",sessionKeepAlive:0},e),a=e.icon,c=60*e.sessionKeepAlive,this._prepareNextRequest(),document.addEventListener("visibilitychange",this._onVisibilityChange.bind(this)),window.addEventListener("storage",this._onStorage.bind(this)),this._onVisibilityChange(null),e.enableNotifications)switch(window.Notification.permission){case"granted":o=!0;break;case"default":window.Notification.requestPermission(function(e){"granted"===e&&(o=!0)})}},_onVisibilityChange:function(e){if(null!==e&&!document.hidden){(Date.now()-r)/6e4>4&&(this._resetTimer(),this._dispatchRequest())}r=document.hidden?Date.now():0},_getNextDelay:function(){if(0===r)return 5;var e=~~((Date.now()-r)/6e4);return e<15?5:e<30?10:15},_resetTimer:function(){null!==l&&(window.clearTimeout(l),l=null)},_prepareNextRequest:function(){this._resetTimer();var e=Math.min(this._getNextDelay(),c);l=window.setTimeout(this._dispatchRequest.bind(this),6e4*e)},_dispatchRequest:function(){var t={};n.fire("com.woltlab.wcf.notification","beforePoll",t),t.lastRequestTimestamp=s,e.api(this,{parameters:t})},_onStorage:function(){this._prepareNextRequest();var e,i,o=!1;try{e=window.localStorage.getItem(t.getStoragePrefix()+"notification"),i=window.localStorage.getItem(t.getStoragePrefix()+"keepAliveData"),e=JSON.parse(e),i=JSON.parse(i)}catch(e){o=!0}o||n.fire("com.woltlab.wcf.notification","onStorage",{pollData:e,keepAliveData:i})},_ajaxSuccess:function(e){var i=!1,o=e.returnValues.keepAliveData,a=e.returnValues.pollData;window.WCF.System.PushNotification.executeCallbacks(o);try{window.localStorage.setItem(t.getStoragePrefix()+"notification",JSON.stringify(a)),window.localStorage.setItem(t.getStoragePrefix()+"keepAliveData",JSON.stringify(o))}catch(e){i=!0,window.console.log(e)}i||this._prepareNextRequest(),s=e.returnValues.lastRequestTimestamp,n.fire("com.woltlab.wcf.notification","afterPoll",a),this._showNotification(a)},_showNotification:function(e){if(o&&"object"==typeof e.notification&&"string"==typeof e.notification.message){var t=new window.Notification(e.notification.title,{body:i.unescapeHTML(e.notification.message),icon:a});t.onclick=function(){window.focus(),t.close(),window.location=e.notification.link}}},_ajaxSetup:function(){return{data:{actionName:"poll",className:"wcf\\data\\session\\SessionAction"},ignoreError:!window.ENABLE_DEBUG_MODE,silent:!window.ENABLE_DEBUG_MODE}}}}),define("WoltLabSuite/Core/Ui/Suggestion",["Ajax","Core","Ui/SimpleDropdown"],function(e,t,n){"use strict";function i(e,t){this.init(e,t)}return i.prototype={init:function(e,n){if(this._dropdownMenu=null,this._value="",this._element=elById(e),null===this._element)throw new Error("Expected a valid element id.");if(this._options=t.extend({ajax:{actionName:"getSearchResultList",className:"",interfaceName:"wcf\\data\\ISearchAction",parameters:{data:{}}},callbackSelect:null,excludedSearchValues:[],threshold:3},n),"function"!=typeof this._options.callbackSelect)throw new Error("Expected a valid callback for option 'callbackSelect'.");this._element.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation()}),this._element.addEventListener("keydown",this._keyDown.bind(this)),this._element.addEventListener("keyup",this._keyUp.bind(this))},addExcludedValue:function(e){-1===this._options.excludedSearchValues.indexOf(e)&&this._options.excludedSearchValues.push(e)},removeExcludedValue:function(e){var t=this._options.excludedSearchValues.indexOf(e);-1!==t&&this._options.excludedSearchValues.splice(t,1)},isActive:function(){return null!==this._dropdownMenu&&n.isOpen(this._element.id)},_keyDown:function(e){if(!this.isActive())return!0;if(13!==e.keyCode&&27!==e.keyCode&&38!==e.keyCode&&40!==e.keyCode)return!0;for(var t,i=0,o=this._dropdownMenu.childElementCount;i<o&&(t=this._dropdownMenu.children[i],!t.classList.contains("active"));)i++;if(13===e.keyCode)n.close(this._element.id),this._select(t);else if(27===e.keyCode){if(!n.isOpen(this._element.id))return!0;n.close(this._element.id)}else{var a=0;38===e.keyCode?a=(0===i?o:i)-1:40===e.keyCode&&(a=i+1)===o&&(a=0),a!==i&&(t.classList.remove("active"),this._dropdownMenu.children[a].classList.add("active"))}return e.preventDefault(),!1},_select:function(e){var t=e instanceof Event;t&&(e=e.currentTarget.parentNode),this._options.callbackSelect(this._element.id,{objectId:elData(e.children[0],"object-id"),value:e.textContent}),t&&this._element.focus()},_keyUp:function(t){var i=t.currentTarget.value.trim();if(this._value!==i){if(i.length<this._options.threshold)return null!==this._dropdownMenu&&n.close(this._element.id),void(this._value=i);this._value=i,e.api(this,{parameters:{data:{excludedSearchValues:this._options.excludedSearchValues,searchString:i}}})}},_ajaxSetup:function(){return{data:this._options.ajax}},_ajaxSuccess:function(e){if(null===this._dropdownMenu?(this._dropdownMenu=elCreate("div"),this._dropdownMenu.className="dropdownMenu",n.initFragment(this._element,this._dropdownMenu)):this._dropdownMenu.innerHTML="",e.returnValues.length){for(var t,i,o,a=0,r=e.returnValues.length;a<r;a++)i=e.returnValues[a],t=elCreate("a"),t.textContent=i.label,elData(t,"object-id",i.objectID),t.addEventListener(WCF_CLICK_EVENT,this._select.bind(this)),o=elCreate("li"),0===a&&(o.className="active"),o.appendChild(t),this._dropdownMenu.appendChild(o);n.open(this._element.id)}else n.close(this._element.id)}},i}),define("WoltLabSuite/Core/Ui/ItemList",["Core","Dictionary","Language","Dom/Traverse","EventKey","WoltLabSuite/Core/Ui/Suggestion","Ui/SimpleDropdown"],function(e,t,n,i,o,a,r){"use strict"
-;var s="",l=new t,c=!1,u=null,d=null,h=null,f=null,p=null,g=null;return{init:function(t,n,o){var s=elById(t);if(null===s)throw new Error("Expected a valid element id, '"+t+"' is invalid.");if(l.has(t)){var c=l.get(t);for(var u in c)if(c.hasOwnProperty(u)){var d=c[u];d instanceof Element&&d.parentNode&&elRemove(d)}r.destroy(t),l.delete(t)}o=e.extend({ajax:{actionName:"getSearchResultList",className:"",data:{}},excludedSearchValues:[],maxItems:-1,maxLength:-1,restricted:!1,isCSV:!1,callbackChange:null,callbackSubmit:null,submitFieldName:""},o);var h=i.parentByTag(s,"FORM");if(null!==h&&!1===o.isCSV){if(!o.submitFieldName.length&&"function"!=typeof o.callbackSubmit)throw new Error("Expected a valid function for option 'callbackSubmit', a non-empty value for option 'submitFieldName' or enabling the option 'submitFieldCSV'.");h.addEventListener("submit",function(){var e=this.getValues(t);if(o.submitFieldName.length)for(var n,i=0,a=e.length;i<a;i++)n=elCreate("input"),n.type="hidden",n.name=o.submitFieldName.replace(/{$objectId}/,e[i].objectId),n.value=e[i].value,h.appendChild(n);else o.callbackSubmit(h,e)}.bind(this))}this._setup();var f=this._createUI(s,o),p=new a(t,{ajax:o.ajax,callbackSelect:this._addItem.bind(this),excludedSearchValues:o.excludedSearchValues});if(l.set(t,{dropdownMenu:null,element:f.element,list:f.list,listItem:f.element.parentNode,options:o,shadow:f.shadow,suggestion:p}),n=f.values.length?f.values:n,Array.isArray(n))for(var g,m=0,v=n.length;m<v;m++)g=n[m],"string"==typeof g&&(g={objectId:0,value:g}),this._addItem(t,g)},getValues:function(e){if(!l.has(e))throw new Error("Element id '"+e+"' is unknown.");var t=l.get(e),n=[];return elBySelAll(".item > span",t.list,function(e){n.push({objectId:~~elData(e,"object-id"),value:e.textContent})}),n},setValues:function(e,t){if(!l.has(e))throw new Error("Element id '"+e+"' is unknown.");var n,o,a=l.get(e),r=i.childrenByClass(a.list,"item");for(n=0,o=r.length;n<o;n++)this._removeItem(null,r[n],!0);for(n=0,o=t.length;n<o;n++)this._addItem(e,t[n])},_setup:function(){c||(c=!0,u=this._keyDown.bind(this),d=this._keyPress.bind(this),h=this._keyUp.bind(this),f=this._paste.bind(this),p=this._removeItem.bind(this),g=this._blur.bind(this))},_createUI:function(e,t){var n=elCreate("ol");n.className="inputItemList",elData(n,"element-id",e.id),n.addEventListener(WCF_CLICK_EVENT,function(t){t.target===n&&e.focus()});var i=elCreate("li");i.className="input",n.appendChild(i),e.addEventListener("keydown",u),e.addEventListener("keypress",d),e.addEventListener("keyup",h),e.addEventListener("paste",f),e.addEventListener("blur",g),e.parentNode.insertBefore(n,e),i.appendChild(e),-1!==t.maxLength&&elAttr(e,"maxLength",t.maxLength);var o=null,a=[];if(t.isCSV){o=elCreate("input"),o.className="itemListInputShadow",o.type="hidden",o.name=e.name,e.removeAttribute("name"),n.parentNode.insertBefore(o,n);for(var r,s=e.value.split(","),l=0,c=s.length;l<c;l++)r=s[l].trim(),r.length&&a.push(r);if("TEXTAREA"===e.nodeName){var p=elCreate("input");p.type="text",e.parentNode.insertBefore(p,e),p.id=e.id,elRemove(e),e=p}}return{element:e,list:n,shadow:o,values:a}},_acceptsNewItems:function(e){var t=l.get(e);return-1===t.options.maxItems||t.list.childElementCount-1<t.options.maxItems},_handleLimit:function(e){var t=l.get(e);this._acceptsNewItems(e)?t.element.disabled&&(t.element.disabled=!1,t.element.removeAttribute("placeholder")):t.element.disabled||(t.element.disabled=!0,elAttr(t.element,"placeholder",n.get("wcf.global.form.input.maxItems")))},_keyDown:function(e){var t=e.currentTarget,n=t.parentNode.previousElementSibling;s=t.id,8===e.keyCode?0===t.value.length&&null!==n&&(n.classList.contains("active")?this._removeItem(null,n):n.classList.add("active")):27===e.keyCode&&null!==n&&n.classList.contains("active")&&n.classList.remove("active")},_keyPress:function(e){if(o.Enter(e)||o.Comma(e)){if(e.preventDefault(),l.get(e.currentTarget.id).options.restricted)return;var t=e.currentTarget.value.trim();t.length&&this._addItem(e.currentTarget.id,{objectId:0,value:t})}},_paste:function(e){var t="";t="object"==typeof window.clipboardData?window.clipboardData.getData("Text"):e.clipboardData.getData("text/plain");var n=e.currentTarget,i=n.id,o=~~elAttr(n,"maxLength");t.split(/,/).forEach(function(e){e=e.trim(),o&&e.length>o&&(e=e.substr(0,o)),e.length>0&&this._acceptsNewItems(i)&&this._addItem(i,{objectId:0,value:e})}.bind(this)),e.preventDefault()},_keyUp:function(e){var t=e.currentTarget;if(t.value.length>0){var n=t.parentNode.previousElementSibling;null!==n&&n.classList.remove("active")}},_addItem:function(e,t){var n=l.get(e),i=elCreate("li");i.className="item";var o=elCreate("span");o.className="content",elData(o,"object-id",t.objectId),o.textContent=t.value;var a=elCreate("a");a.className="icon icon16 fa-times",a.addEventListener(WCF_CLICK_EVENT,p),i.appendChild(o),i.appendChild(a),n.list.insertBefore(i,n.listItem),n.suggestion.addExcludedValue(t.value),n.element.value="",this._handleLimit(e);var r=this._syncShadow(n);"function"==typeof n.options.callbackChange&&(null===r&&(r=this.getValues(e)),n.options.callbackChange(e,r))},_removeItem:function(e,t,n){t=null===e?t:e.currentTarget.parentNode;var i=t.parentNode,o=elData(i,"element-id"),a=l.get(o);a.suggestion.removeExcludedValue(t.children[0].textContent),i.removeChild(t),n||a.element.focus(),this._handleLimit(o);var r=this._syncShadow(a);"function"==typeof a.options.callbackChange&&(null===r&&(r=this.getValues(o)),a.options.callbackChange(o,r))},_syncShadow:function(e){if(!e.options.isCSV)return null;for(var t="",n=this.getValues(e.element.id),i=0,o=n.length;i<o;i++)t+=(t.length?",":"")+n[i].value;return e.shadow.value=t,n},_blur:function(e){var t=l.get(e.currentTarget.id);if(!t.options.restricted){var n=e.currentTarget;window.setTimeout(function(){var e=n.value.trim();e.length&&(t.suggestion&&t.suggestion.isActive()||this._addItem(n.id,{objectId:0,value:e}))}.bind(this),100)}}}}),define("WoltLabSuite/Core/Ui/Page/JumpTo",["Language","ObjectMap","Ui/Dialog"],function(e,t,n){"use strict";var i=null,o=null,a=null,r=new t,s=null;return{init:function(e,t){if(null===(t=t||null)){var n=elData(e,"link");t=n?function(e){window.location=n.replace(/pageNo=%d/,"pageNo="+e)}:function(){}}else if("function"!=typeof t)throw new TypeError("Expected a valid function for parameter 'callback'.");r.has(e)||elBySelAll(".jumpTo",e,function(n){n.addEventListener(WCF_CLICK_EVENT,this._click.bind(this,e)),r.set(e,{callback:t})}.bind(this))},_click:function(t,o){i=t,"object"==typeof o&&o.preventDefault(),n.open(this);var r=elData(t,"pages");s.value=r,s.setAttribute("max",r),s.select(),a.textContent=e.get("wcf.page.jumpTo.description").replace(/#pages#/,r)},_keyUp:function(e){if(13===e.which&&!1===o.disabled)return void this._submit();var t=~~s.value;t<1||t>~~elAttr(s,"max")?o.disabled=!0:o.disabled=!1},_submit:function(e){r.get(i).callback(~~s.value),n.close(this)},_dialogSetup:function(){var t='<dl><dt><label for="jsPaginationPageNo">'+e.get("wcf.page.jumpTo")+'</label></dt><dd><input type="number" id="jsPaginationPageNo" value="1" min="1" max="1" class="tiny"><small></small></dd></dl><div class="formSubmit"><button class="buttonPrimary">'+e.get("wcf.global.button.submit")+"</button></div>";return{id:"paginationOverlay",options:{onSetup:function(e){s=elByTag("input",e)[0],s.addEventListener("keyup",this._keyUp.bind(this)),a=elByTag("small",e)[0],o=elByTag("button",e)[0],o.addEventListener(WCF_CLICK_EVENT,this._submit.bind(this))}.bind(this),title:e.get("wcf.global.page.pagination")},source:t}}}}),define("WoltLabSuite/Core/Ui/Pagination",["Core","Language","ObjectMap","StringUtil","WoltLabSuite/Core/Ui/Page/JumpTo"],function(e,t,n,i,o){"use strict";function a(e,t){this.init(e,t)}return a.prototype={SHOW_LINKS:11,init:function(t,n){this._element=t,this._options=e.extend({activePage:1,maxPage:1,callbackShouldSwitch:null,callbackSwitch:null},n),"function"!=typeof this._options.callbackShouldSwitch&&(this._options.callbackShouldSwitch=null),"function"!=typeof this._options.callbackSwitch&&(this._options.callbackSwitch=null),this._element.classList.add("pagination"),this._rebuild(this._element)},_rebuild:function(){var e=!1;this._element.innerHTML="";var n,i=elCreate("ul"),a=elCreate("li");a.className="skip",i.appendChild(a);var r="icon icon24 fa-chevron-left";this._options.activePage>1?(n=elCreate("a"),n.className=r+" jsTooltip",n.href="#",n.title=t.get("wcf.global.page.previous"),a.appendChild(n),n.addEventListener(WCF_CLICK_EVENT,this.switchPage.bind(this,this._options.activePage-1))):(a.innerHTML='<span class="'+r+'"></span>',a.classList.add("disabled")),i.appendChild(this._createLink(1));var s=this.SHOW_LINKS-4,l=this._options.activePage-2;l<0&&(l=0);var c=this._options.maxPage-(this._options.activePage+1);c<0&&(c=0),this._options.activePage>1&&this._options.activePage<this._options.maxPage&&s--;var u=s/2,d=this._options.activePage,h=this._options.activePage;d<1&&(d=1),h<1&&(h=1),h>this._options.maxPage-1&&(h=this._options.maxPage-1),l>=u?d-=u:(d-=l,h+=u-l),c>=u?h+=u:(h+=c,d-=u-c),h=Math.ceil(h),d=Math.ceil(d),d<1&&(d=1),h>this._options.maxPage&&(h=this._options.maxPage);var f='<a class="jsTooltip" title="'+t.get("wcf.page.jumpTo")+'">…</a>';d>1&&(d-1<2?i.appendChild(this._createLink(2)):(a=elCreate("li"),a.className="jumpTo",a.innerHTML=f,i.appendChild(a),e=!0));for(var p=d+1;p<h;p++)i.appendChild(this._createLink(p));h<this._options.maxPage&&(this._options.maxPage-h<2?i.appendChild(this._createLink(this._options.maxPage-1)):(a=elCreate("li"),a.className="jumpTo",a.innerHTML=f,i.appendChild(a),e=!0)),i.appendChild(this._createLink(this._options.maxPage)),a=elCreate("li"),a.className="skip",i.appendChild(a),r="icon icon24 fa-chevron-right",this._options.activePage<this._options.maxPage?(n=elCreate("a"),n.className=r+" jsTooltip",n.href="#",n.title=t.get("wcf.global.page.next"),a.appendChild(n),n.addEventListener(WCF_CLICK_EVENT,this.switchPage.bind(this,this._options.activePage+1))):(a.innerHTML='<span class="'+r+'"></span>',a.classList.add("disabled")),e&&(elData(i,"pages",this._options.maxPage),o.init(i,this.switchPage.bind(this))),this._element.appendChild(i)},_createLink:function(e){var n=elCreate("li");if(e!==this._options.activePage){var o=elCreate("a");o.textContent=i.addThousandsSeparator(e),o.addEventListener(WCF_CLICK_EVENT,this.switchPage.bind(this,e)),n.appendChild(o)}else n.classList.add("active"),n.innerHTML="<span>"+i.addThousandsSeparator(e)+'</span><span class="invisible">'+t.get("wcf.page.pagePosition",{pageNo:e,pages:this._options.maxPage})+"</span>";return n},getActivePage:function(){return this._options.activePage},getElement:function(){return this._element},getMaxPage:function(){return this._options.maxPage},switchPage:function(t,n){if("object"==typeof n&&(n.preventDefault(),n.currentTarget&&elData(n.currentTarget,"tooltip"))){var i=elById("balloonTooltip");i&&(e.triggerEvent(n.currentTarget,"mouseleave"),i.style.removeProperty("top"),i.style.removeProperty("bottom"))}if((t=~~t)>0&&this._options.activePage!==t&&t<=this._options.maxPage){if(null!==this._options.callbackShouldSwitch&&!0!==this._options.callbackShouldSwitch(t))return;this._options.activePage=t,this._rebuild(),null!==this._options.callbackSwitch&&this._options.callbackSwitch(t)}}},a}),define("WoltLabSuite/Core/Ui/Scroll",["Dom/Util"],function(e){"use strict";var t=null,n=null,i=null,o=null;return{element:function(o,a){if(!(o instanceof Element))throw new TypeError("Expected a valid DOM element.");if(void 0!==a&&"function"!=typeof a)throw new TypeError("Expected a valid callback function.");if(!document.body.contains(o))throw new Error("Element must be part of the visible DOM.");if(null!==t)throw new Error("Cannot scroll to element, a concurrent request is running.");a&&(t=a,null===n&&(n=this._onScroll.bind(this)),window.addEventListener("scroll",n));var r=e.offset(o).top;if(null===i){i=50;var s=elById("pageHeaderPanel");if(null!==s){var l=window.getComputedStyle(s).position;i="fixed"===l||"static"===l?s.offsetHeight:0}}i>0&&(r<=i?r=0:r-=i);var c=window.pageYOffset;window.scrollTo({left:0,top:r,behavior:"smooth"}),window.setTimeout(function(){c===window.pageYOffset&&this._onScroll()}.bind(this),100)},_onScroll:function(){null!==o&&window.clearTimeout(o),o=window.setTimeout(function(){null!==t&&t(),window.removeEventListener("scroll",n),t=null,o=null},100)}}}),define("WoltLabSuite/Core/Media/List/Upload",["Core","Dom/Util","../Upload"],function(e,t,n){"use strict";var i=function(){};return i.prototype={_createButton:function(){},_success:function(){},_upload:function(){},_createFileElement:function(){},_getParameters:function(){},_uploadFiles:function(){},_createFileElements:function(){},_failure:function(){},_insertButton:function(){},_progress:function(){},_removeButton:function(){}},i}),define("WoltLabSuite/Core/Controller/Media/List",["Dom/ChangeListener","EventHandler","WoltLabSuite/Core/Controller/Clipboard","WoltLabSuite/Core/Media/Editor","WoltLabSuite/Core/Media/List/Upload"],function(e,t,n,i,o){"use strict";var a=function(){};return a.prototype={init:function(){},_clipboardAction:function(){},_edit:function(){}},a}),define("WoltLabSuite/Core/Controller/Notice/Dismiss",["Ajax"],function(e){"use strict";return{setup:function(){var e=elByClass("jsDismissNoticeButton");if(e.length)for(var t=this._click.bind(this),n=0,i=e.length;n<i;n++)e[n].addEventListener(WCF_CLICK_EVENT,t)},_click:function(t){var n=t.currentTarget;e.apiOnce({data:{actionName:"dismiss",className:"wcf\\data\\notice\\NoticeAction",objectIDs:[elData(n,"object-id")]},success:function(){elRemove(n.parentNode)}})}}}),define("WoltLabSuite/Core/Media/Manager/Search",["Ajax","Core","Dom/Traverse","Dom/Util","EventKey","Language","Ui/SimpleDropdown"],function(e,t,n,i,o,a,r){"use strict";var s=function(){};return s.prototype={_ajaxSetup:function(){},_ajaxSuccess:function(){},_cancelSearch:function(){},_keyPress:function(){},_search:function(){},hideSearch:function(){},resetSearch:function(){},showSearch:function(){}},s}),define("WoltLabSuite/Core/Media/Manager/Base",["Core","Dictionary","Dom/ChangeListener","Dom/Traverse","Dom/Util","EventHandler","Language","List","Permission","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Controller/Clipboard","WoltLabSuite/Core/Media/Editor","WoltLabSuite/Core/Media/Upload","WoltLabSuite/Core/Media/Manager/Search","StringUtil","WoltLabSuite/Core/Ui/Pagination"],function(e,t,n,i,o,a,r,s,l,c,u,d,h,f,p,g,m){"use strict";var v=function(){};return v.prototype={_addButtonEventListeners:function(){},_click:function(){},_clipboardAction:function(){},_dialogClose:function(){},_dialogInit:function(){},_dialogSetup:function(){},_dialogShow:function(){},_editMedia:function(){},_editorClose:function(){},_editorSuccess:function(){},_removeClipboardCheckboxes:function(){},_setMedia:function(){},addMedia:function(){},getDialog:function(){},getMode:function(){},getOption:function(){},removeMedia:function(){},resetMedia:function(){},setMedia:function(){},setupMediaElement:function(){}},v}),define("WoltLabSuite/Core/Media/Manager/Editor",["Core","Dictionary","Dom/Traverse","EventHandler","Language","Permission","Ui/Dialog","WoltLabSuite/Core/Controller/Clipboard","WoltLabSuite/Core/Media/Manager/Base"],function(e,t,n,i,o,a,r,s,l){"use strict";var c=function(){};return c.prototype={_addButtonEventListeners:function(){},_buildInsertDialog:function(){},_click:function(){},_clipboardAction:function(){},_getInsertDialogId:function(){},_getThumbnailSizes:function(){},_insertMedia:function(){},_insertMediaGallery:function(){},_insertMediaItem:function(){},_openInsertDialog:function(){},insertMedia:function(){},getMode:function(){},setupMediaElement:function(){},_dialogClose:function(){},_dialogInit:function(){},_dialogSetup:function(){},_dialogShow:function(){},_editMedia:function(){},_editorClose:function(){},_editorSuccess:function(){},_removeClipboardCheckboxes:function(){},_setMedia:function(){},addMedia:function(){},getDialog:function(){},getOption:function(){},removeMedia:function(){},resetMedia:function(){},setMedia:function(){}},c}),define("WoltLabSuite/Core/Media/Manager/Select",["Core","Dom/Traverse","Dom/Util","Language","ObjectMap","Ui/Dialog","WoltLabSuite/Core/FileUtil","WoltLabSuite/Core/Media/Manager/Base"],function(e,t,n,i,o,a,r,s){"use strict";var l=function(){};return l.prototype={_addButtonEventListeners:function(){},_chooseMedia:function(){},_click:function(){},getMode:function(){},setupMediaElement:function(){},_removeMedia:function(){},_clipboardAction:function(){},_dialogClose:function(){},_dialogInit:function(){},_dialogSetup:function(){},_dialogShow:function(){},_editMedia:function(){},_editorClose:function(){},_editorSuccess:function(){},_removeClipboardCheckboxes:function(){},_setMedia:function(){},addMedia:function(){},getDialog:function(){},getOption:function(){},removeMedia:function(){},resetMedia:function(){},setMedia:function(){}},l}),define("WoltLabSuite/Core/Ui/Search/Input",["Ajax","Core","EventKey","Dom/Util","Ui/SimpleDropdown"],function(e,t,n,i,o){"use strict";function a(e,t){this.init(e,t)}return a.prototype={init:function(e,n){if(this._element=e,!(this._element instanceof Element))throw new TypeError("Expected a valid DOM element.");if("INPUT"!==this._element.nodeName||"search"!==this._element.type&&"text"!==this._element.type)throw new Error('Expected an input[type="text"].');this._activeItem=null,this._dropdownContainerId="",this._lastValue="",this._list=null,this._request=null,this._timerDelay=null,this._options=t.extend({ajax:{actionName:"getSearchResultList",className:"",interfaceName:"wcf\\data\\ISearchAction"},callbackDropdownInit:null,callbackSelect:null,delay:500,excludedSearchValues:[],minLength:3,noResultPlaceholder:"",preventSubmit:!1},n),elAttr(this._element,"autocomplete","off"),this._element.addEventListener("keydown",this._keydown.bind(this)),this._element.addEventListener("keyup",this._keyup.bind(this))},addExcludedSearchValues:function(e){-1===this._options.excludedSearchValues.indexOf(e)&&this._options.excludedSearchValues.push(e)},removeExcludedSearchValues:function(e){var t=this._options.excludedSearchValues.indexOf(e);-1!==t&&this._options.excludedSearchValues.splice(t,1)},_keydown:function(e){(null!==this._activeItem&&o.isOpen(this._dropdownContainerId)||this._options.preventSubmit)&&n.Enter(e)&&e.preventDefault(),(n.ArrowUp(e)||n.ArrowDown(e)||n.Escape(e))&&e.preventDefault()},_keyup:function(e){if(null!==this._activeItem)if(o.isOpen(this._dropdownContainerId)){if(n.ArrowUp(e))return e.preventDefault(),this._keyboardPreviousItem();if(n.ArrowDown(e))return e.preventDefault(),this._keyboardNextItem();if(n.Enter(e))return e.preventDefault(),this._keyboardSelectItem()}else this._activeItem=null;if(n.Escape(e))return void o.close(this._dropdownContainerId);var t=this._element.value.trim();if(this._lastValue!==t){if(this._lastValue=t,t.length<this._options.minLength)return void(this._dropdownContainerId&&(o.close(this._dropdownContainerId),this._activeItem=null));this._options.delay?(null!==this._timerDelay&&window.clearTimeout(this._timerDelay),this._timerDelay=window.setTimeout(function(){this._search(t)}.bind(this),this._options.delay)):this._search(t)}},_search:function(t){this._request&&this._request.abortPrevious(),this._request=e.api(this,this._getParameters(t))},_getParameters:function(e){return{parameters:{data:{excludedSearchValues:this._options.excludedSearchValues,searchString:e}}}},_keyboardNextItem:function(){this._activeItem.classList.remove("active"),this._activeItem.nextElementSibling?this._activeItem=this._activeItem.nextElementSibling:this._activeItem=this._list.children[0],this._activeItem.classList.add("active")},_keyboardPreviousItem:function(){this._activeItem.classList.remove("active"),this._activeItem.previousElementSibling?this._activeItem=this._activeItem.previousElementSibling:this._activeItem=this._list.children[this._list.childElementCount-1],this._activeItem.classList.add("active")},_keyboardSelectItem:function(){this._selectItem(this._activeItem)},_clickSelectItem:function(e){this._selectItem(e.currentTarget)},_selectItem:function(e){this._options.callbackSelect&&!1===this._options.callbackSelect(e)?this._element.value="":this._element.value=elData(e,"label"),this._activeItem=null,o.close(this._dropdownContainerId)},_ajaxSuccess:function(e){var t=!1;if(null===this._list?(this._list=elCreate("ul"),this._list.className="dropdownMenu",t=!0,"function"==typeof this._options.callbackDropdownInit&&this._options.callbackDropdownInit(this._list)):this._list.innerHTML="","object"==typeof e.returnValues){var n,a=this._clickSelectItem.bind(this);for(var r in e.returnValues)e.returnValues.hasOwnProperty(r)&&(n=this._createListItem(e.returnValues[r]),n.addEventListener(WCF_CLICK_EVENT,a),this._list.appendChild(n))}t&&(i.insertAfter(this._list,this._element),o.initFragment(this._element.parentNode,this._list),this._dropdownContainerId=i.identify(this._element.parentNode)),this._dropdownContainerId&&(this._activeItem=null,this._list.childElementCount||!1!==this._handleEmptyResult()?(o.open(this._dropdownContainerId),this._list.childElementCount&&~~elData(this._list.children[0],"object-id")&&(this._activeItem=this._list.children[0],this._activeItem.classList.add("active"))):o.close(this._dropdownContainerId))},_handleEmptyResult:function(){if(!this._options.noResultPlaceholder)return!1;var e=elCreate("li");e.className="dropdownText";var t=elCreate("span");return t.textContent=this._options.noResultPlaceholder,e.appendChild(t),this._list.appendChild(e),!0},_createListItem:function(e){var t=elCreate("li");elData(t,"object-id",e.objectID),elData(t,"label",e.label);var n=elCreate("span");return n.textContent=e.label,t.appendChild(n),t},_ajaxSetup:function(){return{data:this._options.ajax}}},a}),define("WoltLabSuite/Core/Ui/User/Search/Input",["Core","WoltLabSuite/Core/Ui/Search/Input"],function(e,t){"use strict";function n(e,t){this.init(e,t)}return e.inherit(n,t,{init:function(t,i){var o=e.isPlainObject(i)&&!0===i.includeUserGroups;i=e.extend({ajax:{className:"wcf\\data\\user\\UserAction",parameters:{data:{includeUserGroups:o?1:0}}}},i),n._super.prototype.init.call(this,t,i)},_createListItem:function(e){var t=n._super.prototype._createListItem.call(this,e);elData(t,"type",e.type);var i=elCreate("div");return i.className="box16",i.innerHTML="group"===e.type?'<span class="icon icon16 fa-users"></span>':e.icon,i.appendChild(t.children[0]),t.appendChild(i),t}}),n}),define("WoltLabSuite/Core/Ui/Acl/Simple",["Language","StringUtil","Dom/ChangeListener","WoltLabSuite/Core/Ui/User/Search/Input"],function(e,t,n,i){"use strict";var o=function(){};return o.prototype={init:function(){},_build:function(){},_select:function(){},_removeItem:function(){}},o}),define("WoltLabSuite/Core/Ui/Article/MarkAllAsRead",["Ajax"],function(e){"use strict";return{init:function(){elBySelAll(".markAllAsReadButton",void 0,function(e){e.addEventListener(WCF_CLICK_EVENT,this._click.bind(this))}.bind(this))},_click:function(t){t.preventDefault(),e.api(this)},_ajaxSuccess:function(){var e=elBySel(".mainMenu .active .badge");e&&elRemove(e),elBySelAll(".articleList .newMessageBadge",void 0,elRemove)},_ajaxSetup:function(){return{data:{actionName:"markAllAsRead",className:"wcf\\data\\article\\ArticleAction"}}}}}),define("WoltLabSuite/Core/Ui/Color/Picker",["Core"],function(e){"use strict";function t(e,t){this.init(e,t)}var n=function(e,t){if("object"==typeof window.WCF&&"function"==typeof window.WCF.ColorPicker)return(n=function(e,t){var n=new window.WCF.ColorPicker(e);return"function"==typeof t.callbackSubmit&&n.setCallbackSubmit(t.callbackSubmit),n})(e,t);0===i.length&&(window.__wcf_bc_colorPickerInit=function(){i.forEach(function(e){n(e[0],e[1])}),window.__wcf_bc_colorPickerInit=void 0,i=[]}),i.push([e,t])},i=[];return t.prototype={init:function(t,i){if(!(t instanceof Element))throw new TypeError("Expected a valid DOM element, use `UiColorPicker.fromSelector()` if you want to use a CSS selector.");this._options=e.extend({callbackSubmit:null},i),n(t,i)}},t.fromSelector=function(e){elBySelAll(e,void 0,function(e){new t(e)})},t}),define("WoltLabSuite/Core/Ui/Comment/Add",["Ajax","Core","EventHandler","Language","Dom/ChangeListener","Dom/Util","Dom/Traverse","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Ui/Scroll","EventKey","User","WoltLabSuite/Core/Controller/Captcha"],function(e,t,n,i,o,a,r,s,l,c,u,d,h){"use strict";var f=function(){};return f.prototype={init:function(){},_submitGuestDialog:function(){},_submit:function(){},_getParameters:function(){},_validate:function(){},throwError:function(){},_showLoadingOverlay:function(){},_hideLoadingOverlay:function(){},_reset:function(){},_handleError:function(){},_getEditor:function(){},_insertMessage:function(){},_ajaxSuccess:function(){},_ajaxFailure:function(){},_ajaxSetup:function(){},_cancelGuestDialog:function(){}},f}),define("WoltLabSuite/Core/Ui/Comment/Edit",["Ajax","Core","Dictionary","Environment","EventHandler","Language","List","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Notification","Ui/ReusableDropdown","WoltLabSuite/Core/Ui/Scroll"],function(e,t,n,i,o,a,r,s,l,c,u,d,h){"use strict";var f=function(){};return f.prototype={init:function(){},rebuild:function(){},_click:function(){},_prepare:function(){},_showEditor:function(){},_restoreMessage:function(){},_save:function(){},_validate:function(){},throwError:function(){},_showMessage:function(){},_hideEditor:function(){},_restoreEditor:function(){},_destroyEditor:function(){},_getEditorId:function(){},_getObjectId:function(){},_ajaxFailure:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){}},f}),define("WoltLabSuite/Core/Ui/ItemList/Filter",["Core","EventKey","Language","List","StringUtil","Dom/Util","Ui/SimpleDropdown"],function(e,t,n,i,o,a,r){"use strict";var s=function(){};return s.prototype={init:function(){},_buildItems:function(){},_prepareItem:function(){},_keyup:function(){},_toggleVisibility:function(){},_setupVisibilityFilter:function(){},_setVisibility:function(){}},s}),define("WoltLabSuite/Core/Ui/ItemList/User",["WoltLabSuite/Core/Ui/ItemList"],function(e){"use strict";var t=function(){};return t.prototype={init:function(){},getValues:function(){}},t}),define("WoltLabSuite/Core/Ui/User/List",["Ajax","Core","Dictionary","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ui/Pagination"],function(e,t,n,i,o,a){"use strict";function r(e){this.init(e)}return r.prototype={init:function(e){this._cache=new n,this._pageCount=0,this._pageNo=1,this._options=t.extend({className:"",dialogTitle:"",parameters:{}},e)},open:function(){this._pageNo=1,this._showPage()},_showPage:function(t){if("number"==typeof t&&(this._pageNo=~~t),0!==this._pageCount&&(this._pageNo<1||this._pageNo>this._pageCount))throw new RangeError("pageNo must be between 1 and "+this._pageCount+" ("+this._pageNo+" given).");if(this._cache.has(this._pageNo)){var n=o.open(this,this._cache.get(this._pageNo));if(this._pageCount>1){var i=elBySel(".jsPagination",n.content);null!==i&&new a(i,{activePage:this._pageNo,maxPage:this._pageCount,callbackSwitch:this._showPage.bind(this)});var r=n.content.parentNode;r.scrollTop>0&&(r.scrollTop=0)}}else this._options.parameters.pageNo=this._pageNo,e.api(this,{parameters:this._options.parameters})},_ajaxSuccess:function(e){void 0!==e.returnValues.pageCount&&(this._pageCount=~~e.returnValues.pageCount),this._cache.set(this._pageNo,e.returnValues.template),this._showPage()},_ajaxSetup:function(){return{data:{actionName:"getGroupedUserList",className:this._options.className,interfaceName:"wcf\\data\\IGroupedUserListAction"}}},_dialogSetup:function(){return{id:i.getUniqueId(),options:{title:this._options.dialogTitle},source:null}}},r}),define("WoltLabSuite/Core/Ui/Like/Handler",["Ajax","Core","Dictionary","Language","ObjectMap","StringUtil","Dom/ChangeListener","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ui/User/List","User"],function(e,t,n,i,o,a,r,s,l,c,u){"use strict";function d(e,t){this.init(e,t)}var h=!1;return d.prototype={init:function(e,n){if(""===n.containerSelector)throw new Error("[WoltLabSuite/Core/Ui/Like/Handler] Expected a non-empty string for option 'containerSelector'.");this._containers=new o,this._details=new o,this._objectType=e,this._options=t.extend({badgeClassNames:"",isSingleItem:!1,markListItemAsActive:!1,renderAsButton:!0,summaryPrepend:!0,summaryUseIcon:!0,canDislike:!1,canLike:!1,canLikeOwnContent:!1,canViewSummary:!1,badgeContainerSelector:".messageHeader .messageStatus",buttonAppendToSelector:".messageFooter .messageFooterButtons",buttonBeforeSelector:"",containerSelector:"",summarySelector:".messageFooterGroup"},n),this.initContainers(n,e),r.add("WoltLabSuite/Core/Ui/Like/Handler-"+e,this.initContainers.bind(this))},initContainers:function(){for(var e,t,n=elBySelAll(this._options.containerSelector),i=!1,o=0,a=n.length;o<a;o++)e=n[o],this._containers.has(e)||(t={badge:null,dislikeButton:null,likeButton:null,summary:null,dislikes:~~elData(e,"like-dislikes"),liked:~~elData(e,"like-liked"),likes:~~elData(e,"like-likes"),objectId:~~elData(e,"object-id"),users:JSON.parse(elData(e,"like-users"))},this._containers.set(e,t),this._buildWidget(e,t),i=!0);i&&r.trigger()},_buildWidget:function(e,t){if(this._options.canViewSummary){var n,i,o,a=this._options.isSingleItem?elBySel(this._options.summarySelector):elBySel(this._options.summarySelector,e);null!==a&&(n=elCreate("div"),n.className="likesSummary",this._options.summaryUseIcon&&(o=elCreate("span"),o.className="icon icon16 fa-thumbs-o-up",n.appendChild(o)),i=elCreate("span"),i.className="likesSummaryContent",i.addEventListener(WCF_CLICK_EVENT,this._showSummary.bind(this,e)),n.appendChild(i),this._options.summaryPrepend?s.prepend(n,a):a.appendChild(n),t.summary=i,this._updateSummary(e))}var r,l,c=this._options.isSingleItem?elBySel(this._options.badgeContainerSelector):elBySel(this._options.badgeContainerSelector,e);if(null!==c&&(r=elCreate("a"),r.href="#",r.className="wcfLikeCounter jsTooltip"+(this._options.badgeClassNames?" "+this._options.badgeClassNames:""),r.addEventListener(WCF_CLICK_EVENT,this._showSummary.bind(this,e)),"OL"===c.nodeName||"UL"===c.nodeName?(l=elCreate("li"),l.appendChild(r),c.appendChild(l)):c.appendChild(r),t.badge=r,this._updateBadge(e)),this._options.canLike&&(u.userId!=elData(e,"user-id")||this._options.canLikeOwnContent)){var d=this._options.buttonAppendToSelector?this._options.isSingleItem?elBySel(this._options.buttonAppendToSelector):elBySel(this._options.buttonAppendToSelector,e):null,h=this._options.buttonBeforeSelector?this._options.isSingleItem?elBySel(this._options.buttonBeforeSelector):elBySel(this._options.buttonBeforeSelector,e):null;if(null===h&&null===d)throw new Error("Unable to find insert location for like/dislike buttons.");t.likeButton=this._createButton(e,!0,h,d),this._options.canDislike&&(t.dislikeButton=this._createButton(e,!1,h,d)),this._updateActiveState(e)}},_createButton:function(e,t,n,o){var a=i.get("wcf.like.button."+(t?"like":"dislike")),r=elCreate("li");r.className="wcf"+(t?"Like":"Dislike")+"Button";var s=elCreate("a");return s.className="jsTooltip"+(this._options.renderAsButton?" button":""),s.href="#",s.title=a,s.innerHTML='<span class="icon icon16 fa-thumbs-o-'+(t?"up":"down")+'"></span> <span class="invisible">'+a+"</span>",s.addEventListener(WCF_CLICK_EVENT,this._like.bind(this,e)),elData(s,"type",t?"like":"dislike"),r.appendChild(s),n?n.parentNode.insertBefore(r,n):o.appendChild(r),s},_showSummary:function(e,t){t.preventDefault(),this._details.has(e)||this._details.set(e,new c({className:"wcf\\data\\like\\LikeAction",dialogTitle:i.get("wcf.like.details"),parameters:{data:{containerID:s.identify(e),objectID:this._containers.get(e).objectId,objectType:this._objectType}}})),this._details.get(e).open()},_updateBadge:function(e){var t=this._containers.get(e)
-;if(0===t.likes&&0===t.dislikes)elHide(t.badge);else{elShow(t.badge),t.badge.classList.remove("likeCounterLiked","likeCounterDisliked");var n=t.likes-t.dislikes,o='<span class="icon icon16 fa-thumbs-o-'+(n<0?"down":"up")+'"></span><span class="wcfLikeValue">';n>0?(o+="+"+a.addThousandsSeparator(n),t.badge.classList.add("likeCounterLiked")):n<0?(o+="−"+a.addThousandsSeparator(Math.abs(n)),t.badge.classList.add("likeCounterDisliked")):o+="±0",t.badge.innerHTML=o+"</span>",t.badge.setAttribute("data-tooltip",i.get("wcf.like.tooltip",{dislikes:t.dislikes,likes:t.likes}))}},_updateSummary:function(e){var t=this._containers.get(e);if(t.likes){elShow(t.summary.parentNode);for(var n=[],o=Object.keys(t.users),a=0,r=o.length;a<r;a++)n.push(t.users[o[a]]);var s=t.likes-n.length;t.summary.innerHTML=i.get("wcf.like.summary",{users:n,others:s})}else elHide(t.summary.parentNode)},_updateActiveState:function(e){var t=this._containers.get(e),n=this._options.markListItemAsActive?t.likeButton.parentNode:t.likeButton;if(n.classList.remove("active"),1===t.liked&&n.classList.add("active"),this._options.canDislike){var i=this._options.markListItemAsActive?t.dislikeButton.parentNode:t.dislikeButton;i.classList.remove("active"),-1===t.liked&&i.classList.add("active")}},_like:function(t,n){n.preventDefault(),h||(h=!0,e.api(this,{actionName:elData(n.currentTarget,"type"),parameters:{data:{containerID:s.identify(t),objectID:this._containers.get(t).objectId,objectType:this._objectType}}}))},_ajaxSuccess:function(e){var t=elById(e.returnValues.containerID),n=this._containers.get(t);if(void 0!==n){n.dislikes=~~e.returnValues.dislikes,n.likes=~~e.returnValues.likes;var i=e.returnValues.users;n.users=[];for(var o=Object.keys(i),r=0,s=o.length;r<s;r++)n.users.push(a.escapeHTML(i[o[r]].username));1==e.returnValues.isLiked?n.liked=1:1==e.returnValues.isDisliked?n.liked=-1:n.liked=0,this._updateBadge(t),this._options.canViewSummary&&this._updateSummary(t),this._updateActiveState(t),this._details.delete(t),h=!1}},_ajaxSetup:function(){return{data:{className:"wcf\\data\\like\\LikeAction"}}}},d}),define("WoltLabSuite/Core/Ui/Message/InlineEditor",["Ajax","Core","Dictionary","Environment","EventHandler","Language","ObjectMap","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Notification","Ui/ReusableDropdown","WoltLabSuite/Core/Ui/Scroll"],function(e,t,n,i,o,a,r,s,l,c,u,d,h){"use strict";var f=function(){};return f.prototype={init:function(){},rebuild:function(){},_click:function(){},_clickDropdown:function(){},_dropdownBuild:function(){},_dropdownToggle:function(){},_dropdownGetItems:function(){},_dropdownOpen:function(){},_dropdownSelect:function(){},_clickDropdownItem:function(){},_prepare:function(){},_showEditor:function(){},_restoreMessage:function(){},_save:function(){},_validate:function(){},throwError:function(){},_showMessage:function(){},_hideEditor:function(){},_restoreEditor:function(){},_destroyEditor:function(){},_getHash:function(){},_updateHistory:function(){},_getEditorId:function(){},_getObjectId:function(){},_ajaxFailure:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){},legacyEdit:function(){}},f}),define("WoltLabSuite/Core/Ui/Message/Manager",["Ajax","Core","Dictionary","Language","Dom/ChangeListener","Dom/Util"],function(e,t,n,i,o,a){"use strict";var r=function(){};return r.prototype={init:function(){},rebuild:function(){},getPermission:function(){},getPropertyValue:function(){},update:function(){},updateItems:function(){},updateAllItems:function(){},setNote:function(){},_update:function(){},_updateState:function(){},_toggleMessageStatus:function(){},_getAttributeName:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){}},r}),define("WoltLabSuite/Core/Ui/Message/Reply",["Ajax","Core","EventHandler","Language","Dom/ChangeListener","Dom/Util","Dom/Traverse","Ui/Dialog","Ui/Notification","WoltLabSuite/Core/Ui/Scroll","EventKey","User","WoltLabSuite/Core/Controller/Captcha"],function(e,t,n,i,o,a,r,s,l,c,u,d,h){"use strict";var f=function(){};return f.prototype={init:function(){},_submitGuestDialog:function(){},_submit:function(){},_validate:function(){},throwError:function(){},_showLoadingOverlay:function(){},_hideLoadingOverlay:function(){},_reset:function(){},_handleError:function(){},_getEditor:function(){},_insertMessage:function(){},_ajaxSuccess:function(){},_ajaxFailure:function(){},_ajaxSetup:function(){}},f}),define("WoltLabSuite/Core/Ui/Message/Share",["EventHandler"],function(e){"use strict";return{_pageDescription:"",_pageUrl:"",init:function(){var t=elBySel('meta[property="og:title"]');null!==t&&(this._pageDescription=encodeURIComponent(t.content));var n=elBySel('meta[property="og:url"]');null!==n&&(this._pageUrl=encodeURIComponent(n.content)),elBySelAll(".jsMessageShareButtons",null,function(t){t.classList.remove("jsMessageShareButtons");var n={facebook:{link:elBySel(".jsShareFacebook",t),share:function(){this._share("facebook","https://www.facebook.com/sharer.php?u={pageURL}&t={text}",!0)}.bind(this)},google:{link:elBySel(".jsShareGoogle",t),share:function(){this._share("google","https://plus.google.com/share?url={pageURL}",!1)}.bind(this)},reddit:{link:elBySel(".jsShareReddit",t),share:function(){this._share("reddit","https://ssl.reddit.com/submit?url={pageURL}",!1)}.bind(this)},twitter:{link:elBySel(".jsShareTwitter",t),share:function(){this._share("twitter","https://twitter.com/share?url={pageURL}&text={text}",!1)}.bind(this)},linkedIn:{link:elBySel(".jsShareLinkedIn",t),share:function(){this._share("linkedIn","https://www.linkedin.com/cws/share?url={pageURL}",!1)}.bind(this)},pinterest:{link:elBySel(".jsSharePinterest",t),share:function(){this._share("pinterest","https://www.pinterest.com/pin/create/link/?url={pageURL}&description={text}",!1)}.bind(this)},xing:{link:elBySel(".jsShareXing",t),share:function(){this._share("xing","https://www.xing.com/social_plugins/share?url={pageURL}",!1)}.bind(this)},whatsApp:{link:elBySel(".jsShareWhatsApp",t),share:function(){window.location.href="whatsapp://send?text="+this._pageDescription+"%20"+this._pageUrl}.bind(this)}};e.fire("com.woltlab.wcf.message.share","shareProvider",{container:t,providers:n,pageDescription:this._pageDescription,pageUrl:this._pageUrl});for(var i in n)n.hasOwnProperty(i)&&null!==n[i].link&&n[i].link.addEventListener(WCF_CLICK_EVENT,n[i].share)}.bind(this))},_share:function(e,t,n){window.open(t.replace(/\{pageURL}/,this._pageUrl).replace(/\{text}/,this._pageDescription+(n?"%20"+this._pageUrl:"")),e,"height=600,width=600")}}}),define("WoltLabSuite/Core/Ui/Page/Search",["Ajax","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog"],function(e,t,n,i,o,a){"use strict";var r=function(){};return r.prototype={open:function(){},_search:function(){},_click:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){},_dialogSetup:function(){}},r}),define("WoltLabSuite/Core/Ui/Redactor/Metacode",["EventHandler","Dom/Util"],function(e,t){"use strict";var n=function(){};return n.prototype={convert:function(){},convertFromHtml:function(){},_getOpeningTag:function(){},_getClosingTag:function(){},_getFirstParagraph:function(){},_getLastParagraph:function(){},_parseAttributes:function(){}},n}),define("WoltLabSuite/Core/Ui/Redactor/Autosave",["Core","Devtools","EventHandler","Language","Dom/Traverse","./Metacode"],function(e,t,n,i,o,a){"use strict";var r=function(){};return r.prototype={init:function(){},getInitialValue:function(){},getMetaData:function(){},watch:function(){},destroy:function(){},clear:function(){},createOverlay:function(){},hideOverlay:function(){},_saveToStorage:function(){},_cleanup:function(){}},r}),define("WoltLabSuite/Core/Ui/Redactor/PseudoHeader",[],function(){"use strict";var e=function(){};return e.prototype={getHeight:function(){}},e}),define("WoltLabSuite/Core/Ui/Redactor/Code",["EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./PseudoHeader"],function(e,t,n,i,o,a,r){"use strict";var s=function(){};return s.prototype={init:function(){},_bbcodeCode:function(){},_observeLoad:function(){},_edit:function(){},_setTitle:function(){},_delete:function(){},_dialogSetup:function(){},_dialogSubmit:function(){}},s}),define("WoltLabSuite/Core/Ui/Redactor/DragAndDrop",["Dictionary","EventHandler","Language"],function(e,t,n){"use strict";var i=function(){};return i.prototype={init:function(){},_dragOver:function(){},_drop:function(){},_dragLeave:function(){},_setup:function(){}},i}),define("WoltLabSuite/Core/Ui/Redactor/Format",["Dom/Util"],function(e){"use strict";var t=function(){};return t.prototype={format:function(){},removeFormat:function(){},_handleParentNodes:function(){},_getLastMatchingParent:function(){},_isBoundaryElement:function(){},_getSelectionMarker:function(){}},t}),define("WoltLabSuite/Core/Ui/Redactor/Html",["EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./PseudoHeader"],function(e,t,n,i,o,a,r){"use strict";var s=function(){};return s.prototype={init:function(){},_bbcodeCode:function(){},_observeLoad:function(){},_edit:function(){},_save:function(){},_setTitle:function(){},_delete:function(){},_dialogSetup:function(){}},s}),define("WoltLabSuite/Core/Ui/Redactor/Link",["Core","EventKey","Language","Ui/Dialog"],function(e,t,n,i){"use strict";var o=function(){};return o.prototype={showDialog:function(){},_submit:function(){},_dialogSetup:function(){}},o}),define("WoltLabSuite/Core/Ui/Redactor/Mention",["Ajax","Environment","StringUtil","Ui/CloseOverlay"],function(e,t,n,i){"use strict";var o=function(){};return o.prototype={init:function(){},_keyDown:function(){},_keyUp:function(){},_getTextLineInFrontOfCaret:function(){},_getDropdownMenuPosition:function(){},_setUsername:function(){},_selectMention:function(){},_updateDropdownPosition:function(){},_selectItem:function(){},_hideDropdown:function(){},_ajaxSetup:function(){},_ajaxSuccess:function(){}},o}),define("WoltLabSuite/Core/Ui/Redactor/Page",["WoltLabSuite/Core/Ui/Page/Search"],function(e){"use strict";var t=function(){};return t.prototype={init:function(){},_click:function(){},_insert:function(){}},t}),define("WoltLabSuite/Core/Ui/Redactor/Quote",["Core","EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./Metacode","./PseudoHeader"],function(e,t,n,i,o,a,r,s,l){"use strict";var c=function(){};return c.prototype={init:function(){},_insertQuote:function(){},_click:function(){},_observeLoad:function(){},_edit:function(){},_save:function(){},_setTitle:function(){},_delete:function(){},_dialogSetup:function(){},_dialogSubmit:function(){}},c}),define("WoltLabSuite/Core/Ui/Redactor/Spoiler",["EventHandler","EventKey","Language","StringUtil","Dom/Util","Ui/Dialog","./PseudoHeader"],function(e,t,n,i,o,a,r){"use strict";var s=function(){};return s.prototype={init:function(){},_bbcodeSpoiler:function(){},_observeLoad:function(){},_edit:function(){},_setTitle:function(){},_delete:function(){},_dialogSetup:function(){},_dialogSubmit:function(){}},s}),define("WoltLabSuite/Core/Ui/Redactor/Table",["Language","Ui/Dialog"],function(e,t){"use strict";var n=function(){};return n.prototype={showDialog:function(){},_submit:function(){},_dialogSetup:function(){}},n}),define("WoltLabSuite/Core/Ui/Search/Page",["Core","Dom/Traverse","Dom/Util","Ui/Screen","Ui/SimpleDropdown","./Input"],function(e,t,n,i,o,a){"use strict";return{init:function(r){var s=elById("pageHeaderSearchInput");new a(s,{ajax:{className:"wcf\\data\\search\\keyword\\SearchKeywordAction"},callbackDropdownInit:function(e){if(e.classList.add("dropdownMenuPageSearch"),i.is("screen-lg")){elData(e,"dropdown-alignment-horizontal","right");var t=s.clientWidth;e.style.setProperty("min-width",t+"px","");var o=s.parentNode,a=n.offset(o).left+o.clientWidth-(n.offset(s).left+t),r=n.styleAsInt(window.getComputedStyle(o),"padding-bottom");e.style.setProperty("transform","translateX(-"+Math.ceil(a)+"px) translateY(-"+r+"px)","")}},callbackSelect:function(){return setTimeout(function(){t.parentByTag(s,"FORM").submit()},1),!0}});var l=o.getDropdownMenu(n.identify(elBySel(".pageHeaderSearchType"))),c=this._click.bind(this);elBySelAll("a[data-object-type]",l,function(e){e.addEventListener(WCF_CLICK_EVENT,c)});var u=elBySel('a[data-object-type="'+r+'"]',l);e.triggerEvent(u,WCF_CLICK_EVENT)},_click:function(e){e.preventDefault();var t=elById("pageHeader");t.classList.add("searchBarForceOpen"),window.setTimeout(function(){t.classList.remove("searchBarForceOpen")},10);var n=elData(e.currentTarget,"object-type"),i=elById("pageHeaderSearchParameters");i.innerHTML="";var o=elData(e.currentTarget,"extended-link");o&&(elBySel(".pageHeaderSearchExtendedLink").href=o);var a=elData(e.currentTarget,"parameters");a=a?JSON.parse(a):{},n&&(a["types[]"]=n);for(var r in a)if(a.hasOwnProperty(r)){var s=elCreate("input");s.type="hidden",s.name=r,s.value=a[r],i.appendChild(s)}elBySel(".pageHeaderSearchType > .button",elById("pageHeaderSearchInputContainer")).textContent=e.currentTarget.textContent}}}),define("WoltLabSuite/Core/Ui/Sortable/List",["Core","Ui/Screen"],function(e,t){"use strict";var n=function(){};return n.prototype={init:function(){},_enable:function(){},_disable:function(){}},n}),define("WoltLabSuite/Core/Ui/Style/FontAwesome",["Language","Ui/Dialog","WoltLabSuite/Core/Ui/ItemList/Filter"],function(e,t,n){"use strict";var i=function(){};return i.prototype={setup:function(){},open:function(){},_click:function(){},_dialogSetup:function(){}},i}),define("WoltLabSuite/Core/Ui/Toggle/Input",["Core"],function(e){"use strict";function t(e,t){this.init(e,t)}return t.prototype={init:function(t,n){if(this._element=elBySel(t),null===this._element)throw new Error("Unable to find element by selector '"+t+"'.");var i="INPUT"===this._element.nodeName?elAttr(this._element,"type"):"";if("checkbox"!==i&&"radio"!==i)throw new Error("Illegal element, expected input[type='checkbox'] or input[type='radio'].");this._options=e.extend({hide:[],show:[]},n),["hide","show"].forEach(function(e){var t,n,i;for(n=0,i=this._options[e].length;n<i;n++)if("string"!=typeof(t=this._options[e][n])&&!(t instanceof Element))throw new TypeError("The array '"+e+"' may only contain string selectors or DOM elements.")}.bind(this)),this._element.addEventListener("change",this._change.bind(this)),this._handleElements(this._options.show,this._element.checked),this._handleElements(this._options.hide,!this._element.checked)},_change:function(e){var t=e.currentTarget.checked;this._handleElements(this._options.show,t),this._handleElements(this._options.hide,!t)},_handleElements:function(e,t){for(var n,i,o=0,a=e.length;o<a;o++){if("string"==typeof(n=e[o])){if(null===(i=elBySel(n)))throw new Error("Unable to find element by selector '"+n+"'.");e[o]=n=i}window[t?"elShow":"elHide"](n)}}},t}),define("WoltLabSuite/Core/Ui/User/Editor",["Ajax","Language","StringUtil","Dom/Util","Ui/Dialog","Ui/Notification"],function(e,t,n,i,o,a){"use strict";var r=function(){};return r.prototype={init:function(){},_click:function(){},_submit:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){},_dialogSetup:function(){}},r}),define("WoltLabSuite/Core/Controller/Condition/Page/Dependence",["Dom/ChangeListener","Dom/Traverse","EventHandler","ObjectMap"],function(e,t,n,i){"use strict";var o=function(){};return o.prototype={register:function(){},_checkVisibility:function(){},_hideDependentElement:function(){},_showDependentElement:function(){}},o}),define("WoltLabSuite/Core/Controller/Map/Route/Planner",["Dom/Traverse","Dom/Util","Language","Ui/Dialog","WoltLabSuite/Core/Ajax/Status"],function(e,t,n,i,o){function a(e,t){if(this._button=elById(e),null===this._button)throw new Error("Unknown button with id '"+e+"'");this._button.addEventListener("click",this._openDialog.bind(this)),this._destination=t}return a.prototype={_dialogSetup:function(){return{id:this._button.id+"Dialog",options:{onShow:this._initDialog.bind(this),title:n.get("wcf.map.route.planner")},source:'<div class="googleMapsDirectionsContainer" style="display: none;"><div class="googleMap"></div><div class="googleMapsDirections"></div></div><small class="googleMapsDirectionsGoogleLinkContainer"><a href="'+this._getGoogleMapsLink()+'" class="googleMapsDirectionsGoogleLink" target="_blank" style="display: none;">'+n.get("wcf.map.route.viewOnGoogleMaps")+"</a></small><dl><dt>"+n.get("wcf.map.route.origin")+'</dt><dd><input type="text" name="origin" class="long" autofocus /></dd></dl><dl style="display: none;"><dt>'+n.get("wcf.map.route.travelMode")+'</dt><dd><select name="travelMode"><option value="driving">'+n.get("wcf.map.route.travelMode.driving")+'</option><option value="walking">'+n.get("wcf.map.route.travelMode.walking")+'</option><option value="bicycling">'+n.get("wcf.map.route.travelMode.bicycling")+'</option><option value="transit">'+n.get("wcf.map.route.travelMode.transit")+"</option></select></dd></dl>"}},_calculateRoute:function(e){var t=i.getDialog(this).dialog;e.label&&(this._originInput.value=e.label),void 0===this._map&&(this._map=new google.maps.Map(elByClass("googleMap",t)[0],{disableDoubleClickZoom:WCF.Location.GoogleMaps.Settings.get("disableDoubleClickZoom"),draggable:WCF.Location.GoogleMaps.Settings.get("draggable"),mapTypeId:google.maps.MapTypeId.ROADMAP,scaleControl:WCF.Location.GoogleMaps.Settings.get("scaleControl"),scrollwheel:WCF.Location.GoogleMaps.Settings.get("scrollwheel")}),this._directionsService=new google.maps.DirectionsService,this._directionsRenderer=new google.maps.DirectionsRenderer,this._directionsRenderer.setMap(this._map),this._directionsRenderer.setPanel(elByClass("googleMapsDirections",t)[0]),this._googleLink=elByClass("googleMapsDirectionsGoogleLink",t)[0]);var n={destination:this._destination,origin:e.location,provideRouteAlternatives:!0,travelMode:google.maps.TravelMode[this._travelMode.value.toUpperCase()]};o.show(),this._directionsService.route(n,this._setRoute.bind(this)),elAttr(this._googleLink,"href",this._getGoogleMapsLink(e.location,this._travelMode.value)),this._lastOrigin=e.location},_getGoogleMapsLink:function(e,t){if(e){var n="https://www.google.com/maps/dir/?api=1&origin="+e.lat()+","+e.lng()+"&destination="+this._destination.lat()+","+this._destination.lng();return t&&(n+="&travelmode="+t),n}return"https://www.google.com/maps/search/?api=1&query="+this._destination.lat()+","+this._destination.lng()},_initDialog:function(){if(!this._didInitDialog){var e=i.getDialog(this).dialog;this._originInput=elBySel('input[name="origin"]',e),new WCF.Location.GoogleMaps.LocationSearch(this._originInput,this._calculateRoute.bind(this)),this._travelMode=elBySel('select[name="travelMode"]',e),this._travelMode.addEventListener("change",this._updateRoute.bind(this)),this._didInitDialog=!0}},_openDialog:function(){i.open(this)},_setRoute:function(t,i){o.hide(),"OK"===i?(elShow(this._map.getDiv().parentNode),google.maps.event.trigger(this._map,"resize"),this._directionsRenderer.setDirections(t),elShow(e.parentByTag(this._travelMode,"DL")),elShow(this._googleLink),elInnerError(this._originInput,!1)):("OVER_QUERY_LIMIT"!==i&&"REQUEST_DENIED"!==i&&(i="NOT_FOUND"),elInnerError(this._originInput,n.get("wcf.map.route.error."+i.toLowerCase())))},_updateRoute:function(){this._calculateRoute({location:this._lastOrigin})}},a}),define("WoltLabSuite/Core/Controller/User/Notification/Settings",["Dictionary","Language","Dom/Traverse","Ui/SimpleDropdown"],function(e,t,n,i){"use strict";var o=function(){};return o.prototype={setup:function(){},_initGroup:function(){},_click:function(){},_createDropdown:function(){},_selectType:function(){}},o}),define("WoltLabSuite/Core/Ui/Comment/Response/Add",["Core","Language","Dom/ChangeListener","Dom/Util","Dom/Traverse","Ui/Notification","WoltLabSuite/Core/Ui/Comment/Add"],function(e,t,n,i,o,a,r){"use strict";var s=function(){};return s.prototype={init:function(){},getContainer:function(){},getContent:function(){},setContent:function(){},_submitGuestDialog:function(){},_submit:function(){},_getParameters:function(){},_validate:function(){},throwError:function(){},_showLoadingOverlay:function(){},_hideLoadingOverlay:function(){},_reset:function(){},_handleError:function(){},_getEditor:function(){},_insertMessage:function(){},_ajaxSuccess:function(){},_ajaxFailure:function(){},_ajaxSetup:function(){}},s}),define("WoltLabSuite/Core/Ui/Comment/Response/Edit",["Ajax","Core","Dictionary","Environment","EventHandler","Language","List","Dom/ChangeListener","Dom/Traverse","Dom/Util","Ui/Notification","Ui/ReusableDropdown","WoltLabSuite/Core/Ui/Scroll","WoltLabSuite/Core/Ui/Comment/Edit"],function(e,t,n,i,o,a,r,s,l,c,u,d,h,f){"use strict";var p=function(){};return p.prototype={init:function(){},rebuild:function(){},_click:function(){},_prepare:function(){},_showEditor:function(){},_restoreMessage:function(){},_save:function(){},_validate:function(){},throwError:function(){},_showMessage:function(){},_hideEditor:function(){},_restoreEditor:function(){},_destroyEditor:function(){},_getEditorId:function(){},_getObjectId:function(){},_ajaxFailure:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){}},p}),define("WoltLabSuite/Core/Ui/Page/Header/Fixed",["Core","EventHandler","Ui/Alignment","Ui/CloseOverlay","Ui/SimpleDropdown","Ui/Screen"],function(e,t,n,i,o,a){"use strict";var r,s,l,c,u,d,h,f=!1;return{init:function(){r=elById("pageHeader"),s=elById("pageHeaderContainer"),this._initSearchBar(),a.on("screen-md-down",{match:function(){f=!0},unmatch:function(){f=!1},setup:function(){f=!0}}),t.add("com.woltlab.wcf.Search","close",this._closeSearchBar.bind(this))},_initSearchBar:function(){c=elById("pageHeaderSearch"),c.addEventListener(WCF_CLICK_EVENT,function(e){e.stopPropagation()}),l=elById("pageHeaderPanel"),u=elById("pageHeaderSearchInput"),d=elById("topMenu"),h=elById("userPanelSearchButton"),h.addEventListener(WCF_CLICK_EVENT,function(e){e.preventDefault(),e.stopPropagation(),r.classList.contains("searchBarOpen")?this._closeSearchBar():this._openSearchBar()}.bind(this)),i.add("WoltLabSuite/Core/Ui/Page/Header/Fixed",function(){r.classList.contains("searchBarForceOpen")||this._closeSearchBar()}.bind(this)),t.add("com.woltlab.wcf.MainMenuMobile","more",function(t){"com.woltlab.wcf.search"===t.identifier&&(t.handler.close(!0),e.triggerEvent(h,WCF_CLICK_EVENT))}.bind(this))},_openSearchBar:function(){window.WCF.Dropdown.Interactive.Handler.closeAll(),r.classList.add("searchBarOpen"),h.parentNode.classList.add("open"),f||n.set(c,d,{horizontal:"right"}),c.style.setProperty("top",l.clientHeight+"px",""),u.focus()},_closeSearchBar:function(){r.classList.remove("searchBarOpen"),h.parentNode.classList.remove("open"),["bottom","left","right","top"].forEach(function(e){c.style.removeProperty(e)}),u.blur();var e=elBySel(".pageHeaderSearchType",c);o.close(e.id)}}}),define("WoltLabSuite/Core/Ui/Page/Search/Input",["Core","WoltLabSuite/Core/Ui/Search/Input"],function(e,t){"use strict";function n(e,t){this.init(e,t)}return e.inherit(n,t,{init:function(t,i){if(i=e.extend({ajax:{className:"wcf\\data\\page\\PageAction"},callbackSuccess:null},i),"function"!=typeof i.callbackSuccess)throw new Error("Expected a valid callback function for 'callbackSuccess'.");n._super.prototype.init.call(this,t,i),this._pageId=0},setPageId:function(e){this._pageId=e},_getParameters:function(e){var t=n._super.prototype._getParameters.call(this,e);return t.objectIDs=[this._pageId],t},_ajaxSuccess:function(e){this._options.callbackSuccess(e)}}),n}),define("WoltLabSuite/Core/Ui/Page/Search/Handler",["Language","StringUtil","Dom/Util","Ui/Dialog","./Input"],function(e,t,n,i,o){"use strict";var a=null,r=null,s=null,l=null,c=null,u=null;return{open:function(t,n,o,r){a=o,i.open(this),i.setTitle(this,n),s.textContent=r?e.get(r):e.get("wcf.page.pageObjectID.search.terms"),this._getSearchInputHandler().setPageId(t)},_buildList:function(n){if(this._resetList(),!Array.isArray(n.returnValues)||0===n.returnValues.length)return void elInnerError(r,e.get("wcf.page.pageObjectID.search.noResults"));for(var i,o,a,s=0,l=n.returnValues.length;s<l;s++)o=n.returnValues[s],i=o.image,/^fa-/.test(i)&&(i='<span class="icon icon48 '+i+' pointer jsTooltip" title="'+e.get("wcf.global.select")+'"></span>'),a=elCreate("li"),elData(a,"object-id",o.objectID),a.innerHTML='<div class="box48">'+i+'<div><div class="containerHeadline"><h3><a href="'+t.escapeHTML(o.link)+'">'+t.escapeHTML(o.title)+"</a></h3>"+(o.description?"<p>"+o.description+"</p>":"")+"</div></div></div>",a.addEventListener(WCF_CLICK_EVENT,this._click.bind(this)),c.appendChild(a);elShow(u)},_resetList:function(){elInnerError(r,!1),c.innerHTML="",elHide(u)},_getSearchInputHandler:function(){if(null===l){var e=this._buildList.bind(this);l=new o(elById("wcfUiPageSearchInput"),{callbackSuccess:e})}return l},_click:function(e){"A"!==e.target.nodeName&&(e.stopPropagation(),a(elData(e.currentTarget,"object-id")),i.close(this))},_dialogSetup:function(){return{id:"wcfUiPageSearchHandler",options:{onShow:function(){null===r&&(r=elById("wcfUiPageSearchInput"),s=r.parentNode.previousSibling.childNodes[0],c=elById("wcfUiPageSearchResultList"),u=elById("wcfUiPageSearchResultListContainer")),r.value="",elHide(u),c.innerHTML="",r.focus()},title:""},source:'<div class="section"><dl><dt><label for="wcfUiPageSearchInput">'+e.get("wcf.page.pageObjectID.search.terms")+'</label></dt><dd><input type="text" id="wcfUiPageSearchInput" class="long"></dd></dl></div><section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList"><header class="sectionHeader"><h2 class="sectionTitle">'+e.get("wcf.page.pageObjectID.search.results")+'</h2></header><ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul></section>'}}}}),define("WoltLabSuite/Core/Ui/User/Activity/Recent",["Ajax","Language","Dom/Util"],function(e,t,n){"use strict";function i(e){this.init(e)}return i.prototype={init:function(e){this._containerId=e;var n=elById(this._containerId);this._list=elBySel(".recentActivityList",n);var i=elCreate("li");i.className="showMore",this._list.childElementCount?(i.innerHTML='<button class="small">'+t.get("wcf.user.recentActivity.more")+"</button>",i.children[0].addEventListener(WCF_CLICK_EVENT,this._showMore.bind(this))):i.innerHTML="<small>"+t.get("wcf.user.recentActivity.noMoreEntries")+"</small>",this._list.appendChild(i),this._showMoreItem=i,elBySelAll(".jsRecentActivitySwitchContext .button",n,function(e){e.addEventListener(WCF_CLICK_EVENT,function(t){t.preventDefault(),e.classList.contains("active")||this._switchContext()}.bind(this))}.bind(this))},_showMore:function(t){t.preventDefault(),this._showMoreItem.children[0].disabled=!0,e.api(this,{actionName:"load",parameters:{boxID:~~elData(this._list,"box-id"),filteredByFollowedUsers:elDataBool(this._list,"filtered-by-followed-users"),lastEventId:elData(this._list,"last-event-id"),lastEventTime:elData(this._list,"last-event-time"),userID:~~elData(this._list,"user-id")}})},_switchContext:function(){e.api(this,{actionName:"switchContext"},function(){window.location.hash="#"+this._containerId,window.location.reload()}.bind(this))},_ajaxSuccess:function(e){e.returnValues.template?(n.insertHtml(e.returnValues.template,this._showMoreItem,"before"),elData(this._list,"last-event-time",e.returnValues.lastEventTime),elData(this._list,"last-event-id",e.returnValues.lastEventID),this._showMoreItem.children[0].disabled=!1):this._showMoreItem.innerHTML="<small>"+t.get("wcf.user.recentActivity.noMoreEntries")+"</small>"},_ajaxSetup:function(){return{data:{className:"wcf\\data\\user\\activity\\event\\UserActivityEventAction"}}}},i}),define("WoltLabSuite/Core/Ui/User/CoverPhoto/Delete",["Ajax","EventHandler","Language","Ui/Confirmation","Ui/Notification"],function(e,t,n,i,o){"use strict";var a;return{init:function(){a=elBySel(".jsButtonDeleteCoverPhoto"),a.addEventListener(WCF_CLICK_EVENT,this._click.bind(this)),t.add("com.woltlab.wcf.user","coverPhoto",function(e){"string"==typeof e.url&&e.url.length>0&&elShow(a.parentNode)})},_click:function(){i.show({confirm:e.api.bind(e,this),message:n.get("wcf.user.coverPhoto.delete.confirmMessage")})},_ajaxSuccess:function(e){elBySel(".userProfileCoverPhoto").style.setProperty("background-image","url("+e.returnValues.url+")",""),elHide(a.parentNode),o.show()},_ajaxSetup:function(){return{data:{actionName:"deleteCoverPhoto",className:"wcf\\data\\user\\UserProfileAction"}}}}}),define("WoltLabSuite/Core/Ui/User/CoverPhoto/Upload",["Core","EventHandler","Upload","Ui/Notification","Ui/Dialog"],function(e,t,n,i,o){"use strict";function a(){n.call(this,"coverPhotoUploadButtonContainer","coverPhotoUploadPreview",{action:"uploadCoverPhoto",className:"wcf\\data\\user\\UserProfileAction"})}return e.inherit(a,n,{_success:function(e,n){elInnerError(this._button,n.returnValues.errorMessage),this._target.innerHTML="",n.returnValues.url&&(elBySel(".userProfileCoverPhoto").style.setProperty("background-image","url("+n.returnValues.url+")",""),o.close("userProfileCoverPhotoUpload"),i.show(),t.fire("com.woltlab.wcf.user","coverPhoto",{url:n.returnValues.url}))}}),a}),define("WoltLabSuite/Core/Ui/User/Trophy/List",["Ajax","Core","Dictionary","Dom/Util","Ui/Dialog","WoltLabSuite/Core/Ui/Pagination","Dom/ChangeListener","List"],function(e,t,n,i,o,a,r,s){"use strict";function l(){this.init()}return l.prototype={init:function(){this._cache=new n,this._knownElements=new s,this._options={className:"wcf\\data\\user\\trophy\\UserTrophyAction",parameters:{}},this._rebuild(),r.add("WoltLabSuite/Core/Ui/User/Trophy/List",this._rebuild.bind(this))},_rebuild:function(){elBySelAll(".userTrophyOverlayList",void 0,function(e){this._knownElements.has(e)||(e.addEventListener(WCF_CLICK_EVENT,this._open.bind(this,elData(e,"user-id"))),this._knownElements.add(e))}.bind(this))},_open:function(e,t){t.preventDefault(),this._currentPageNo=1,this._currentUser=e,this._showPage()},_showPage:function(t){if(void 0!==t&&(this._currentPageNo=t),this._cache.has(this._currentUser)){if(0!==this._cache.get(this._currentUser).get("pageCount")&&(this._currentPageNo<1||this._currentPageNo>this._cache.get(this._currentUser).get("pageCount")))throw new RangeError("pageNo must be between 1 and "+this._cache.get(this._currentUser).get("pageCount")+" ("+this._currentPageNo+" given).")}else this._cache.set(this._currentUser,new n);if(this._cache.get(this._currentUser).has(this._currentPageNo)){var i=o.open(this,this._cache.get(this._currentUser).get(this._currentPageNo));if(o.setTitle("userTrophyListOverlay",this._cache.get(this._currentUser).get("title")),this._cache.get(this._currentUser).get("pageCount")>1){var r=elBySel(".jsPagination",i.content);null!==r&&new a(r,{activePage:this._currentPageNo,maxPage:this._cache.get(this._currentUser).get("pageCount"),callbackSwitch:this._showPage.bind(this)})}}else this._options.parameters.pageNo=this._currentPageNo,this._options.parameters.userID=this._currentUser,e.api(this,{parameters:this._options.parameters})},_ajaxSuccess:function(e){void 0!==e.returnValues.pageCount&&this._cache.get(this._currentUser).set("pageCount",~~e.returnValues.pageCount),this._cache.get(this._currentUser).set(this._currentPageNo,e.returnValues.template),this._cache.get(this._currentUser).set("title",e.returnValues.title),this._showPage()},_ajaxSetup:function(){return{data:{actionName:"getGroupedUserTrophyList",className:this._options.className}}},_dialogSetup:function(){return{id:"userTrophyListOverlay",options:{title:""},source:null}}},l}),define("WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Abstract",["Ajax","Dom/Util"],function(e,t){"use strict";function n(e,t){}return n.prototype={init:function(e,t){this._userId=e,this._isActive=!1!==t,this._initButton(),this._updateButton()},_initButton:function(){var e=elCreate("a");e.href="#",e.addEventListener(WCF_CLICK_EVENT,this._toggle.bind(this));var n=elCreate("li");n.appendChild(e);var i=elBySel('.userProfileButtonMenu[data-menu="interaction"]');t.prepend(n,i),this._button=e,this._listItem=n},
-_toggle:function(t){t.preventDefault(),e.api(this,{actionName:this._getAjaxActionName(),parameters:{data:{userID:this._userId}}})},_updateButton:function(){this._button.textContent=this._getLabel(),this._listItem.classList[this._isActive?"add":"remove"]("active")},_getLabel:function(){throw new Error("Implement me!")},_getAjaxActionName:function(){throw new Error("Implement me!")},_ajaxSuccess:function(){throw new Error("Implement me!")},_ajaxSetup:function(){throw new Error("Implement me!")}},n}),define("WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Follow",["Core","Language","Ui/Notification","./Abstract"],function(e,t,n,i){"use strict";var o=function(){};return o.prototype={_getLabel:function(){},_getAjaxActionName:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){},init:function(){},_initButton:function(){},_toggle:function(){},_updateButton:function(){}},o}),define("WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Ignore",["Core","Language","Ui/Notification","./Abstract"],function(e,t,n,i){"use strict";var o=function(){};return o.prototype={_getLabel:function(){},_getAjaxActionName:function(){},_ajaxSuccess:function(){},_ajaxSetup:function(){},init:function(){},_initButton:function(){},_toggle:function(){},_updateButton:function(){}},o}),function(e){e.matches=e.matches||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector,e.closest=e.closest||function(e){for(var t=this;t&&!t.matches(e);)t=t.parentElement;return t}}(Element.prototype),define("closest",function(){}),function(e){function t(){for(;i.length&&"function"==typeof i[0];)i.shift()()}var n=e.require,i=[],o=0;e.require=function(a,r,s){if(!Array.isArray(a))return n.apply(e,arguments);var l=new Promise(function(e,r){var s=o++;i.push(s),n(a,function(){var n=arguments;i[i.indexOf(s)]=function(){e(n)},t()},function(e){i[i.indexOf(s)]=function(){r(e)},t()})});return r&&l.then(function(t){r.apply(e,t)}),s&&l.catch(s),l},e.require.config=n.config}(window),define("require.linearExecution",function(){});
\ No newline at end of file
+/**
+ * @license alameda 1.2.0 Copyright jQuery Foundation and other contributors.
+ * Released under MIT license, https://github.com/requirejs/alameda/blob/master/LICENSE
+ */
+// Going sloppy because loader plugin execs may depend on non-strict execution.
+/*jslint sloppy: true, nomen: true, regexp: true */
+/*global document, navigator, importScripts, Promise, setTimeout */
+
+var requirejs, require, define;
+(function (global, Promise, undef) {
+ if (!Promise) {
+ throw new Error('No Promise implementation available');
+ }
+
+ var topReq, dataMain, src, subPath,
+ bootstrapConfig = requirejs || require,
+ hasOwn = Object.prototype.hasOwnProperty,
+ contexts = {},
+ queue = [],
+ currDirRegExp = /^\.\//,
+ urlRegExp = /^\/|\:|\?|\.js$/,
+ commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg,
+ cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
+ jsSuffixRegExp = /\.js$/,
+ slice = Array.prototype.slice;
+
+ if (typeof requirejs === 'function') {
+ return;
+ }
+
+ var asap = Promise.resolve(undefined);
+
+ // Could match something like ')//comment', do not lose the prefix to comment.
+ function commentReplace(match, singlePrefix) {
+ return singlePrefix || '';
+ }
+
+ function hasProp(obj, prop) {
+ return hasOwn.call(obj, prop);
+ }
+
+ function getOwn(obj, prop) {
+ return obj && hasProp(obj, prop) && obj[prop];
+ }
+
+ function obj() {
+ return Object.create(null);
+ }
+
+ /**
+ * Cycles over properties in an object and calls a function for each
+ * property value. If the function returns a truthy value, then the
+ * iteration is stopped.
+ */
+ function eachProp(obj, func) {
+ var prop;
+ for (prop in obj) {
+ if (hasProp(obj, prop)) {
+ if (func(obj[prop], prop)) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Simple function to mix in properties from source into target,
+ * but only if target does not already have a property of the same name.
+ */
+ function mixin(target, source, force, deepStringMixin) {
+ if (source) {
+ eachProp(source, function (value, prop) {
+ if (force || !hasProp(target, prop)) {
+ if (deepStringMixin && typeof value === 'object' && value &&
+ !Array.isArray(value) && typeof value !== 'function' &&
+ !(value instanceof RegExp)) {
+
+ if (!target[prop]) {
+ target[prop] = {};
+ }
+ mixin(target[prop], value, force, deepStringMixin);
+ } else {
+ target[prop] = value;
+ }
+ }
+ });
+ }
+ return target;
+ }
+
+ // Allow getting a global that expressed in
+ // dot notation, like 'a.b.c'.
+ function getGlobal(value) {
+ if (!value) {
+ return value;
+ }
+ var g = global;
+ value.split('.').forEach(function (part) {
+ g = g[part];
+ });
+ return g;
+ }
+
+ function newContext(contextName) {
+ var req, main, makeMap, callDep, handlers, checkingLater, load, context,
+ defined = obj(),
+ waiting = obj(),
+ config = {
+ // Defaults. Do not set a default for map
+ // config to speed up normalize(), which
+ // will run faster if there is no default.
+ waitSeconds: 7,
+ baseUrl: './',
+ paths: {},
+ bundles: {},
+ pkgs: {},
+ shim: {},
+ config: {}
+ },
+ mapCache = obj(),
+ requireDeferreds = [],
+ deferreds = obj(),
+ calledDefine = obj(),
+ calledPlugin = obj(),
+ loadCount = 0,
+ startTime = (new Date()).getTime(),
+ errCount = 0,
+ trackedErrors = obj(),
+ urlFetched = obj(),
+ bundlesMap = obj(),
+ asyncResolve = Promise.resolve();
+
+ /**
+ * Trims the . and .. from an array of path segments.
+ * It will keep a leading path segment if a .. will become
+ * the first path segment, to help with module name lookups,
+ * which act like paths, but can be remapped. But the end result,
+ * all paths that use this function should look normalized.
+ * NOTE: this method MODIFIES the input array.
+ * @param {Array} ary the array of path segments.
+ */
+ function trimDots(ary) {
+ var i, part, length = ary.length;
+ for (i = 0; i < length; i++) {
+ part = ary[i];
+ if (part === '.') {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === '..') {
+ // If at the start, or previous value is still ..,
+ // keep them so that when converted to a path it may
+ // still work when converted to a path, even though
+ // as an ID it is less than ideal. In larger point
+ // releases, may be better to just kick out an error.
+ if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') {
+ continue;
+ } else if (i > 0) {
+ ary.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Given a relative module name, like ./something, normalize it to
+ * a real name that can be mapped to a path.
+ * @param {String} name the relative name
+ * @param {String} baseName a real name that the name arg is relative
+ * to.
+ * @param {Boolean} applyMap apply the map config to the value. Should
+ * only be done if this normalization is for a dependency ID.
+ * @returns {String} normalized name
+ */
+ function normalize(name, baseName, applyMap) {
+ var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
+ foundMap, foundI, foundStarMap, starI,
+ baseParts = baseName && baseName.split('/'),
+ normalizedBaseParts = baseParts,
+ map = config.map,
+ starMap = map && map['*'];
+
+
+ //Adjust any relative paths.
+ if (name) {
+ name = name.split('/');
+ lastIndex = name.length - 1;
+
+ // If wanting node ID compatibility, strip .js from end
+ // of IDs. Have to do this here, and not in nameToUrl
+ // because node allows either .js or non .js to map
+ // to same file.
+ if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
+ name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
+ }
+
+ // Starts with a '.' so need the baseName
+ if (name[0].charAt(0) === '.' && baseParts) {
+ //Convert baseName to array, and lop off the last part,
+ //so that . matches that 'directory' and not name of the baseName's
+ //module. For instance, baseName of 'one/two/three', maps to
+ //'one/two/three.js', but we want the directory, 'one/two' for
+ //this normalization.
+ normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
+ name = normalizedBaseParts.concat(name);
+ }
+
+ trimDots(name);
+ name = name.join('/');
+ }
+
+ // Apply map config if available.
+ if (applyMap && map && (baseParts || starMap)) {
+ nameParts = name.split('/');
+
+ outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
+ nameSegment = nameParts.slice(0, i).join('/');
+
+ if (baseParts) {
+ // Find the longest baseName segment match in the config.
+ // So, do joins on the biggest to smallest lengths of baseParts.
+ for (j = baseParts.length; j > 0; j -= 1) {
+ mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
+
+ // baseName segment has config, find if it has one for
+ // this name.
+ if (mapValue) {
+ mapValue = getOwn(mapValue, nameSegment);
+ if (mapValue) {
+ // Match, update name to the new value.
+ foundMap = mapValue;
+ foundI = i;
+ break outerLoop;
+ }
+ }
+ }
+ }
+
+ // Check for a star map match, but just hold on to it,
+ // if there is a shorter segment match later in a matching
+ // config, then favor over this star map.
+ if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
+ foundStarMap = getOwn(starMap, nameSegment);
+ starI = i;
+ }
+ }
+
+ if (!foundMap && foundStarMap) {
+ foundMap = foundStarMap;
+ foundI = starI;
+ }
+
+ if (foundMap) {
+ nameParts.splice(0, foundI, foundMap);
+ name = nameParts.join('/');
+ }
+ }
+
+ // If the name points to a package's name, use
+ // the package main instead.
+ pkgMain = getOwn(config.pkgs, name);
+
+ return pkgMain ? pkgMain : name;
+ }
+
+ function makeShimExports(value) {
+ function fn() {
+ var ret;
+ if (value.init) {
+ ret = value.init.apply(global, arguments);
+ }
+ return ret || (value.exports && getGlobal(value.exports));
+ }
+ return fn;
+ }
+
+ function takeQueue(anonId) {
+ var i, id, args, shim;
+ for (i = 0; i < queue.length; i += 1) {
+ // Peek to see if anon
+ if (typeof queue[i][0] !== 'string') {
+ if (anonId) {
+ queue[i].unshift(anonId);
+ anonId = undef;
+ } else {
+ // Not our anon module, stop.
+ break;
+ }
+ }
+ args = queue.shift();
+ id = args[0];
+ i -= 1;
+
+ if (!(id in defined) && !(id in waiting)) {
+ if (id in deferreds) {
+ main.apply(undef, args);
+ } else {
+ waiting[id] = args;
+ }
+ }
+ }
+
+ // if get to the end and still have anonId, then could be
+ // a shimmed dependency.
+ if (anonId) {
+ shim = getOwn(config.shim, anonId) || {};
+ main(anonId, shim.deps || [], shim.exportsFn);
+ }
+ }
+
+ function makeRequire(relName, topLevel) {
+ var req = function (deps, callback, errback, alt) {
+ var name, cfg;
+
+ if (topLevel) {
+ takeQueue();
+ }
+
+ if (typeof deps === "string") {
+ if (handlers[deps]) {
+ return handlers[deps](relName);
+ }
+ // Just return the module wanted. In this scenario, the
+ // deps arg is the module name, and second arg (if passed)
+ // is just the relName.
+ // Normalize module name, if it contains . or ..
+ name = makeMap(deps, relName, true).id;
+ if (!(name in defined)) {
+ throw new Error('Not loaded: ' + name);
+ }
+ return defined[name];
+ } else if (deps && !Array.isArray(deps)) {
+ // deps is a config object, not an array.
+ cfg = deps;
+ deps = undef;
+
+ if (Array.isArray(callback)) {
+ // callback is an array, which means it is a dependency list.
+ // Adjust args if there are dependencies
+ deps = callback;
+ callback = errback;
+ errback = alt;
+ }
+
+ if (topLevel) {
+ // Could be a new context, so call returned require
+ return req.config(cfg)(deps, callback, errback);
+ }
+ }
+
+ // Support require(['a'])
+ callback = callback || function () {
+ // In case used later as a promise then value, return the
+ // arguments as an array.
+ return slice.call(arguments, 0);
+ };
+
+ // Complete async to maintain expected execution semantics.
+ return asyncResolve.then(function () {
+ // Grab any modules that were defined after a require call.
+ takeQueue();
+
+ return main(undef, deps || [], callback, errback, relName);
+ });
+ };
+
+ req.isBrowser = typeof document !== 'undefined' &&
+ typeof navigator !== 'undefined';
+
+ req.nameToUrl = function (moduleName, ext, skipExt) {
+ var paths, syms, i, parentModule, url,
+ parentPath, bundleId,
+ pkgMain = getOwn(config.pkgs, moduleName);
+
+ if (pkgMain) {
+ moduleName = pkgMain;
+ }
+
+ bundleId = getOwn(bundlesMap, moduleName);
+
+ if (bundleId) {
+ return req.nameToUrl(bundleId, ext, skipExt);
+ }
+
+ // If a colon is in the URL, it indicates a protocol is used and it is
+ // just an URL to a file, or if it starts with a slash, contains a query
+ // arg (i.e. ?) or ends with .js, then assume the user meant to use an
+ // url and not a module id. The slash is important for protocol-less
+ // URLs as well as full paths.
+ if (urlRegExp.test(moduleName)) {
+ // Just a plain path, not module name lookup, so just return it.
+ // Add extension if it is included. This is a bit wonky, only non-.js
+ // things pass an extension, this method probably needs to be
+ // reworked.
+ url = moduleName + (ext || '');
+ } else {
+ // A module that needs to be converted to a path.
+ paths = config.paths;
+
+ syms = moduleName.split('/');
+ // For each module name segment, see if there is a path
+ // registered for it. Start with most specific name
+ // and work up from it.
+ for (i = syms.length; i > 0; i -= 1) {
+ parentModule = syms.slice(0, i).join('/');
+
+ parentPath = getOwn(paths, parentModule);
+ if (parentPath) {
+ // If an array, it means there are a few choices,
+ // Choose the one that is desired
+ if (Array.isArray(parentPath)) {
+ parentPath = parentPath[0];
+ }
+ syms.splice(0, i, parentPath);
+ break;
+ }
+ }
+
+ // Join the path parts together, then figure out if baseUrl is needed.
+ url = syms.join('/');
+ url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js'));
+ url = (url.charAt(0) === '/' ||
+ url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
+ }
+
+ return config.urlArgs && !/^blob\:/.test(url) ?
+ url + config.urlArgs(moduleName, url) : url;
+ };
+
+ /**
+ * Converts a module name + .extension into an URL path.
+ * *Requires* the use of a module name. It does not support using
+ * plain URLs like nameToUrl.
+ */
+ req.toUrl = function (moduleNamePlusExt) {
+ var ext,
+ index = moduleNamePlusExt.lastIndexOf('.'),
+ segment = moduleNamePlusExt.split('/')[0],
+ isRelative = segment === '.' || segment === '..';
+
+ // Have a file extension alias, and it is not the
+ // dots from a relative path.
+ if (index !== -1 && (!isRelative || index > 1)) {
+ ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
+ moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
+ }
+
+ return req.nameToUrl(normalize(moduleNamePlusExt, relName), ext, true);
+ };
+
+ req.defined = function (id) {
+ return makeMap(id, relName, true).id in defined;
+ };
+
+ req.specified = function (id) {
+ id = makeMap(id, relName, true).id;
+ return id in defined || id in deferreds;
+ };
+
+ return req;
+ }
+
+ function resolve(name, d, value) {
+ if (name) {
+ defined[name] = value;
+ if (requirejs.onResourceLoad) {
+ requirejs.onResourceLoad(context, d.map, d.deps);
+ }
+ }
+ d.finished = true;
+ d.resolve(value);
+ }
+
+ function reject(d, err) {
+ d.finished = true;
+ d.rejected = true;
+ d.reject(err);
+ }
+
+ function makeNormalize(relName) {
+ return function (name) {
+ return normalize(name, relName, true);
+ };
+ }
+
+ function defineModule(d) {
+ d.factoryCalled = true;
+
+ var ret,
+ name = d.map.id;
+
+ try {
+ ret = context.execCb(name, d.factory, d.values, defined[name]);
+ } catch(err) {
+ return reject(d, err);
+ }
+
+ if (name) {
+ // Favor return value over exports. If node/cjs in play,
+ // then will not have a return value anyway. Favor
+ // module.exports assignment over exports object.
+ if (ret === undef) {
+ if (d.cjsModule) {
+ ret = d.cjsModule.exports;
+ } else if (d.usingExports) {
+ ret = defined[name];
+ }
+ }
+ } else {
+ // Remove the require deferred from the list to
+ // make cycle searching faster. Do not need to track
+ // it anymore either.
+ requireDeferreds.splice(requireDeferreds.indexOf(d), 1);
+ }
+ resolve(name, d, ret);
+ }
+
+ // This method is attached to every module deferred,
+ // so the "this" in here is the module deferred object.
+ function depFinished(val, i) {
+ if (!this.rejected && !this.depDefined[i]) {
+ this.depDefined[i] = true;
+ this.depCount += 1;
+ this.values[i] = val;
+ if (!this.depending && this.depCount === this.depMax) {
+ defineModule(this);
+ }
+ }
+ }
+
+ function makeDefer(name, calculatedMap) {
+ var d = {};
+ d.promise = new Promise(function (resolve, reject) {
+ d.resolve = resolve;
+ d.reject = function(err) {
+ if (!name) {
+ requireDeferreds.splice(requireDeferreds.indexOf(d), 1);
+ }
+ reject(err);
+ };
+ });
+ d.map = name ? (calculatedMap || makeMap(name)) : {};
+ d.depCount = 0;
+ d.depMax = 0;
+ d.values = [];
+ d.depDefined = [];
+ d.depFinished = depFinished;
+ if (d.map.pr) {
+ // Plugin resource ID, implicitly
+ // depends on plugin. Track it in deps
+ // so cycle breaking can work
+ d.deps = [makeMap(d.map.pr)];
+ }
+ return d;
+ }
+
+ function getDefer(name, calculatedMap) {
+ var d;
+ if (name) {
+ d = (name in deferreds) && deferreds[name];
+ if (!d) {
+ d = deferreds[name] = makeDefer(name, calculatedMap);
+ }
+ } else {
+ d = makeDefer();
+ requireDeferreds.push(d);
+ }
+ return d;
+ }
+
+ function makeErrback(d, name) {
+ return function (err) {
+ if (!d.rejected) {
+ if (!err.dynaId) {
+ err.dynaId = 'id' + (errCount += 1);
+ err.requireModules = [name];
+ }
+ reject(d, err);
+ }
+ };
+ }
+
+ function waitForDep(depMap, relName, d, i) {
+ d.depMax += 1;
+
+ // Do the fail at the end to catch errors
+ // in the then callback execution.
+ callDep(depMap, relName).then(function (val) {
+ d.depFinished(val, i);
+ }, makeErrback(d, depMap.id)).catch(makeErrback(d, d.map.id));
+ }
+
+ function makeLoad(id) {
+ var fromTextCalled;
+ function load(value) {
+ // Protect against older plugins that call load after
+ // calling load.fromText
+ if (!fromTextCalled) {
+ resolve(id, getDefer(id), value);
+ }
+ }
+
+ load.error = function (err) {
+ getDefer(id).reject(err);
+ };
+
+ load.fromText = function (text, textAlt) {
+ /*jslint evil: true */
+ var d = getDefer(id),
+ map = makeMap(makeMap(id).n),
+ plainId = map.id;
+
+ fromTextCalled = true;
+
+ // Set up the factory just to be a return of the value from
+ // plainId.
+ d.factory = function (p, val) {
+ return val;
+ };
+
+ // As of requirejs 2.1.0, support just passing the text, to reinforce
+ // fromText only being called once per resource. Still
+ // support old style of passing moduleName but discard
+ // that moduleName in favor of the internal ref.
+ if (textAlt) {
+ text = textAlt;
+ }
+
+ // Transfer any config to this other module.
+ if (hasProp(config.config, id)) {
+ config.config[plainId] = config.config[id];
+ }
+
+ try {
+ req.exec(text);
+ } catch (e) {
+ reject(d, new Error('fromText eval for ' + plainId +
+ ' failed: ' + e));
+ }
+
+ // Execute any waiting define created by the plainId
+ takeQueue(plainId);
+
+ // Mark this as a dependency for the plugin
+ // resource
+ d.deps = [map];
+ waitForDep(map, null, d, d.deps.length);
+ };
+
+ return load;
+ }
+
+ load = typeof importScripts === 'function' ?
+ function (map) {
+ var url = map.url;
+ if (urlFetched[url]) {
+ return;
+ }
+ urlFetched[url] = true;
+
+ // Ask for the deferred so loading is triggered.
+ // Do this before loading, since loading is sync.
+ getDefer(map.id);
+ importScripts(url);
+ takeQueue(map.id);
+ } :
+ function (map) {
+ var script,
+ id = map.id,
+ url = map.url;
+
+ if (urlFetched[url]) {
+ return;
+ }
+ urlFetched[url] = true;
+
+ script = document.createElement('script');
+ script.setAttribute('data-requiremodule', id);
+ script.type = config.scriptType || 'text/javascript';
+ script.charset = 'utf-8';
+ script.async = true;
+
+ loadCount += 1;
+
+ script.addEventListener('load', function () {
+ loadCount -= 1;
+ takeQueue(id);
+ }, false);
+ script.addEventListener('error', function () {
+ loadCount -= 1;
+ var err,
+ pathConfig = getOwn(config.paths, id);
+ if (pathConfig && Array.isArray(pathConfig) &&
+ pathConfig.length > 1) {
+ script.parentNode.removeChild(script);
+ // Pop off the first array value, since it failed, and
+ // retry
+ pathConfig.shift();
+ var d = getDefer(id);
+ d.map = makeMap(id);
+ // mapCache will have returned previous map value, update the
+ // url, which will also update mapCache value.
+ d.map.url = req.nameToUrl(id);
+ load(d.map);
+ } else {
+ err = new Error('Load failed: ' + id + ': ' + script.src);
+ err.requireModules = [id];
+ getDefer(id).reject(err);
+ }
+ }, false);
+
+ script.src = url;
+
+ // If the script is cached, IE10 executes the script body and the
+ // onload handler synchronously here. That's a spec violation,
+ // so be sure to do this asynchronously.
+ if (document.documentMode === 10) {
+ asap.then(function() {
+ document.head.appendChild(script);
+ });
+ } else {
+ document.head.appendChild(script);
+ }
+ };
+
+ function callPlugin(plugin, map, relName) {
+ plugin.load(map.n, makeRequire(relName), makeLoad(map.id), config);
+ }
+
+ callDep = function (map, relName) {
+ var args, bundleId,
+ name = map.id,
+ shim = config.shim[name];
+
+ if (name in waiting) {
+ args = waiting[name];
+ delete waiting[name];
+ main.apply(undef, args);
+ } else if (!(name in deferreds)) {
+ if (map.pr) {
+ // If a bundles config, then just load that file instead to
+ // resolve the plugin, as it is built into that bundle.
+ if ((bundleId = getOwn(bundlesMap, name))) {
+ map.url = req.nameToUrl(bundleId);
+ load(map);
+ } else {
+ return callDep(makeMap(map.pr)).then(function (plugin) {
+ // Redo map now that plugin is known to be loaded
+ var newMap = map.prn ? map : makeMap(name, relName, true),
+ newId = newMap.id,
+ shim = getOwn(config.shim, newId);
+
+ // Make sure to only call load once per resource. Many
+ // calls could have been queued waiting for plugin to load.
+ if (!(newId in calledPlugin)) {
+ calledPlugin[newId] = true;
+ if (shim && shim.deps) {
+ req(shim.deps, function () {
+ callPlugin(plugin, newMap, relName);
+ });
+ } else {
+ callPlugin(plugin, newMap, relName);
+ }
+ }
+ return getDefer(newId).promise;
+ });
+ }
+ } else if (shim && shim.deps) {
+ req(shim.deps, function () {
+ load(map);
+ });
+ } else {
+ load(map);
+ }
+ }
+
+ return getDefer(name).promise;
+ };
+
+ // Turns a plugin!resource to [plugin, resource]
+ // with the plugin being undefined if the name
+ // did not have a plugin prefix.
+ function splitPrefix(name) {
+ var prefix,
+ index = name ? name.indexOf('!') : -1;
+ if (index > -1) {
+ prefix = name.substring(0, index);
+ name = name.substring(index + 1, name.length);
+ }
+ return [prefix, name];
+ }
+
+ /**
+ * Makes a name map, normalizing the name, and using a plugin
+ * for normalization if necessary. Grabs a ref to plugin
+ * too, as an optimization.
+ */
+ makeMap = function (name, relName, applyMap) {
+ if (typeof name !== 'string') {
+ return name;
+ }
+
+ var plugin, url, parts, prefix, result, prefixNormalized,
+ cacheKey = name + ' & ' + (relName || '') + ' & ' + !!applyMap;
+
+ parts = splitPrefix(name);
+ prefix = parts[0];
+ name = parts[1];
+
+ if (!prefix && (cacheKey in mapCache)) {
+ return mapCache[cacheKey];
+ }
+
+ if (prefix) {
+ prefix = normalize(prefix, relName, applyMap);
+ plugin = (prefix in defined) && defined[prefix];
+ }
+
+ // Normalize according
+ if (prefix) {
+ if (plugin && plugin.normalize) {
+ name = plugin.normalize(name, makeNormalize(relName));
+ prefixNormalized = true;
+ } else {
+ // If nested plugin references, then do not try to
+ // normalize, as it will not normalize correctly. This
+ // places a restriction on resourceIds, and the longer
+ // term solution is not to normalize until plugins are
+ // loaded and all normalizations to allow for async
+ // loading of a loader plugin. But for now, fixes the
+ // common uses. Details in requirejs#1131
+ name = name.indexOf('!') === -1 ?
+ normalize(name, relName, applyMap) :
+ name;
+ }
+ } else {
+ name = normalize(name, relName, applyMap);
+ parts = splitPrefix(name);
+ prefix = parts[0];
+ name = parts[1];
+
+ url = req.nameToUrl(name);
+ }
+
+ // Using ridiculous property names for space reasons
+ result = {
+ id: prefix ? prefix + '!' + name : name, // fullName
+ n: name,
+ pr: prefix,
+ url: url,
+ prn: prefix && prefixNormalized
+ };
+
+ if (!prefix) {
+ mapCache[cacheKey] = result;
+ }
+
+ return result;
+ };
+
+ handlers = {
+ require: function (name) {
+ return makeRequire(name);
+ },
+ exports: function (name) {
+ var e = defined[name];
+ if (typeof e !== 'undefined') {
+ return e;
+ } else {
+ return (defined[name] = {});
+ }
+ },
+ module: function (name) {
+ return {
+ id: name,
+ uri: '',
+ exports: handlers.exports(name),
+ config: function () {
+ return getOwn(config.config, name) || {};
+ }
+ };
+ }
+ };
+
+ function breakCycle(d, traced, processed) {
+ var id = d.map.id;
+
+ traced[id] = true;
+ if (!d.finished && d.deps) {
+ d.deps.forEach(function (depMap) {
+ var depId = depMap.id,
+ dep = !hasProp(handlers, depId) && getDefer(depId, depMap);
+
+ // Only force things that have not completed
+ // being defined, so still in the registry,
+ // and only if it has not been matched up
+ // in the module already.
+ if (dep && !dep.finished && !processed[depId]) {
+ if (hasProp(traced, depId)) {
+ d.deps.forEach(function (depMap, i) {
+ if (depMap.id === depId) {
+ d.depFinished(defined[depId], i);
+ }
+ });
+ } else {
+ breakCycle(dep, traced, processed);
+ }
+ }
+ });
+ }
+ processed[id] = true;
+ }
+
+ function check(d) {
+ var err, mid, dfd,
+ notFinished = [],
+ waitInterval = config.waitSeconds * 1000,
+ // It is possible to disable the wait interval by using waitSeconds 0.
+ expired = waitInterval &&
+ (startTime + waitInterval) < (new Date()).getTime();
+
+ if (loadCount === 0) {
+ // If passed in a deferred, it is for a specific require call.
+ // Could be a sync case that needs resolution right away.
+ // Otherwise, if no deferred, means it was the last ditch
+ // timeout-based check, so check all waiting require deferreds.
+ if (d) {
+ if (!d.finished) {
+ breakCycle(d, {}, {});
+ }
+ } else if (requireDeferreds.length) {
+ requireDeferreds.forEach(function (d) {
+ breakCycle(d, {}, {});
+ });
+ }
+ }
+
+ // If still waiting on loads, and the waiting load is something
+ // other than a plugin resource, or there are still outstanding
+ // scripts, then just try back later.
+ if (expired) {
+ // If wait time expired, throw error of unloaded modules.
+ for (mid in deferreds) {
+ dfd = deferreds[mid];
+ if (!dfd.finished) {
+ notFinished.push(dfd.map.id);
+ }
+ }
+ err = new Error('Timeout for modules: ' + notFinished);
+ err.requireModules = notFinished;
+ req.onError(err);
+ } else if (loadCount || requireDeferreds.length) {
+ // Something is still waiting to load. Wait for it, but only
+ // if a later check is not already scheduled. Using setTimeout
+ // because want other things in the event loop to happen,
+ // to help in dependency resolution, and this is really a
+ // last ditch check, mostly for detecting timeouts (cycles
+ // should come through the main() use of check()), so it can
+ // wait a bit before doing the final check.
+ if (!checkingLater) {
+ checkingLater = true;
+ setTimeout(function () {
+ checkingLater = false;
+ check();
+ }, 70);
+ }
+ }
+ }
+
+ // Used to break out of the promise try/catch chains.
+ function delayedError(e) {
+ setTimeout(function () {
+ if (!e.dynaId || !trackedErrors[e.dynaId]) {
+ trackedErrors[e.dynaId] = true;
+ req.onError(e);
+ }
+ });
+ return e;
+ }
+
+ main = function (name, deps, factory, errback, relName) {
+ if (name) {
+ // Only allow main calling once per module.
+ if (name in calledDefine) {
+ return;
+ }
+ calledDefine[name] = true;
+ }
+
+ var d = getDefer(name);
+
+ // This module may not have dependencies
+ if (deps && !Array.isArray(deps)) {
+ // deps is not an array, so probably means
+ // an object literal or factory function for
+ // the value. Adjust args.
+ factory = deps;
+ deps = [];
+ }
+
+ // Create fresh array instead of modifying passed in value.
+ deps = deps ? slice.call(deps, 0) : null;
+
+ if (!errback) {
+ if (hasProp(config, 'defaultErrback')) {
+ if (config.defaultErrback) {
+ errback = config.defaultErrback;
+ }
+ } else {
+ errback = delayedError;
+ }
+ }
+
+ if (errback) {
+ d.promise.catch(errback);
+ }
+
+ // Use name if no relName
+ relName = relName || name;
+
+ // Call the factory to define the module, if necessary.
+ if (typeof factory === 'function') {
+
+ if (!deps.length && factory.length) {
+ // Remove comments from the callback string,
+ // look for require calls, and pull them into the dependencies,
+ // but only if there are function args.
+ factory
+ .toString()
+ .replace(commentRegExp, commentReplace)
+ .replace(cjsRequireRegExp, function (match, dep) {
+ deps.push(dep);
+ });
+
+ // May be a CommonJS thing even without require calls, but still
+ // could use exports, and module. Avoid doing exports and module
+ // work though if it just needs require.
+ // REQUIRES the function to expect the CommonJS variables in the
+ // order listed below.
+ deps = (factory.length === 1 ?
+ ['require'] :
+ ['require', 'exports', 'module']).concat(deps);
+ }
+
+ // Save info for use later.
+ d.factory = factory;
+ d.deps = deps;
+
+ d.depending = true;
+ deps.forEach(function (depName, i) {
+ var depMap;
+ deps[i] = depMap = makeMap(depName, relName, true);
+ depName = depMap.id;
+
+ // Fast path CommonJS standard dependencies.
+ if (depName === "require") {
+ d.values[i] = handlers.require(name);
+ } else if (depName === "exports") {
+ // CommonJS module spec 1.1
+ d.values[i] = handlers.exports(name);
+ d.usingExports = true;
+ } else if (depName === "module") {
+ // CommonJS module spec 1.1
+ d.values[i] = d.cjsModule = handlers.module(name);
+ } else if (depName === undefined) {
+ d.values[i] = undefined;
+ } else {
+ waitForDep(depMap, relName, d, i);
+ }
+ });
+ d.depending = false;
+
+ // Some modules just depend on the require, exports, modules, so
+ // trigger their definition here if so.
+ if (d.depCount === d.depMax) {
+ defineModule(d);
+ }
+ } else if (name) {
+ // May just be an object definition for the module. Only
+ // worry about defining if have a module name.
+ resolve(name, d, factory);
+ }
+
+ startTime = (new Date()).getTime();
+
+ if (!name) {
+ check(d);
+ }
+
+ return d.promise;
+ };
+
+ req = makeRequire(null, true);
+
+ /*
+ * Just drops the config on the floor, but returns req in case
+ * the config return value is used.
+ */
+ req.config = function (cfg) {
+ if (cfg.context && cfg.context !== contextName) {
+ var existingContext = getOwn(contexts, cfg.context);
+ if (existingContext) {
+ return existingContext.req.config(cfg);
+ } else {
+ return newContext(cfg.context).config(cfg);
+ }
+ }
+
+ // Since config changed, mapCache may not be valid any more.
+ mapCache = obj();
+
+ // Make sure the baseUrl ends in a slash.
+ if (cfg.baseUrl) {
+ if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
+ cfg.baseUrl += '/';
+ }
+ }
+
+ // Convert old style urlArgs string to a function.
+ if (typeof cfg.urlArgs === 'string') {
+ var urlArgs = cfg.urlArgs;
+ cfg.urlArgs = function(id, url) {
+ return (url.indexOf('?') === -1 ? '?' : '&') + urlArgs;
+ };
+ }
+
+ // Save off the paths and packages since they require special processing,
+ // they are additive.
+ var shim = config.shim,
+ objs = {
+ paths: true,
+ bundles: true,
+ config: true,
+ map: true
+ };
+
+ eachProp(cfg, function (value, prop) {
+ if (objs[prop]) {
+ if (!config[prop]) {
+ config[prop] = {};
+ }
+ mixin(config[prop], value, true, true);
+ } else {
+ config[prop] = value;
+ }
+ });
+
+ // Reverse map the bundles
+ if (cfg.bundles) {
+ eachProp(cfg.bundles, function (value, prop) {
+ value.forEach(function (v) {
+ if (v !== prop) {
+ bundlesMap[v] = prop;
+ }
+ });
+ });
+ }
+
+ // Merge shim
+ if (cfg.shim) {
+ eachProp(cfg.shim, function (value, id) {
+ // Normalize the structure
+ if (Array.isArray(value)) {
+ value = {
+ deps: value
+ };
+ }
+ if ((value.exports || value.init) && !value.exportsFn) {
+ value.exportsFn = makeShimExports(value);
+ }
+ shim[id] = value;
+ });
+ config.shim = shim;
+ }
+
+ // Adjust packages if necessary.
+ if (cfg.packages) {
+ cfg.packages.forEach(function (pkgObj) {
+ var location, name;
+
+ pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
+
+ name = pkgObj.name;
+ location = pkgObj.location;
+ if (location) {
+ config.paths[name] = pkgObj.location;
+ }
+
+ // Save pointer to main module ID for pkg name.
+ // Remove leading dot in main, so main paths are normalized,
+ // and remove any trailing .js, since different package
+ // envs have different conventions: some use a module name,
+ // some use a file name.
+ config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
+ .replace(currDirRegExp, '')
+ .replace(jsSuffixRegExp, '');
+ });
+ }
+
+ // If a deps array or a config callback is specified, then call
+ // require with those args. This is useful when require is defined as a
+ // config object before require.js is loaded.
+ if (cfg.deps || cfg.callback) {
+ req(cfg.deps, cfg.callback);
+ }
+
+ return req;
+ };
+
+ req.onError = function (err) {
+ throw err;
+ };
+
+ context = {
+ id: contextName,
+ defined: defined,
+ waiting: waiting,
+ config: config,
+ deferreds: deferreds,
+ req: req,
+ execCb: function execCb(name, callback, args, exports) {
+ return callback.apply(exports, args);
+ }
+ };
+
+ contexts[contextName] = context;
+
+ return req;
+ }
+
+ requirejs = topReq = newContext('_');
+
+ if (typeof require !== 'function') {
+ require = topReq;
+ }
+
+ /**
+ * Executes the text. Normally just uses eval, but can be modified
+ * to use a better, environment-specific call. Only used for transpiling
+ * loader plugins, not for plain JS modules.
+ * @param {String} text the text to execute/evaluate.
+ */
+ topReq.exec = function (text) {
+ /*jslint evil: true */
+ return eval(text);
+ };
+
+ topReq.contexts = contexts;
+
+ define = function () {
+ queue.push(slice.call(arguments, 0));
+ };
+
+ define.amd = {
+ jQuery: true
+ };
+
+ if (bootstrapConfig) {
+ topReq.config(bootstrapConfig);
+ }
+
+ // data-main support.
+ if (topReq.isBrowser && !contexts._.config.skipDataMain) {
+ dataMain = document.querySelectorAll('script[data-main]')[0];
+ dataMain = dataMain && dataMain.getAttribute('data-main');
+ if (dataMain) {
+ // Strip off any trailing .js since dataMain is now
+ // like a module name.
+ dataMain = dataMain.replace(jsSuffixRegExp, '');
+
+ // Set final baseUrl if there is not already an explicit one,
+ // but only do so if the data-main value is not a loader plugin
+ // module ID.
+ if ((!bootstrapConfig || !bootstrapConfig.baseUrl) &&
+ dataMain.indexOf('!') === -1) {
+ // Pull off the directory of data-main for use as the
+ // baseUrl.
+ src = dataMain.split('/');
+ dataMain = src.pop();
+ subPath = src.length ? src.join('/') + '/' : './';
+
+ topReq.config({baseUrl: subPath});
+ }
+
+ topReq([dataMain]);
+ }
+ }
+}(this, (typeof Promise !== 'undefined' ? Promise : undefined)));
+
+define("requireLib", function(){});
+
+//noinspection JSUnresolvedVariable
+requirejs.config({
+ paths: {
+ enquire: '3rdParty/enquire',
+ favico: '3rdParty/favico',
+ 'perfect-scrollbar': '3rdParty/perfect-scrollbar',
+ 'Pica': '3rdParty/pica',
+ prism: '3rdParty/prism',
+ },
+ shim: {
+ enquire: { exports: 'enquire' },
+ favico: { exports: 'Favico' },
+ 'perfect-scrollbar': { exports: 'PerfectScrollbar' }
+ },
+ map: {
+ '*': {
+ 'Ajax': 'WoltLabSuite/Core/Ajax',
+ 'AjaxJsonp': 'WoltLabSuite/Core/Ajax/Jsonp',
+ 'AjaxRequest': 'WoltLabSuite/Core/Ajax/Request',
+ 'CallbackList': 'WoltLabSuite/Core/CallbackList',
+ 'ColorUtil': 'WoltLabSuite/Core/ColorUtil',
+ 'Core': 'WoltLabSuite/Core/Core',
+ 'DateUtil': 'WoltLabSuite/Core/Date/Util',
+ 'Devtools': 'WoltLabSuite/Core/Devtools',
+ 'Dictionary': 'WoltLabSuite/Core/Dictionary',
+ 'Dom/ChangeListener': 'WoltLabSuite/Core/Dom/Change/Listener',
+ 'Dom/Traverse': 'WoltLabSuite/Core/Dom/Traverse',
+ 'Dom/Util': 'WoltLabSuite/Core/Dom/Util',
+ 'Environment': 'WoltLabSuite/Core/Environment',
+ 'EventHandler': 'WoltLabSuite/Core/Event/Handler',
+ 'EventKey': 'WoltLabSuite/Core/Event/Key',
+ 'Language': 'WoltLabSuite/Core/Language',
+ 'List': 'WoltLabSuite/Core/List',
+ 'ObjectMap': 'WoltLabSuite/Core/ObjectMap',
+ 'Permission': 'WoltLabSuite/Core/Permission',
+ 'StringUtil': 'WoltLabSuite/Core/StringUtil',
+ 'Ui/Alignment': 'WoltLabSuite/Core/Ui/Alignment',
+ 'Ui/CloseOverlay': 'WoltLabSuite/Core/Ui/CloseOverlay',
+ 'Ui/Confirmation': 'WoltLabSuite/Core/Ui/Confirmation',
+ 'Ui/Dialog': 'WoltLabSuite/Core/Ui/Dialog',
+ 'Ui/Notification': 'WoltLabSuite/Core/Ui/Notification',
+ 'Ui/ReusableDropdown': 'WoltLabSuite/Core/Ui/Dropdown/Reusable',
+ 'Ui/Screen': 'WoltLabSuite/Core/Ui/Screen',
+ 'Ui/Scroll': 'WoltLabSuite/Core/Ui/Scroll',
+ 'Ui/SimpleDropdown': 'WoltLabSuite/Core/Ui/Dropdown/Simple',
+ 'Ui/TabMenu': 'WoltLabSuite/Core/Ui/TabMenu',
+ 'Upload': 'WoltLabSuite/Core/Upload',
+ 'User': 'WoltLabSuite/Core/User'
+ }
+ },
+ waitSeconds: 0
+});
+
+/* Define jQuery shim. We cannot use the shim object in the configuration above,
+ because it tries to load the file, even if the exported global already exists.
+ This shim is needed for jQuery plugins supporting an AMD loaded jQuery, because
+ we break the AMD support of jQuery for BC reasons.
+*/
+define('jquery', [],function() {
+ return window.jQuery;
+});
+
+
+define("require.config", function(){});
+
+/**
+ * Collection of global short hand functions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+(function(window, document) {
+ /**
+ * Shorthand function to retrieve or set an attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @param {?=} value attribute value, omit if attribute should be read
+ * @return {(string|undefined)} attribute value, empty string if attribute is not set or undefined if `value` was omitted
+ */
+ window.elAttr = function(element, attribute, value) {
+ if (value === undefined) {
+ return element.getAttribute(attribute) || '';
+ }
+
+ element.setAttribute(attribute, value);
+ };
+
+ /**
+ * Shorthand function to retrieve a boolean attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @return {boolean} true if value is either `1` or `true`
+ */
+ window.elAttrBool = function(element, attribute) {
+ var value = elAttr(element, attribute);
+
+ return (value === "1" || value === "true");
+ };
+
+ /**
+ * Shorthand function to find elements by class name.
+ *
+ * @param {string} className CSS class name
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @return {NodeList} matching elements
+ */
+ window.elByClass = function(className, context) {
+ return (context || document).getElementsByClassName(className);
+ };
+
+ /**
+ * Shorthand function to retrieve an element by id.
+ *
+ * @param {string} id element id
+ * @return {(Element|null)} matching element or null if not found
+ */
+ window.elById = function(id) {
+ return document.getElementById(id);
+ };
+
+ /**
+ * Shorthand function to find an element by CSS selector.
+ *
+ * @param {string} selector CSS selector
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @return {(Element|null)} matching element or null if no match
+ */
+ window.elBySel = function(selector, context) {
+ return (context || document).querySelector(selector);
+ };
+
+ /**
+ * Shorthand function to find elements by CSS selector.
+ *
+ * @param {string} selector CSS selector
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @param {function=} callback callback function passed to forEach()
+ * @return {NodeList} matching elements
+ */
+ window.elBySelAll = function(selector, context, callback) {
+ var nodeList = (context || document).querySelectorAll(selector);
+ if (typeof callback === 'function') {
+ Array.prototype.forEach.call(nodeList, callback);
+ }
+
+ return nodeList;
+ };
+
+ /**
+ * Shorthand function to find elements by tag name.
+ *
+ * @param {string} tagName element tag name
+ * @param {Element=} context target element, assuming `document` if omitted
+ * @return {NodeList} matching elements
+ */
+ window.elByTag = function(tagName, context) {
+ return (context || document).getElementsByTagName(tagName);
+ };
+
+ /**
+ * Shorthand function to create a DOM element.
+ *
+ * @param {string} tagName element tag name
+ * @return {Element} new DOM element
+ */
+ window.elCreate = function(tagName) {
+ return document.createElement(tagName);
+ };
+
+ /**
+ * Returns the closest element (parent for text nodes), optionally matching
+ * the provided selector.
+ *
+ * @param {Node} node start node
+ * @param {string=} selector optional CSS selector
+ * @return {Element} closest matching element
+ */
+ window.elClosest = function (node, selector) {
+ if (!(node instanceof Node)) {
+ throw new TypeError('Provided element is not a Node.');
+ }
+
+ // retrieve the parent element for text nodes
+ if (node.nodeType === Node.TEXT_NODE) {
+ node = node.parentNode;
+
+ // text node had no parent
+ if (node === null) return null;
+ }
+
+ if (typeof selector !== 'string') selector = '';
+
+ if (selector.length === 0) return node;
+
+ return node.closest(selector);
+ };
+
+ /**
+ * Shorthand function to retrieve or set a 'data-' attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @param {?=} value attribute value, omit if attribute should be read
+ * @return {(string|undefined)} attribute value, empty string if attribute is not set or undefined if `value` was omitted
+ */
+ window.elData = function(element, attribute, value) {
+ attribute = 'data-' + attribute;
+
+ if (value === undefined) {
+ return element.getAttribute(attribute) || '';
+ }
+
+ element.setAttribute(attribute, value);
+ };
+
+ /**
+ * Shorthand function to retrieve a boolean 'data-' attribute.
+ *
+ * @param {Element} element target element
+ * @param {string} attribute attribute name
+ * @return {boolean} true if value is either `1` or `true`
+ */
+ window.elDataBool = function(element, attribute) {
+ var value = elData(element, attribute);
+
+ return (value === "1" || value === "true");
+ };
+
+ /**
+ * Shorthand function to hide an element by setting its 'display' value to 'none'.
+ *
+ * @param {Element} element DOM element
+ */
+ window.elHide = function(element) {
+ element.style.setProperty('display', 'none', '');
+ };
+
+ /**
+ * Shorthand function to check if given element is hidden by setting its 'display'
+ * value to 'none'.
+ *
+ * @param {Element} element DOM element
+ * @return {boolean}
+ */
+ window.elIsHidden = function(element) {
+ return element.style.getPropertyValue('display') === 'none';
+ }
+
+ /**
+ * Displays or removes an error message below the provided element.
+ *
+ * @param {Element} element DOM element
+ * @param {string?} errorMessage error message; `false`, `null` and `undefined` are treated as an empty string
+ * @param {boolean?} isHtml defaults to false, causes `errorMessage` to be treated as text only
+ * @return {?Element} the inner error element or null if it was removed
+ */
+ window.elInnerError = function (element, errorMessage, isHtml) {
+ var parent = element.parentNode;
+ if (parent === null) {
+ throw new Error('Only elements that have a parent element or document are valid.');
+ }
+
+ if (typeof errorMessage !== 'string') {
+ if (errorMessage === undefined || errorMessage === null || errorMessage === false) {
+ errorMessage = '';
+ }
+ else {
+ throw new TypeError('The error message must be a string; `false`, `null` or `undefined` can be used as a substitute for an empty string.');
+ }
+ }
+
+ var innerError = element.nextElementSibling;
+ if (innerError === null || innerError.nodeName !== 'SMALL' || !innerError.classList.contains('innerError')) {
+ if (errorMessage === '') {
+ innerError = null;
+ }
+ else {
+ innerError = elCreate('small');
+ innerError.className = 'innerError';
+ parent.insertBefore(innerError, element.nextSibling);
+ }
+ }
+
+ if (errorMessage === '') {
+ if (innerError !== null) {
+ parent.removeChild(innerError);
+ innerError = null;
+ }
+ }
+ else {
+ innerError[(isHtml ? 'innerHTML' : 'textContent')] = errorMessage;
+ }
+
+ return innerError;
+ };
+
+ /**
+ * Shorthand function to remove an element.
+ *
+ * @param {Node} element DOM node
+ */
+ window.elRemove = function(element) {
+ element.parentNode.removeChild(element);
+ };
+
+ /**
+ * Shorthand function to show an element previously hidden by using `elHide()`.
+ *
+ * @param {Element} element DOM element
+ */
+ window.elShow = function(element) {
+ element.style.removeProperty('display');
+ };
+
+ /**
+ * Toggles visibility of an element using the display style.
+ *
+ * @param {Element} element DOM element
+ */
+ window.elToggle = function (element) {
+ if (element.style.getPropertyValue('display') === 'none') {
+ elShow(element);
+ }
+ else {
+ elHide(element);
+ }
+ };
+
+ /**
+ * Shorthand function to iterative over an array-like object, arguments passed are the value and the index second.
+ *
+ * Do not use this function if a simple `for()` is enough or `list` is a plain object.
+ *
+ * @param {object} list array-like object
+ * @param {function} callback callback function
+ */
+ window.forEach = function(list, callback) {
+ for (var i = 0, length = list.length; i < length; i++) {
+ callback(list[i], i);
+ }
+ };
+
+ /**
+ * Shorthand function to check if an object has a property while ignoring the chain.
+ *
+ * @param {object} obj target object
+ * @param {string} property property name
+ * @return {boolean} false if property does not exist or belongs to the chain
+ */
+ window.objOwns = function(obj, property) {
+ return obj.hasOwnProperty(property);
+ };
+
+ /* assigns a global constant defining the proper 'click' event depending on the browser,
+ enforcing 'touchstart' on mobile devices for a better UX. We're using defineProperty()
+ here because at the time of writing Safari does not support 'const'. Thanks Safari.
+ */
+ var clickEvent = ('touchstart' in document.documentElement || 'ontouchstart' in window || navigator.MaxTouchPoints > 0 || navigator.msMaxTouchPoints > 0) ? 'touchstart' : 'click';
+ Object.defineProperty(window, 'WCF_CLICK_EVENT', {
+ value: 'click' //clickEvent
+ });
+
+ /* Overwrites any history states after 'initial' with 'skip' on initial page load.
+ This is done, as the necessary DOM of other history states may not exist any more.
+ On forward navigation these 'skip' states are automatically skipped, otherwise the
+ user might have to press the forward button several times.
+ Note: A 'skip' state cannot be hit in the 'popstate' event when navigation backwards,
+ because the history already is left of all the 'skip' states for the current page.
+ Note 2: Setting the URL component of `history.replaceState()` to an empty string will
+ cause the Internet Explorer to discard the path and query string from the
+ address bar.
+ */
+ (function() {
+ var stateDepth = 0;
+ function check() {
+ if (window.history.state && window.history.state.name && window.history.state.name !== 'initial') {
+ window.history.replaceState({
+ name: 'skip',
+ depth: ++stateDepth
+ }, '');
+ window.history.back();
+
+ // window.history does not update in this iteration of the event loop
+ setTimeout(check, 1);
+ }
+ else {
+ window.history.replaceState({name: 'initial'}, '');
+ }
+ }
+ check();
+
+ window.addEventListener('popstate', function(event) {
+ if (event.state && event.state.name && event.state.name === 'skip') {
+ window.history.go(event.state.depth);
+ }
+ });
+ })();
+
+ /**
+ * Provides a hashCode() method for strings, similar to Java's String.hashCode().
+ *
+ * @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
+ */
+ window.String.prototype.hashCode = function() {
+ var $char;
+ var $hash = 0;
+
+ if (this.length) {
+ for (var $i = 0, $length = this.length; $i < $length; $i++) {
+ $char = this.charCodeAt($i);
+ $hash = (($hash << 5) - $hash) + $char;
+ $hash = $hash & $hash; // convert to 32bit integer
+ }
+ }
+
+ return $hash;
+ };
+})(window, document);
+
+define("wcf.globalHelper", function(){});
+
+/**
+ * Provides the basic core functionality.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Core
+ */
+define('WoltLabSuite/Core/Core',[], function() {
+ "use strict";
+
+ var _clone = function(variable) {
+ if (typeof variable === 'object' && (Array.isArray(variable) || Core.isPlainObject(variable))) {
+ return _cloneObject(variable);
+ }
+
+ return variable;
+ };
+
+ var _cloneObject = function(obj) {
+ if (!obj) {
+ return null;
+ }
+
+ if (Array.isArray(obj)) {
+ return obj.slice();
+ }
+
+ var newObj = {};
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') {
+ newObj[key] = _clone(obj[key]);
+ }
+ }
+
+ return newObj;
+ };
+
+ //noinspection JSUnresolvedVariable
+ var _prefix = 'wsc' + window.WCF_PATH.hashCode() + '-';
+
+ /**
+ * @exports WoltLabSuite/Core/Core
+ */
+ var Core = {
+ /**
+ * Deep clones an object.
+ *
+ * @param {object} obj source object
+ * @return {object} cloned object
+ */
+ clone: function(obj) {
+ return _clone(obj);
+ },
+
+ /**
+ * Converts WCF 2.0-style URLs into the default URL layout.
+ *
+ * @param string url target url
+ * @return rewritten url
+ */
+ convertLegacyUrl: function(url) {
+ return url.replace(/^index\.php\/(.*?)\/\?/, function(match, controller) {
+ var parts = controller.split(/([A-Z][a-z0-9]+)/);
+ controller = '';
+ for (var i = 0, length = parts.length; i < length; i++) {
+ var part = parts[i].trim();
+ if (part.length) {
+ if (controller.length) controller += '-';
+ controller += part.toLowerCase();
+ }
+ }
+
+ return 'index.php?' + controller + '/&';
+ });
+ },
+
+ /**
+ * Merges objects with the first argument.
+ *
+ * @param {object} out destination object
+ * @param {...object} arguments variable number of objects to be merged into the destination object
+ * @return {object} destination object with all provided objects merged into
+ */
+ extend: function(out) {
+ out = out || {};
+ var newObj = this.clone(out);
+
+ for (var i = 1, length = arguments.length; i < length; i++) {
+ var obj = arguments[i];
+
+ if (!obj) continue;
+
+ for (var key in obj) {
+ if (objOwns(obj, key)) {
+ if (!Array.isArray(obj[key]) && typeof obj[key] === 'object') {
+ if (this.isPlainObject(obj[key])) {
+ // object literals have the prototype of Object which in return has no parent prototype
+ newObj[key] = this.extend(out[key], obj[key]);
+ }
+ else {
+ newObj[key] = obj[key];
+ }
+ }
+ else {
+ newObj[key] = obj[key];
+ }
+ }
+ }
+ }
+
+ return newObj;
+ },
+
+ /**
+ * Inherits the prototype methods from one constructor to another
+ * constructor.
+ *
+ * Usage:
+ *
+ * function MyDerivedClass() {}
+ * Core.inherit(MyDerivedClass, TheAwesomeBaseClass, {
+ * // regular prototype for `MyDerivedClass`
+ *
+ * overwrittenMethodFromBaseClass: function(foo, bar) {
+ * // do stuff
+ *
+ * // invoke parent
+ * MyDerivedClass._super.prototype.overwrittenMethodFromBaseClass.call(this, foo, bar);
+ * }
+ * });
+ *
+ * @see https://github.com/nodejs/node/blob/7d14dd9b5e78faabb95d454a79faa513d0bbc2a5/lib/util.js#L697-L735
+ * @param {function} constructor inheriting constructor function
+ * @param {function} superConstructor inherited constructor function
+ * @param {object=} propertiesObject additional prototype properties
+ */
+ inherit: function(constructor, superConstructor, propertiesObject) {
+ if (constructor === undefined || constructor === null) {
+ throw new TypeError("The constructor must not be undefined or null.");
+ }
+ if (superConstructor === undefined || superConstructor === null) {
+ throw new TypeError("The super constructor must not be undefined or null.");
+ }
+ if (superConstructor.prototype === undefined) {
+ throw new TypeError("The super constructor must have a prototype.");
+ }
+
+ constructor._super = superConstructor;
+ constructor.prototype = Core.extend(Object.create(superConstructor.prototype, {
+ constructor: {
+ configurable: true,
+ enumerable: false,
+ value: constructor,
+ writable: true
+ }
+ }), propertiesObject || {});
+ },
+
+ /**
+ * Returns true if `obj` is an object literal.
+ *
+ * @param {*} obj target object
+ * @returns {boolean} true if target is an object literal
+ */
+ isPlainObject: function(obj) {
+ if (typeof obj !== 'object' || obj === null || obj.nodeType) {
+ return false;
+ }
+
+ return (Object.getPrototypeOf(obj) === Object.prototype);
+ },
+
+ /**
+ * Returns the object's class name.
+ *
+ * @param {object} obj target object
+ * @return {string} object class name
+ */
+ getType: function(obj) {
+ return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1');
+ },
+
+ /**
+ * Returns a RFC4122 version 4 compilant UUID.
+ *
+ * @see http://stackoverflow.com/a/2117523
+ * @return {string}
+ */
+ getUuid: function() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ });
+ },
+
+ /**
+ * Recursively serializes an object into an encoded URI parameter string.
+ *
+ * @param {object} obj target object
+ * @param {string=} prefix parameter prefix
+ * @return {string} encoded parameter string
+ */
+ serialize: function(obj, prefix) {
+ var parameters = [];
+
+ for (var key in obj) {
+ if (objOwns(obj, key)) {
+ var parameterKey = (prefix) ? prefix + '[' + key + ']' : key;
+ var value = obj[key];
+
+ if (typeof value === 'object') {
+ parameters.push(this.serialize(value, parameterKey));
+ }
+ else {
+ parameters.push(encodeURIComponent(parameterKey) + '=' + encodeURIComponent(value));
+ }
+ }
+ }
+
+ return parameters.join('&');
+ },
+
+ /**
+ * Triggers a custom or built-in event.
+ *
+ * @param {Element} element target element
+ * @param {string} eventName event name
+ */
+ triggerEvent: function(element, eventName) {
+ var event;
+
+ try {
+ event = new Event(eventName, {
+ bubbles: true,
+ cancelable: true
+ });
+ }
+ catch (e) {
+ event = document.createEvent('Event');
+ event.initEvent(eventName, true, true);
+ }
+
+ element.dispatchEvent(event);
+ },
+
+ /**
+ * Returns the unique prefix for the localStorage.
+ *
+ * @return {string} prefix for the localStorage
+ */
+ getStoragePrefix: function() {
+ return _prefix;
+ }
+ };
+
+ return Core;
+});
+
+/**
+ * Dictionary implementation relying on an object or if supported on a Map to hold key => value data.
+ *
+ * If you're looking for a dictionary with object keys, please see `WoltLabSuite/Core/ObjectMap`.
+ *
+ * @author Tim Duesterhus, Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dictionary
+ */
+define('WoltLabSuite/Core/Dictionary',['Core'], function(Core) {
+ "use strict";
+
+ var _hasMap = objOwns(window, 'Map') && typeof window.Map === 'function';
+
+ /**
+ * @constructor
+ */
+ function Dictionary() {
+ this._dictionary = (_hasMap) ? new Map() : {};
+ }
+ Dictionary.prototype = {
+ /**
+ * Sets a new key with given value, will overwrite an existing key.
+ *
+ * @param {(number|string)} key key
+ * @param {?} value value
+ */
+ set: function(key, value) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (typeof key !== "string") {
+ throw new TypeError("Only strings can be used as keys, rejected '" + key + "' (" + typeof key + ").");
+ }
+
+ if (_hasMap) this._dictionary.set(key, value);
+ else this._dictionary[key] = value;
+ },
+
+ /**
+ * Removes a key from the dictionary.
+ *
+ * @param {(number|string)} key key
+ */
+ 'delete': function(key) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (_hasMap) this._dictionary['delete'](key);
+ else this._dictionary[key] = undefined;
+ },
+
+ /**
+ * Returns true if dictionary contains a value for given key and is not undefined.
+ *
+ * @param {(number|string)} key key
+ * @return {boolean} true if key exists and value is not undefined
+ */
+ has: function(key) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (_hasMap) return this._dictionary.has(key);
+ else {
+ return (objOwns(this._dictionary, key) && typeof this._dictionary[key] !== "undefined");
+ }
+ },
+
+ /**
+ * Retrieves a value by key, returns undefined if there is no match.
+ *
+ * @param {(number|string)} key key
+ * @return {*}
+ */
+ get: function(key) {
+ if (typeof key === 'number') key = key.toString();
+
+ if (this.has(key)) {
+ if (_hasMap) return this._dictionary.get(key);
+ else return this._dictionary[key];
+ }
+
+ return undefined;
+ },
+
+ /**
+ * Iterates over the dictionary keys and values, callback function should expect the
+ * value as first parameter and the key name second.
+ *
+ * @param {function<*, string>} callback callback for each iteration
+ */
+ forEach: function(callback) {
+ if (typeof callback !== "function") {
+ throw new TypeError("forEach() expects a callback as first parameter.");
+ }
+
+ if (_hasMap) {
+ this._dictionary.forEach(callback);
+ }
+ else {
+ var keys = Object.keys(this._dictionary);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ callback(this._dictionary[keys[i]], keys[i]);
+ }
+ }
+ },
+
+ /**
+ * Merges one or more Dictionary instances into this one.
+ *
+ * @param {...Dictionary} var_args one or more Dictionary instances
+ */
+ merge: function() {
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var dictionary = arguments[i];
+ if (!(dictionary instanceof Dictionary)) {
+ throw new TypeError("Expected an object of type Dictionary, but argument " + i + " is not.");
+ }
+
+ dictionary.forEach((function(value, key) {
+ this.set(key, value);
+ }).bind(this));
+ }
+ },
+
+ /**
+ * Returns the object representation of the dictionary.
+ *
+ * @return {object} dictionary's object representation
+ */
+ toObject: function() {
+ if (!_hasMap) return Core.clone(this._dictionary);
+
+ var object = { };
+ this._dictionary.forEach(function(value, key) {
+ object[key] = value;
+ });
+
+ return object;
+ }
+ };
+
+ /**
+ * Creates a new Dictionary based on the given object.
+ * All properties that are owned by the object will be added
+ * as keys to the resulting Dictionary.
+ *
+ * @param {object} object
+ * @return {Dictionary}
+ */
+ Dictionary.fromObject = function(object) {
+ var result = new Dictionary();
+
+ for (var key in object) {
+ if (objOwns(object, key)) {
+ result.set(key, object[key]);
+ }
+ }
+
+ return result;
+ };
+
+ Object.defineProperty(Dictionary.prototype, 'size', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
+ if (_hasMap) {
+ return this._dictionary.size;
+ }
+ else {
+ return Object.keys(this._dictionary).length;
+ }
+ }
+ });
+
+ return Dictionary;
+});
+
+
+
+define('WoltLabSuite/Core/Template.grammar',['require'],function(require){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[2,37],$V1=[5,9,11,12,13,18,19,21,22,23,25,26,27,28,30,31,32,33,35,37,39],$V2=[1,24],$V3=[1,25],$V4=[1,31],$V5=[1,29],$V6=[1,30],$V7=[1,26],$V8=[1,27],$V9=[1,33],$Va=[11,12,15,40,41,45,47,49,50,52],$Vb=[9,11,12,13,18,19,21,23,26,28,30,31,32,33,35,37],$Vc=[11,12,15,40,41,44,45,46,47,49,50,52],$Vd=[18,35,37],$Ve=[12,15];
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"TEMPLATE":3,"CHUNK_STAR":4,"EOF":5,"CHUNK_STAR_repetition0":6,"CHUNK":7,"PLAIN_ANY":8,"T_LITERAL":9,"COMMAND":10,"T_ANY":11,"T_WS":12,"{if":13,"COMMAND_PARAMETERS":14,"}":15,"COMMAND_repetition0":16,"COMMAND_option0":17,"{/if}":18,"{include":19,"COMMAND_PARAMETER_LIST":20,"{implode":21,"{/implode}":22,"{foreach":23,"COMMAND_option1":24,"{/foreach}":25,"{lang}":26,"{/lang}":27,"{":28,"VARIABLE":29,"{#":30,"{@":31,"{ldelim}":32,"{rdelim}":33,"ELSE":34,"{else}":35,"ELSE_IF":36,"{elseif":37,"FOREACH_ELSE":38,"{foreachelse}":39,"T_VARIABLE":40,"T_VARIABLE_NAME":41,"VARIABLE_repetition0":42,"VARIABLE_SUFFIX":43,"[":44,"]":45,".":46,"(":47,"VARIABLE_SUFFIX_option0":48,")":49,"=":50,"COMMAND_PARAMETER_VALUE":51,"T_QUOTED_STRING":52,"COMMAND_PARAMETERS_repetition_plus0":53,"COMMAND_PARAMETER":54,"$accept":0,"$end":1},
+terminals_: {2:"error",5:"EOF",9:"T_LITERAL",11:"T_ANY",12:"T_WS",13:"{if",15:"}",18:"{/if}",19:"{include",21:"{implode",22:"{/implode}",23:"{foreach",25:"{/foreach}",26:"{lang}",27:"{/lang}",28:"{",30:"{#",31:"{@",32:"{ldelim}",33:"{rdelim}",35:"{else}",37:"{elseif",39:"{foreachelse}",40:"T_VARIABLE",41:"T_VARIABLE_NAME",44:"[",45:"]",46:".",47:"(",49:")",50:"=",52:"T_QUOTED_STRING"},
+productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[8,1],[8,1],[10,7],[10,3],[10,5],[10,6],[10,3],[10,3],[10,3],[10,3],[10,1],[10,1],[34,2],[36,4],[38,2],[29,3],[43,3],[43,2],[43,3],[20,5],[20,3],[51,1],[51,1],[14,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,3],[6,0],[6,2],[16,0],[16,2],[17,0],[17,1],[24,0],[24,1],[42,0],[42,2],[48,0],[48,1],[53,1],[53,2]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1:
+ return $$[$0-1] + ";";
+break;
+case 2:
+
+ var result = $$[$0].reduce(function (carry, item) {
+ if (item.encode && !carry[1]) carry[0] += " + '" + item.value;
+ else if (item.encode && carry[1]) carry[0] += item.value;
+ else if (!item.encode && carry[1]) carry[0] += "' + " + item.value;
+ else if (!item.encode && !carry[1]) carry[0] += " + " + item.value;
+
+ carry[1] = item.encode;
+ return carry;
+ }, [ "''", false ]);
+ if (result[1]) result[0] += "'";
+
+ this.$ = result[0];
+
+break;
+case 3: case 4:
+this.$ = { encode: true, value: $$[$0].replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/(\r\n|\n|\r)/g, '\\n') };
+break;
+case 5:
+this.$ = { encode: false, value: $$[$0] };
+break;
+case 8:
+
+ this.$ = "(function() { if (" + $$[$0-5] + ") { return " + $$[$0-3] + "; } " + $$[$0-2].join(' ') + " " + ($$[$0-1] || '') + " return ''; })()";
+
+break;
+case 9:
+
+ if (!$$[$0-1]['file']) throw new Error('Missing parameter file');
+
+ this.$ = $$[$0-1]['file'] + ".fetch(v)";
+
+break;
+case 10:
+
+ if (!$$[$0-3]['from']) throw new Error('Missing parameter from');
+ if (!$$[$0-3]['item']) throw new Error('Missing parameter item');
+ if (!$$[$0-3]['glue']) $$[$0-3]['glue'] = "', '";
+
+ this.$ = "(function() { return " + $$[$0-3]['from'] + ".map(function(item) { v[" + $$[$0-3]['item'] + "] = item; return " + $$[$0-1] + "; }).join(" + $$[$0-3]['glue'] + "); })()";
+
+break;
+case 11:
+
+ if (!$$[$0-4]['from']) throw new Error('Missing parameter from');
+ if (!$$[$0-4]['item']) throw new Error('Missing parameter item');
+
+ this.$ = "(function() {"
+ + "var looped = false, result = '';"
+ + "if (" + $$[$0-4]['from'] + " instanceof Array) {"
+ + "for (var i = 0; i < " + $$[$0-4]['from'] + ".length; i++) { looped = true;"
+ + "v[" + $$[$0-4]['key'] + "] = i;"
+ + "v[" + $$[$0-4]['item'] + "] = " + $$[$0-4]['from'] + "[i];"
+ + "result += " + $$[$0-2] + ";"
+ + "}"
+ + "} else {"
+ + "for (var key in " + $$[$0-4]['from'] + ") {"
+ + "if (!" + $$[$0-4]['from'] + ".hasOwnProperty(key)) continue;"
+ + "looped = true;"
+ + "v[" + $$[$0-4]['key'] + "] = key;"
+ + "v[" + $$[$0-4]['item'] + "] = " + $$[$0-4]['from'] + "[key];"
+ + "result += " + $$[$0-2] + ";"
+ + "}"
+ + "}"
+ + "return (looped ? result : " + ($$[$0-1] || "''") + "); })()"
+
+break;
+case 12:
+this.$ = "Language.get(" + $$[$0-1] + ", v)";
+break;
+case 13:
+this.$ = "StringUtil.escapeHTML(" + $$[$0-1] + ")";
+break;
+case 14:
+this.$ = "StringUtil.formatNumeric(" + $$[$0-1] + ")";
+break;
+case 15:
+this.$ = $$[$0-1];
+break;
+case 16:
+this.$ = "'{'";
+break;
+case 17:
+this.$ = "'}'";
+break;
+case 18:
+this.$ = "else { return " + $$[$0] + "; }";
+break;
+case 19:
+this.$ = "else if (" + $$[$0-2] + ") { return " + $$[$0] + "; }";
+break;
+case 20:
+this.$ = $$[$0];
+break;
+case 21:
+this.$ = "v['" + $$[$0-1] + "']" + $$[$0].join('');;
+break;
+case 22:
+this.$ = $$[$0-2] + $$[$0-1] + $$[$0];
+break;
+case 23:
+this.$ = "['" + $$[$0] + "']";
+break;
+case 24: case 36:
+this.$ = $$[$0-2] + ($$[$0-1] || '') + $$[$0];
+break;
+case 25:
+ this.$ = $$[$0]; this.$[$$[$0-4]] = $$[$0-2];
+break;
+case 26:
+ this.$ = {}; this.$[$$[$0-2]] = $$[$0];
+break;
+case 29:
+this.$ = $$[$0].join('');
+break;
+case 37: case 39: case 45:
+this.$ = [];
+break;
+case 38: case 40: case 46: case 50:
+$$[$0-1].push($$[$0]);
+break;
+case 49:
+this.$ = [$$[$0]];
+break;
+}
+},
+table: [o([5,9,11,12,13,19,21,23,26,28,30,31,32,33],$V0,{3:1,4:2,6:3}),{1:[3]},{5:[1,4]},o([5,18,22,25,27,35,37,39],[2,2],{7:5,8:6,10:8,9:[1,7],11:[1,9],12:[1,10],13:[1,11],19:[1,12],21:[1,13],23:[1,14],26:[1,15],28:[1,16],30:[1,17],31:[1,18],32:[1,19],33:[1,20]}),{1:[2,1]},o($V1,[2,38]),o($V1,[2,3]),o($V1,[2,4]),o($V1,[2,5]),o($V1,[2,6]),o($V1,[2,7]),{11:$V2,12:$V3,14:21,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},{20:32,41:$V9},{20:34,41:$V9},{20:35,41:$V9},o([9,11,12,13,19,21,23,26,27,28,30,31,32,33],$V0,{6:3,4:36}),{29:37,40:$V4},{29:38,40:$V4},{29:39,40:$V4},o($V1,[2,16]),o($V1,[2,17]),{15:[1,40]},o([15,45,49],[2,29],{29:28,54:41,11:$V2,12:$V3,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8}),o($Va,[2,49]),o($Va,[2,30]),o($Va,[2,31]),o($Va,[2,32]),o($Va,[2,33]),o($Va,[2,34]),o($Va,[2,35]),{11:$V2,12:$V3,14:42,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},{41:[1,43]},{15:[1,44]},{50:[1,45]},{15:[1,46]},{15:[1,47]},{27:[1,48]},{15:[1,49]},{15:[1,50]},{15:[1,51]},o($Vb,$V0,{6:3,4:52}),o($Va,[2,50]),{49:[1,53]},o($Vc,[2,45],{42:54}),o($V1,[2,9]),{29:57,40:$V4,51:55,52:[1,56]},o([9,11,12,13,19,21,22,23,26,28,30,31,32,33],$V0,{6:3,4:58}),o([9,11,12,13,19,21,23,25,26,28,30,31,32,33,39],$V0,{6:3,4:59}),o($V1,[2,12]),o($V1,[2,13]),o($V1,[2,14]),o($V1,[2,15]),o($Vd,[2,39],{16:60}),o($Va,[2,36]),o([11,12,15,40,41,45,49,50,52],[2,21],{43:61,44:[1,62],46:[1,63],47:[1,64]}),{12:[1,65],15:[2,26]},o($Ve,[2,27]),o($Ve,[2,28]),{22:[1,66]},{24:67,25:[2,43],38:68,39:[1,69]},{17:70,18:[2,41],34:72,35:[1,74],36:71,37:[1,73]},o($Vc,[2,46]),{11:$V2,12:$V3,14:75,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},{41:[1,76]},{11:$V2,12:$V3,14:78,29:28,40:$V4,41:$V5,47:$V6,48:77,49:[2,47],50:$V7,52:$V8,53:22,54:23},{20:79,41:$V9},o($V1,[2,10]),{25:[1,80]},{25:[2,44]},o([9,11,12,13,19,21,23,25,26,28,30,31,32,33],$V0,{6:3,4:81}),{18:[1,82]},o($Vd,[2,40]),{18:[2,42]},{11:$V2,12:$V3,14:83,29:28,40:$V4,41:$V5,47:$V6,50:$V7,52:$V8,53:22,54:23},o([9,11,12,13,18,19,21,23,26,28,30,31,32,33],$V0,{6:3,4:84}),{45:[1,85]},o($Vc,[2,23]),{49:[1,86]},{49:[2,48]},{15:[2,25]},o($V1,[2,11]),{25:[2,20]},o($V1,[2,8]),{15:[1,87]},{18:[2,18]},o($Vc,[2,22]),o($Vc,[2,24]),o($Vb,$V0,{6:3,4:88}),o($Vd,[2,19])],
+defaultActions: {4:[2,1],68:[2,44],72:[2,42],78:[2,48],79:[2,25],81:[2,20],84:[2,18]},
+parseError: function parseError(str, hash) {
+ if (hash.recoverable) {
+ this.trace(str);
+ } else {
+ function _parseError (msg, hash) {
+ this.message = msg;
+ this.hash = hash;
+ }
+ _parseError.prototype = Error;
+
+ throw new _parseError(str, hash);
+ }
+},
+parse: function parse(input) {
+ var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+ var args = lstack.slice.call(arguments, 1);
+ var lexer = Object.create(this.lexer);
+ var sharedState = { yy: {} };
+ for (var k in this.yy) {
+ if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+ sharedState.yy[k] = this.yy[k];
+ }
+ }
+ lexer.setInput(input, sharedState.yy);
+ sharedState.yy.lexer = lexer;
+ sharedState.yy.parser = this;
+ if (typeof lexer.yylloc == 'undefined') {
+ lexer.yylloc = {};
+ }
+ var yyloc = lexer.yylloc;
+ lstack.push(yyloc);
+ var ranges = lexer.options && lexer.options.ranges;
+ if (typeof sharedState.yy.parseError === 'function') {
+ this.parseError = sharedState.yy.parseError;
+ } else {
+ this.parseError = Object.getPrototypeOf(this).parseError;
+ }
+ function popStack(n) {
+ stack.length = stack.length - 2 * n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+ _token_stack:
+ var lex = function () {
+ var token;
+ token = lexer.lex() || EOF;
+ if (typeof token !== 'number') {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ };
+ var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+ while (true) {
+ state = stack[stack.length - 1];
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol === null || typeof symbol == 'undefined') {
+ symbol = lex();
+ }
+ action = table[state] && table[state][symbol];
+ }
+ if (typeof action === 'undefined' || !action.length || !action[0]) {
+ var errStr = '';
+ expected = [];
+ for (p in table[state]) {
+ if (this.terminals_[p] && p > TERROR) {
+ expected.push('\'' + this.terminals_[p] + '\'');
+ }
+ }
+ if (lexer.showPosition) {
+ errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+ } else {
+ errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+ }
+ this.parseError(errStr, {
+ text: lexer.match,
+ token: this.terminals_[symbol] || symbol,
+ line: lexer.yylineno,
+ loc: yyloc,
+ expected: expected
+ });
+ }
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+ }
+ switch (action[0]) {
+ case 1:
+ stack.push(symbol);
+ vstack.push(lexer.yytext);
+ lstack.push(lexer.yylloc);
+ stack.push(action[1]);
+ symbol = null;
+ if (!preErrorSymbol) {
+ yyleng = lexer.yyleng;
+ yytext = lexer.yytext;
+ yylineno = lexer.yylineno;
+ yyloc = lexer.yylloc;
+ if (recovering > 0) {
+ recovering--;
+ }
+ } else {
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+ case 2:
+ len = this.productions_[action[1]][1];
+ yyval.$ = vstack[vstack.length - len];
+ yyval._$ = {
+ first_line: lstack[lstack.length - (len || 1)].first_line,
+ last_line: lstack[lstack.length - 1].last_line,
+ first_column: lstack[lstack.length - (len || 1)].first_column,
+ last_column: lstack[lstack.length - 1].last_column
+ };
+ if (ranges) {
+ yyval._$.range = [
+ lstack[lstack.length - (len || 1)].range[0],
+ lstack[lstack.length - 1].range[1]
+ ];
+ }
+ r = this.performAction.apply(yyval, [
+ yytext,
+ yyleng,
+ yylineno,
+ sharedState.yy,
+ action[1],
+ vstack,
+ lstack
+ ].concat(args));
+ if (typeof r !== 'undefined') {
+ return r;
+ }
+ if (len) {
+ stack = stack.slice(0, -1 * len * 2);
+ vstack = vstack.slice(0, -1 * len);
+ lstack = lstack.slice(0, -1 * len);
+ }
+ stack.push(this.productions_[action[1]][0]);
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+ stack.push(newState);
+ break;
+ case 3:
+ return true;
+ }
+ }
+ return true;
+}};
+
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+ if (this.yy.parser) {
+ this.yy.parser.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+ this.yy = yy || this.yy || {};
+ this._input = input;
+ this._more = this._backtrack = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = '';
+ this.conditionStack = ['INITIAL'];
+ this.yylloc = {
+ first_line: 1,
+ first_column: 0,
+ last_line: 1,
+ last_column: 0
+ };
+ if (this.options.ranges) {
+ this.yylloc.range = [0,0];
+ }
+ this.offset = 0;
+ return this;
+ },
+
+// consumes and returns one char from the input
+input:function () {
+ var ch = this._input[0];
+ this.yytext += ch;
+ this.yyleng++;
+ this.offset++;
+ this.match += ch;
+ this.matched += ch;
+ var lines = ch.match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno++;
+ this.yylloc.last_line++;
+ } else {
+ this.yylloc.last_column++;
+ }
+ if (this.options.ranges) {
+ this.yylloc.range[1]++;
+ }
+
+ this._input = this._input.slice(1);
+ return ch;
+ },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+ var len = ch.length;
+ var lines = ch.split(/(?:\r\n?|\n)/g);
+
+ this._input = ch + this._input;
+ this.yytext = this.yytext.substr(0, this.yytext.length - len);
+ //this.yyleng -= len;
+ this.offset -= len;
+ var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+ this.match = this.match.substr(0, this.match.length - 1);
+ this.matched = this.matched.substr(0, this.matched.length - 1);
+
+ if (lines.length - 1) {
+ this.yylineno -= lines.length - 1;
+ }
+ var r = this.yylloc.range;
+
+ this.yylloc = {
+ first_line: this.yylloc.first_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.first_column,
+ last_column: lines ?
+ (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+ + oldLines[oldLines.length - lines.length].length - lines[0].length :
+ this.yylloc.first_column - len
+ };
+
+ if (this.options.ranges) {
+ this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+ }
+ this.yyleng = this.yytext.length;
+ return this;
+ },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+ this._more = true;
+ return this;
+ },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+ if (this.options.backtrack_lexer) {
+ this._backtrack = true;
+ } else {
+ return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+ text: "",
+ token: null,
+ line: this.yylineno
+ });
+
+ }
+ return this;
+ },
+
+// retain first n characters of the match
+less:function (n) {
+ this.unput(this.match.slice(n));
+ },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+ },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20-next.length);
+ }
+ return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+ },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c + "^";
+ },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+ var token,
+ lines,
+ backup;
+
+ if (this.options.backtrack_lexer) {
+ // save context
+ backup = {
+ yylineno: this.yylineno,
+ yylloc: {
+ first_line: this.yylloc.first_line,
+ last_line: this.last_line,
+ first_column: this.yylloc.first_column,
+ last_column: this.yylloc.last_column
+ },
+ yytext: this.yytext,
+ match: this.match,
+ matches: this.matches,
+ matched: this.matched,
+ yyleng: this.yyleng,
+ offset: this.offset,
+ _more: this._more,
+ _input: this._input,
+ yy: this.yy,
+ conditionStack: this.conditionStack.slice(0),
+ done: this.done
+ };
+ if (this.options.ranges) {
+ backup.yylloc.range = this.yylloc.range.slice(0);
+ }
+ }
+
+ lines = match[0].match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno += lines.length;
+ }
+ this.yylloc = {
+ first_line: this.yylloc.last_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ?
+ lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+ this.yylloc.last_column + match[0].length
+ };
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ if (this.options.ranges) {
+ this.yylloc.range = [this.offset, this.offset += this.yyleng];
+ }
+ this._more = false;
+ this._backtrack = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+ if (this.done && this._input) {
+ this.done = false;
+ }
+ if (token) {
+ return token;
+ } else if (this._backtrack) {
+ // recover context
+ for (var k in backup) {
+ this[k] = backup[k];
+ }
+ return false; // rule action called reject() implying the next rule should be tested instead.
+ }
+ return false;
+ },
+
+// return next match in input
+next:function () {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) {
+ this.done = true;
+ }
+
+ var token,
+ match,
+ tempMatch,
+ index;
+ if (!this._more) {
+ this.yytext = '';
+ this.match = '';
+ }
+ var rules = this._currentRules();
+ for (var i = 0; i < rules.length; i++) {
+ tempMatch = this._input.match(this.rules[rules[i]]);
+ if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+ match = tempMatch;
+ index = i;
+ if (this.options.backtrack_lexer) {
+ token = this.test_match(tempMatch, rules[i]);
+ if (token !== false) {
+ return token;
+ } else if (this._backtrack) {
+ match = false;
+ continue; // rule action called reject() implying a rule MISmatch.
+ } else {
+ // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+ return false;
+ }
+ } else if (!this.options.flex) {
+ break;
+ }
+ }
+ }
+ if (match) {
+ token = this.test_match(match, rules[index]);
+ if (token !== false) {
+ return token;
+ }
+ // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+ return false;
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+ text: "",
+ token: null,
+ line: this.yylineno
+ });
+ }
+ },
+
+// return next match that has a token
+lex:function lex() {
+ var r = this.next();
+ if (r) {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+ var n = this.conditionStack.length - 1;
+ if (n > 0) {
+ return this.conditionStack.pop();
+ } else {
+ return this.conditionStack[0];
+ }
+ },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+ if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+ return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+ } else {
+ return this.conditions["INITIAL"].rules;
+ }
+ },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+ n = this.conditionStack.length - 1 - Math.abs(n || 0);
+ if (n >= 0) {
+ return this.conditionStack[n];
+ } else {
+ return "INITIAL";
+ }
+ },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+ this.begin(condition);
+ },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+ return this.conditionStack.length;
+ },
+options: {},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:/* comment */
+break;
+case 1: yy_.yytext = yy_.yytext.substring(9, yy_.yytext.length - 10); return 9;
+break;
+case 2:return 52;
+break;
+case 3:return 52;
+break;
+case 4:return 40;
+break;
+case 5: return 41;
+break;
+case 6:return 46;
+break;
+case 7:return 44;
+break;
+case 8:return 45;
+break;
+case 9:return 47;
+break;
+case 10:return 49;
+break;
+case 11:return 50;
+break;
+case 12:return 32;
+break;
+case 13:return 33;
+break;
+case 14: this.begin('command'); return 30;
+break;
+case 15: this.begin('command'); return 31;
+break;
+case 16: this.begin('command'); return 13;
+break;
+case 17: this.begin('command'); return 37;
+break;
+case 18: this.begin('command'); return 37;
+break;
+case 19:return 35;
+break;
+case 20:return 18;
+break;
+case 21:return 26;
+break;
+case 22:return 27;
+break;
+case 23: this.begin('command'); return 19;
+break;
+case 24: this.begin('command'); return 21;
+break;
+case 25:return 22;
+break;
+case 26: this.begin('command'); return 23;
+break;
+case 27:return 39;
+break;
+case 28:return 25;
+break;
+case 29: this.begin('command'); return 28;
+break;
+case 30: this.popState(); return 15;
+break;
+case 31:return 12;
+break;
+case 32:return 5;
+break;
+case 33:return 11;
+break;
+}
+},
+rules: [/^(?:\{\*[\s\S]*?\*\})/,/^(?:\{literal\}[\s\S]*?\{\/literal\})/,/^(?:"([^"]|\\\.)*")/,/^(?:'([^']|\\\.)*')/,/^(?:\$)/,/^(?:[_a-zA-Z][_a-zA-Z0-9]*)/,/^(?:\.)/,/^(?:\[)/,/^(?:\])/,/^(?:\()/,/^(?:\))/,/^(?:=)/,/^(?:\{ldelim\})/,/^(?:\{rdelim\})/,/^(?:\{#)/,/^(?:\{@)/,/^(?:\{if )/,/^(?:\{else if )/,/^(?:\{elseif )/,/^(?:\{else\})/,/^(?:\{\/if\})/,/^(?:\{lang\})/,/^(?:\{\/lang\})/,/^(?:\{include )/,/^(?:\{implode )/,/^(?:\{\/implode\})/,/^(?:\{foreach )/,/^(?:\{foreachelse\})/,/^(?:\{\/foreach\})/,/^(?:\{(?!\s))/,/^(?:\})/,/^(?:\s+)/,/^(?:$)/,/^(?:[^{])/],
+conditions: {"command":{"rules":[0,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],"inclusive":true},"INITIAL":{"rules":[0,1,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+return parser;
+});
+/**
+ * Provides helper functions for Number handling.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/NumberUtil
+ */
+define('WoltLabSuite/Core/NumberUtil',[], function() {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/NumberUtil
+ */
+ var NumberUtil = {
+ /**
+ * Decimal adjustment of a number.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
+ * @param {Number} value The number.
+ * @param {Integer} exp The exponent (the 10 logarithm of the adjustment base).
+ * @returns {Number} The adjusted value.
+ */
+ round: function (value, exp) {
+ // If the exp is undefined or zero...
+ if (typeof exp === 'undefined' || +exp === 0) {
+ return Math.round(value);
+ }
+ value = +value;
+ exp = +exp;
+
+ // If the value is not a number or the exp is not an integer...
+ if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
+ return NaN;
+ }
+
+ // Shift
+ value = value.toString().split('e');
+ value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
+
+ // Shift back
+ value = value.toString().split('e');
+ return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
+ }
+ };
+
+ return NumberUtil;
+});
+
+/**
+ * Provides helper functions for String handling.
+ *
+ * @author Tim Duesterhus, Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/StringUtil
+ */
+define('WoltLabSuite/Core/StringUtil',['Language', './NumberUtil'], function(Language, NumberUtil) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/StringUtil
+ */
+ return {
+ /**
+ * Adds thousands separators to a given number.
+ *
+ * @see http://stackoverflow.com/a/6502556/782822
+ * @param {?} number
+ * @return {String}
+ */
+ addThousandsSeparator: function(number) {
+ // Fetch Language, as it cannot be provided because of a circular dependency
+ if (Language === undefined) Language = require('Language');
+
+ return String(number).replace(/(^-?\d{1,3}|\d{3})(?=(?:\d{3})+(?:$|\.))/g, '$1' + Language.get('wcf.global.thousandsSeparator'));
+ },
+
+ /**
+ * Escapes special HTML-characters within a string
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ escapeHTML: function (string) {
+ return String(string).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
+ },
+
+ /**
+ * Escapes a String to work with RegExp.
+ *
+ * @see https://github.com/sstephenson/prototype/blob/master/src/prototype/lang/regexp.js#L25
+ * @param {?} string
+ * @return {String}
+ */
+ escapeRegExp: function(string) {
+ return String(string).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ /**
+ * Rounds number to given count of floating point digits, localizes decimal-point and inserts thousands separators.
+ *
+ * @param {?} number
+ * @param {int} decimalPlaces The number of decimal places to leave after rounding.
+ * @return {String}
+ */
+ formatNumeric: function(number, decimalPlaces) {
+ // Fetch Language, as it cannot be provided because of a circular dependency
+ if (Language === undefined) Language = require('Language');
+
+ number = String(NumberUtil.round(number, decimalPlaces || -2));
+ var numberParts = number.split('.');
+
+ number = this.addThousandsSeparator(numberParts[0]);
+ if (numberParts.length > 1) number += Language.get('wcf.global.decimalPoint') + numberParts[1];
+
+ number = number.replace('-', '\u2212');
+
+ return number;
+ },
+
+ /**
+ * Makes a string's first character lowercase.
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ lcfirst: function(string) {
+ return String(string).substring(0, 1).toLowerCase() + string.substring(1);
+ },
+
+ /**
+ * Makes a string's first character uppercase.
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ ucfirst: function(string) {
+ return String(string).substring(0, 1).toUpperCase() + string.substring(1);
+ },
+
+ /**
+ * Unescapes special HTML-characters within a string.
+ *
+ * @param {?} string
+ * @return {String}
+ */
+ unescapeHTML: function(string) {
+ return String(string).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
+ },
+
+ /**
+ * Shortens numbers larger than 1000 by using unit suffixes.
+ *
+ * @param {?} number
+ * @return {String}
+ */
+ shortUnit: function(number) {
+ var unitSuffix = '';
+
+ if (number >= 1000000) {
+ number /= 1000000;
+
+ if (number > 10) {
+ number = Math.floor(number);
+ }
+ else {
+ number = NumberUtil.round(number, -1);
+ }
+
+ unitSuffix = 'M';
+ }
+ else if (number >= 1000) {
+ number /= 1000;
+
+ if (number > 10) {
+ number = Math.floor(number);
+ }
+ else {
+ number = NumberUtil.round(number, -1);
+ }
+
+ unitSuffix = 'k';
+ }
+
+ return this.formatNumeric(number) + unitSuffix;
+ }
+ };
+});
+
+/**
+ * WoltLabSuite/Core/Template provides a template scripting compiler similar
+ * to the PHP one of WoltLab Suite Core. It supports a limited
+ * set of useful commands and compiles templates down to a pure
+ * JavaScript Function.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Template
+ */
+define('WoltLabSuite/Core/Template',['./Template.grammar', './StringUtil', 'Language'], function(parser, StringUtil, Language) {
+ "use strict";
+
+ // work around bug in AMD module generation of Jison
+ function Parser() {
+ this.yy = {};
+ }
+ Parser.prototype = parser;
+ parser.Parser = Parser;
+ parser = new Parser();
+
+ /**
+ * Compiles the given template.
+ *
+ * @param {string} template Template to compile.
+ * @constructor
+ */
+ function Template(template) {
+ // Fetch Language/StringUtil, as it cannot be provided because of a circular dependency
+ if (Language === undefined) Language = require('Language');
+ if (StringUtil === undefined) StringUtil = require('StringUtil');
+
+ try {
+ template = parser.parse(template);
+ template = "var tmp = {};\n"
+ + "for (var key in v) tmp[key] = v[key];\n"
+ + "v = tmp;\n"
+ + "v.__wcf = window.WCF; v.__window = window;\n"
+ + "return " + template;
+
+ this.fetch = new Function("StringUtil", "Language", "v", template).bind(undefined, StringUtil, Language);
+ }
+ catch (e) {
+ console.debug(e.message);
+ throw e;
+ }
+ }
+
+ Object.defineProperty(Template, 'callbacks', {
+ enumerable: false,
+ configurable: false,
+ get: function() {
+ throw new Error('WCF.Template.callbacks is no longer supported');
+ },
+ set: function(value) {
+ throw new Error('WCF.Template.callbacks is no longer supported');
+ }
+ });
+
+ Template.prototype = {
+ /**
+ * Evaluates the Template using the given parameters.
+ *
+ * @param {object} v Parameters to pass to the template.
+ */
+ fetch: function(v) {
+ // this will be replaced in the init function
+ throw new Error('This Template is not initialized.');
+ }
+ };
+
+ return Template;
+});
+
+/**
+ * Manages language items.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language
+ */
+define('WoltLabSuite/Core/Language',['Dictionary', './Template'], function(Dictionary, Template) {
+ "use strict";
+
+ var _languageItems = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Language
+ */
+ var Language = {
+ /**
+ * Adds all the language items in the given object to the store.
+ *
+ * @param {Object.<string, string>} object
+ */
+ addObject: function(object) {
+ _languageItems.merge(Dictionary.fromObject(object));
+ },
+
+ /**
+ * Adds a single language item to the store.
+ *
+ * @param {string} key
+ * @param {string} value
+ */
+ add: function(key, value) {
+ _languageItems.set(key, value);
+ },
+
+ /**
+ * Fetches the language item specified by the given key.
+ * If the language item is a string it will be evaluated as
+ * WoltLabSuite/Core/Template with the given parameters.
+ *
+ * @param {string} key Language item to return.
+ * @param {Object=} parameters Parameters to provide to WoltLabSuite/Core/Template.
+ * @return {string}
+ */
+ get: function(key, parameters) {
+ if (!parameters) parameters = { };
+
+ var value = _languageItems.get(key);
+
+ if (value === undefined) {
+ return key;
+ }
+
+ // fetch Template, as it cannot be provided because of a circular dependency
+ if (Template === undefined) Template = require('WoltLabSuite/Core/Template');
+
+ if (typeof value === 'string') {
+ // lazily convert to WCF.Template
+ try {
+ _languageItems.set(key, new Template(value));
+ }
+ catch (e) {
+ _languageItems.set(key, new Template('{literal}' + value.replace(/\{\/literal\}/g, '{/literal}{ldelim}/literal}{literal}') + '{/literal}'));
+ }
+ value = _languageItems.get(key);
+ }
+
+ if (value instanceof Template) {
+ value = value.fetch(parameters);
+ }
+
+ return value;
+ }
+ };
+
+ return Language;
+});
+
+/**
+ * Simple API to store and invoke multiple callbacks per identifier.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/CallbackList
+ */
+define('WoltLabSuite/Core/CallbackList',['Dictionary'], function(Dictionary) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function CallbackList() {
+ this._dictionary = new Dictionary();
+ }
+ CallbackList.prototype = {
+ /**
+ * Adds a callback for given identifier.
+ *
+ * @param {string} identifier arbitrary string to group and identify callbacks
+ * @param {function} callback callback function
+ */
+ add: function(identifier, callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback as second argument for identifier '" + identifier + "'.");
+ }
+
+ if (!this._dictionary.has(identifier)) {
+ this._dictionary.set(identifier, []);
+ }
+
+ this._dictionary.get(identifier).push(callback);
+ },
+
+ /**
+ * Removes all callbacks registered for given identifier
+ *
+ * @param {string} identifier arbitrary string to group and identify callbacks
+ */
+ remove: function(identifier) {
+ this._dictionary['delete'](identifier);
+ },
+
+ /**
+ * Invokes callback function on each registered callback.
+ *
+ * @param {string|null} identifier arbitrary string to group and identify callbacks.
+ * null is a wildcard to match every identifier
+ * @param {function(function)} callback function called with the individual callback as parameter
+ */
+ forEach: function(identifier, callback) {
+ if (identifier === null) {
+ this._dictionary.forEach(function(callbacks, identifier) {
+ callbacks.forEach(callback);
+ });
+ }
+ else {
+ var callbacks = this._dictionary.get(identifier);
+ if (callbacks !== undefined) {
+ callbacks.forEach(callback);
+ }
+ }
+ }
+ };
+
+ return CallbackList;
+});
+
+/**
+ * Allows to be informed when the DOM may have changed and
+ * new elements that are relevant to you may have been added.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dom/Change/Listener
+ */
+define('WoltLabSuite/Core/Dom/Change/Listener',['CallbackList'], function(CallbackList) {
+ "use strict";
+
+ var _callbackList = new CallbackList();
+ var _hot = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Dom/Change/Listener
+ */
+ return {
+ /**
+ * @see WoltLabSuite/Core/CallbackList#add
+ */
+ add: _callbackList.add.bind(_callbackList),
+
+ /**
+ * @see WoltLabSuite/Core/CallbackList#remove
+ */
+ remove: _callbackList.remove.bind(_callbackList),
+
+ /**
+ * Triggers the execution of all the listeners.
+ * Use this function when you added new elements to the DOM that might
+ * be relevant to others.
+ * While this function is in progress further calls to it will be ignored.
+ */
+ trigger: function() {
+ if (_hot) return;
+
+ try {
+ _hot = true;
+ _callbackList.forEach(null, function(callback) {
+ callback();
+ });
+ }
+ finally {
+ _hot = false;
+ }
+ }
+ };
+});
+
+/**
+ * Provides basic details on the JavaScript environment.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Environment
+ */
+define('WoltLabSuite/Core/Environment',[], function() {
+ "use strict";
+
+ var _browser = 'other';
+ var _editor = 'none';
+ var _platform = 'desktop';
+ var _touch = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Environment
+ */
+ return {
+ /**
+ * Determines environment variables.
+ */
+ setup: function() {
+ if (typeof window.chrome === 'object') {
+ // this detects Opera as well, we could check for window.opr if we need to
+ _browser = 'chrome';
+ }
+ else {
+ var styles = window.getComputedStyle(document.documentElement);
+ for (var i = 0, length = styles.length; i < length; i++) {
+ var property = styles[i];
+
+ if (property.indexOf('-ms-') === 0) {
+ // it is tempting to use 'msie', but it wouldn't really represent 'Edge'
+ _browser = 'microsoft';
+ }
+ else if (property.indexOf('-moz-') === 0) {
+ _browser = 'firefox';
+ }
+ else if (_browser !== 'firefox' && property.indexOf('-webkit-') === 0) {
+ _browser = 'safari';
+ }
+ }
+ }
+
+ var ua = window.navigator.userAgent.toLowerCase();
+ if (ua.indexOf('crios') !== -1) {
+ _browser = 'chrome';
+ _platform = 'ios';
+ }
+ else if (/(?:iphone|ipad|ipod)/.test(ua)) {
+ _browser = 'safari';
+ _platform = 'ios';
+ }
+ else if (ua.indexOf('android') !== -1) {
+ _platform = 'android';
+ }
+ else if (ua.indexOf('iemobile') !== -1) {
+ _browser = 'microsoft';
+ _platform = 'windows';
+ }
+
+ if (_platform === 'desktop' && (ua.indexOf('mobile') !== -1 || ua.indexOf('tablet') !== -1)) {
+ _platform = 'mobile';
+ }
+
+ _editor = 'redactor';
+ _touch = (!!('ontouchstart' in window) || (!!('msMaxTouchPoints' in window.navigator) && window.navigator.msMaxTouchPoints > 0) || window.DocumentTouch && document instanceof DocumentTouch);
+ },
+
+ /**
+ * Returns the lower-case browser identifier.
+ *
+ * Possible values:
+ * - chrome: Chrome and Opera
+ * - firefox
+ * - microsoft: Internet Explorer and Microsoft Edge
+ * - safari
+ *
+ * @return {string} browser identifier
+ */
+ browser: function() {
+ return _browser;
+ },
+
+ /**
+ * Returns the available editor's name or an empty string.
+ *
+ * @return {string} editor name
+ */
+ editor: function() {
+ return _editor;
+ },
+
+ /**
+ * Returns the browser platform.
+ *
+ * Possible values:
+ * - desktop
+ * - android
+ * - ios: iPhone, iPad and iPod
+ * - windows: Windows on phones/tablets
+ *
+ * @return {string} browser platform
+ */
+ platform: function() {
+ return _platform;
+ },
+
+ /**
+ * Returns true if browser is potentially used with a touchscreen.
+ *
+ * Warning: Detecting touch is unreliable and should be avoided at all cost.
+ *
+ * @deprecated 3.0 - exists for backward-compatibility only, will be removed in the future
+ *
+ * @return {boolean} true if a touchscreen is present
+ */
+ touch: function() {
+ return _touch;
+ }
+ };
+});
+
+/**
+ * Provides helper functions to work with DOM nodes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dom/Util
+ */
+define('WoltLabSuite/Core/Dom/Util',['Environment', 'StringUtil'], function(Environment, StringUtil) {
+ "use strict";
+
+ function _isBoundaryNode(element, ancestor, position) {
+ if (!ancestor.contains(element)) {
+ throw new Error("Ancestor element does not contain target element.");
+ }
+
+ var node, whichSibling = position + 'Sibling';
+ while (element !== null && element !== ancestor) {
+ if (element[position + 'ElementSibling'] !== null) {
+ return false;
+ }
+ else if (element[whichSibling]) {
+ node = element[whichSibling];
+ while (node) {
+ if (node.textContent.trim() !== '') {
+ return false;
+ }
+
+ node = node[whichSibling];
+ }
+ }
+
+ element = element.parentNode;
+ }
+
+ return true;
+ }
+
+ var _idCounter = 0;
+
+ /**
+ * @exports WoltLabSuite/Core/Dom/Util
+ */
+ var DomUtil = {
+ /**
+ * Returns a DocumentFragment containing the provided HTML string as DOM nodes.
+ *
+ * @param {string} html HTML string
+ * @return {DocumentFragment} fragment containing DOM nodes
+ */
+ createFragmentFromHtml: function(html) {
+ var tmp = elCreate('div');
+ this.setInnerHtml(tmp, html);
+
+ var fragment = document.createDocumentFragment();
+ while (tmp.childNodes.length) {
+ fragment.appendChild(tmp.childNodes[0]);
+ }
+
+ return fragment;
+ },
+
+ /**
+ * Returns a unique element id.
+ *
+ * @return {string} unique id
+ */
+ getUniqueId: function() {
+ var elementId;
+
+ do {
+ elementId = 'wcf' + _idCounter++;
+ }
+ while (elById(elementId) !== null);
+
+ return elementId;
+ },
+
+ /**
+ * Returns the element's id. If there is no id set, a unique id will be
+ * created and assigned.
+ *
+ * @param {Element} el element
+ * @return {string} element id
+ */
+ identify: function(el) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element as argument.");
+ }
+
+ var id = elAttr(el, 'id');
+ if (!id) {
+ id = this.getUniqueId();
+ elAttr(el, 'id', id);
+ }
+
+ return id;
+ },
+
+ /**
+ * Returns the outer height of an element including margins.
+ *
+ * @param {Element} el element
+ * @param {CSSStyleDeclaration=} styles result of window.getComputedStyle()
+ * @return {int} outer height in px
+ */
+ outerHeight: function(el, styles) {
+ styles = styles || window.getComputedStyle(el);
+
+ var height = el.offsetHeight;
+ height += ~~styles.marginTop + ~~styles.marginBottom;
+
+ return height;
+ },
+
+ /**
+ * Returns the outer width of an element including margins.
+ *
+ * @param {Element} el element
+ * @param {CSSStyleDeclaration=} styles result of window.getComputedStyle()
+ * @return {int} outer width in px
+ */
+ outerWidth: function(el, styles) {
+ styles = styles || window.getComputedStyle(el);
+
+ var width = el.offsetWidth;
+ width += ~~styles.marginLeft + ~~styles.marginRight;
+
+ return width;
+ },
+
+ /**
+ * Returns the outer dimensions of an element including margins.
+ *
+ * @param {Element} el element
+ * @return {{height: int, width: int}} dimensions in px
+ */
+ outerDimensions: function(el) {
+ var styles = window.getComputedStyle(el);
+
+ return {
+ height: this.outerHeight(el, styles),
+ width: this.outerWidth(el, styles)
+ };
+ },
+
+ /**
+ * Returns the element's offset relative to the document's top left corner.
+ *
+ * @param {Element} el element
+ * @return {{left: int, top: int}} offset relative to top left corner
+ */
+ offset: function(el) {
+ var rect = el.getBoundingClientRect();
+
+ return {
+ top: Math.round(rect.top + (window.scrollY || window.pageYOffset)),
+ left: Math.round(rect.left + (window.scrollX || window.pageXOffset))
+ };
+ },
+
+ /**
+ * Prepends an element to a parent element.
+ *
+ * @param {Element} el element to prepend
+ * @param {Element} parentEl future containing element
+ */
+ prepend: function(el, parentEl) {
+ if (parentEl.childNodes.length === 0) {
+ parentEl.appendChild(el);
+ }
+ else {
+ parentEl.insertBefore(el, parentEl.childNodes[0]);
+ }
+ },
+
+ /**
+ * Inserts an element after an existing element.
+ *
+ * @param {Element} newEl element to insert
+ * @param {Element} el reference element
+ */
+ insertAfter: function(newEl, el) {
+ if (el.nextSibling !== null) {
+ el.parentNode.insertBefore(newEl, el.nextSibling);
+ }
+ else {
+ el.parentNode.appendChild(newEl);
+ }
+ },
+
+ /**
+ * Applies a list of CSS properties to an element.
+ *
+ * @param {Element} el element
+ * @param {Object<string, *>} styles list of CSS styles
+ */
+ setStyles: function(el, styles) {
+ var important = false;
+ for (var property in styles) {
+ if (styles.hasOwnProperty(property)) {
+ if (/ !important$/.test(styles[property])) {
+ important = true;
+
+ styles[property] = styles[property].replace(/ !important$/, '');
+ }
+ else {
+ important = false;
+ }
+
+ // for a set style property with priority = important, some browsers are
+ // not able to overwrite it with a property != important; removing the
+ // property first solves this issue
+ if (el.style.getPropertyPriority(property) === 'important' && !important) {
+ el.style.removeProperty(property);
+ }
+
+ el.style.setProperty(property, styles[property], (important ? 'important' : ''));
+ }
+ }
+ },
+
+ /**
+ * Returns a style property value as integer.
+ *
+ * The behavior of this method is undefined for properties that are not considered
+ * to have a "numeric" value, e.g. "background-image".
+ *
+ * @param {CSSStyleDeclaration} styles result of window.getComputedStyle()
+ * @param {string} propertyName property name
+ * @return {int} property value as integer
+ */
+ styleAsInt: function(styles, propertyName) {
+ var value = styles.getPropertyValue(propertyName);
+ if (value === null) {
+ return 0;
+ }
+
+ return parseInt(value);
+ },
+
+ /**
+ * Sets the inner HTML of given element and reinjects <script> elements to be properly executed.
+ *
+ * @see http://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0
+ * @param {Element} element target element
+ * @param {string} innerHtml HTML string
+ */
+ setInnerHtml: function(element, innerHtml) {
+ element.innerHTML = innerHtml;
+
+ var newScript, script, scripts = elBySelAll('script', element);
+ for (var i = 0, length = scripts.length; i < length; i++) {
+ script = scripts[i];
+ newScript = elCreate('script');
+ if (script.src) {
+ newScript.src = script.src;
+ }
+ else {
+ newScript.textContent = script.textContent;
+ }
+
+ element.appendChild(newScript);
+ elRemove(script);
+ }
+ },
+
+ /**
+ *
+ * @param html
+ * @param {Element} referenceElement
+ * @param insertMethod
+ */
+ insertHtml: function(html, referenceElement, insertMethod) {
+ var element = elCreate('div');
+ this.setInnerHtml(element, html);
+
+ if (!element.childNodes.length) {
+ return;
+ }
+
+ var node = element.childNodes[0];
+ switch (insertMethod) {
+ case 'append':
+ referenceElement.appendChild(node);
+ break;
+
+ case 'after':
+ this.insertAfter(node, referenceElement);
+ break;
+
+ case 'prepend':
+ this.prepend(node, referenceElement);
+ break;
+
+ case 'before':
+ referenceElement.parentNode.insertBefore(node, referenceElement);
+ break;
+
+ default:
+ throw new Error("Unknown insert method '" + insertMethod + "'.");
+ break;
+ }
+
+ var tmp;
+ while (element.childNodes.length) {
+ tmp = element.childNodes[0];
+
+ this.insertAfter(tmp, node);
+ node = tmp;
+ }
+ },
+
+ /**
+ * Returns true if `element` contains the `child` element.
+ *
+ * @param {Element} element container element
+ * @param {Element} child child element
+ * @returns {boolean} true if `child` is a (in-)direct child of `element`
+ */
+ contains: function(element, child) {
+ while (child !== null) {
+ child = child.parentNode;
+
+ if (element === child) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Retrieves all data attributes from target element, optionally allowing for
+ * a custom prefix that serves two purposes: First it will restrict the results
+ * for items starting with it and second it will remove that prefix.
+ *
+ * @param {Element} element target element
+ * @param {string=} prefix attribute prefix
+ * @param {boolean=} camelCaseName transform attribute names into camel case using dashes as separators
+ * @param {boolean=} idToUpperCase transform '-id' into 'ID'
+ * @returns {object<string, string>} list of data attributes
+ */
+ getDataAttributes: function(element, prefix, camelCaseName, idToUpperCase) {
+ prefix = prefix || '';
+ if (!/^data-/.test(prefix)) prefix = 'data-' + prefix;
+ camelCaseName = (camelCaseName === true);
+ idToUpperCase = (idToUpperCase === true);
+
+ var attribute, attributes = {}, name, tmp;
+ for (var i = 0, length = element.attributes.length; i < length; i++) {
+ attribute = element.attributes[i];
+
+ if (attribute.name.indexOf(prefix) === 0) {
+ name = attribute.name.replace(new RegExp('^' + prefix), '');
+ if (camelCaseName) {
+ tmp = name.split('-');
+ name = '';
+ for (var j = 0, innerLength = tmp.length; j < innerLength; j++) {
+ if (name.length) {
+ if (idToUpperCase && tmp[j] === 'id') {
+ tmp[j] = 'ID';
+ }
+ else {
+ tmp[j] = StringUtil.ucfirst(tmp[j]);
+ }
+ }
+
+ name += tmp[j];
+ }
+ }
+
+ attributes[name] = attribute.value;
+ }
+ }
+
+ return attributes;
+ },
+
+ /**
+ * Unwraps contained nodes by moving them out of `element` while
+ * preserving their previous order. Target element will be removed
+ * at the end of the operation.
+ *
+ * @param {Element} element target element
+ */
+ unwrapChildNodes: function(element) {
+ var parent = element.parentNode;
+ while (element.childNodes.length) {
+ parent.insertBefore(element.childNodes[0], element);
+ }
+
+ elRemove(element);
+ },
+
+ /**
+ * Replaces an element by moving all child nodes into the new element
+ * while preserving their previous order. The old element will be removed
+ * at the end of the operation.
+ *
+ * @param {Element} oldElement old element
+ * @param {Element} newElement old element
+ */
+ replaceElement: function(oldElement, newElement) {
+ while (oldElement.childNodes.length) {
+ newElement.appendChild(oldElement.childNodes[0]);
+ }
+
+ oldElement.parentNode.insertBefore(newElement, oldElement);
+ elRemove(oldElement);
+ },
+
+ /**
+ * Returns true if given element is the most left node of the ancestor, that is
+ * a node without any content nor elements before it or its parent nodes.
+ *
+ * @param {Element} element target element
+ * @param {Element} ancestor ancestor element, must contain the target element
+ * @returns {boolean} true if target element is the most left node
+ */
+ isAtNodeStart: function(element, ancestor) {
+ return _isBoundaryNode(element, ancestor, 'previous');
+ },
+
+ /**
+ * Returns true if given element is the most right node of the ancestor, that is
+ * a node without any content nor elements after it or its parent nodes.
+ *
+ * @param {Element} element target element
+ * @param {Element} ancestor ancestor element, must contain the target element
+ * @returns {boolean} true if target element is the most right node
+ */
+ isAtNodeEnd: function(element, ancestor) {
+ return _isBoundaryNode(element, ancestor, 'next');
+ },
+
+ /**
+ * Returns the first ancestor element with position fixed or null.
+ *
+ * @param {Element} element target element
+ * @returns {(Element|null)} first ancestor with position fixed or null
+ */
+ getFixedParent: function (element) {
+ while (element && element !== document.body) {
+ if (window.getComputedStyle(element).getPropertyValue('position') === 'fixed') {
+ return element;
+ }
+
+ element = element.offsetParent;
+ }
+
+ return null;
+ }
+ };
+
+ // expose on window object for backward compatibility
+ window.bc_wcfDomUtil = DomUtil;
+
+ return DomUtil;
+});
+
+/**
+ * Simple `object` to `object` map using a native WeakMap on supported browsers, otherwise a set of two arrays.
+ *
+ * If you're looking for a dictionary with string keys, please see `WoltLabSuite/Core/Dictionary`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/ObjectMap
+ */
+define('WoltLabSuite/Core/ObjectMap',[], function() {
+ "use strict";
+
+ var _hasMap = objOwns(window, 'WeakMap') && typeof window.WeakMap === 'function';
+
+ /**
+ * @constructor
+ */
+ function ObjectMap() {
+ this._map = (_hasMap) ? new WeakMap() : { key: [], value: [] };
+ }
+ ObjectMap.prototype = {
+ /**
+ * Sets a new key with given value, will overwrite an existing key.
+ *
+ * @param {object} key key
+ * @param {object} value value
+ */
+ set: function(key, value) {
+ if (typeof key !== 'object' || key === null) {
+ throw new TypeError("Only objects can be used as key");
+ }
+
+ if (typeof value !== 'object' || value === null) {
+ throw new TypeError("Only objects can be used as value");
+ }
+
+ if (_hasMap) {
+ this._map.set(key, value);
+ }
+ else {
+ this._map.key.push(key);
+ this._map.value.push(value);
+ }
+ },
+
+ /**
+ * Removes a key from the map.
+ *
+ * @param {object} key key
+ */
+ 'delete': function(key) {
+ if (_hasMap) {
+ this._map['delete'](key);
+ }
+ else {
+ var index = this._map.key.indexOf(key);
+ this._map.key.splice(index);
+ this._map.value.splice(index);
+ }
+ },
+
+ /**
+ * Returns true if dictionary contains a value for given key.
+ *
+ * @param {object} key key
+ * @return {boolean} true if key exists
+ */
+ has: function(key) {
+ if (_hasMap) {
+ return this._map.has(key);
+ }
+ else {
+ return (this._map.key.indexOf(key) !== -1);
+ }
+ },
+
+ /**
+ * Retrieves a value by key, returns undefined if there is no match.
+ *
+ * @param {object} key key
+ * @return {*}
+ */
+ get: function(key) {
+ if (_hasMap) {
+ return this._map.get(key);
+ }
+ else {
+ var index = this._map.key.indexOf(key);
+ if (index !== -1) {
+ return this._map.value[index];
+ }
+
+ return undefined;
+ }
+ }
+ };
+
+ return ObjectMap;
+});
+
+/**
+ * Provides helper functions to traverse the DOM.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Dom/Traverse
+ */
+define('WoltLabSuite/Core/Dom/Traverse',[], function() {
+ "use strict";
+
+ /** @const */ var NONE = 0;
+ /** @const */ var SELECTOR = 1;
+ /** @const */ var CLASS_NAME = 2;
+ /** @const */ var TAG_NAME = 3;
+
+ var _probe = [
+ function(el, none) { return true; },
+ function(el, selector) { return el.matches(selector); },
+ function(el, className) { return el.classList.contains(className); },
+ function(el, tagName) { return el.nodeName === tagName; }
+ ];
+
+ var _children = function(el, type, value) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid element as first argument.");
+ }
+
+ var children = [];
+
+ for (var i = 0; i < el.childElementCount; i++) {
+ if (_probe[type](el.children[i], value)) {
+ children.push(el.children[i]);
+ }
+ }
+
+ return children;
+ };
+
+ var _parent = function(el, type, value, untilElement) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid element as first argument.");
+ }
+
+ el = el.parentNode;
+
+ while (el instanceof Element) {
+ if (el === untilElement) {
+ return null;
+ }
+
+ if (_probe[type](el, value)) {
+ return el;
+ }
+
+ el = el.parentNode;
+ }
+
+ return null;
+ };
+
+ var _sibling = function(el, siblingType, type, value) {
+ if (!(el instanceof Element)) {
+ throw new TypeError("Expected a valid element as first argument.");
+ }
+
+ if (el instanceof Element) {
+ if (el[siblingType] !== null && _probe[type](el[siblingType], value)) {
+ return el[siblingType];
+ }
+ }
+
+ return null;
+ };
+
+ /**
+ * @exports WoltLabSuite/Core/Dom/Traverse
+ */
+ return {
+ /**
+ * Examines child elements and returns the first child matching the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match child elements against
+ * @return {(Element|null)} null if there is no child node matching the selector
+ */
+ childBySel: function(el, selector) {
+ return _children(el, SELECTOR, selector)[0] || null;
+ },
+
+ /**
+ * Examines child elements and returns the first child that has the given CSS class set.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {(Element|null)} null if there is no child node with given CSS class
+ */
+ childByClass: function(el, className) {
+ return _children(el, CLASS_NAME, className)[0] || null;
+ },
+
+ /**
+ * Examines child elements and returns the first child which equals the given tag.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {(Element|null)} null if there is no child node which equals given tag
+ */
+ childByTag: function(el, tagName) {
+ return _children(el, TAG_NAME, tagName)[0] || null;
+ },
+
+ /**
+ * Examines child elements and returns all children matching the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match child elements against
+ * @return {array<Element>} list of children matching the selector
+ */
+ childrenBySel: function(el, selector) {
+ return _children(el, SELECTOR, selector);
+ },
+
+ /**
+ * Examines child elements and returns all children that have the given CSS class set.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {array<Element>} list of children with the given class
+ */
+ childrenByClass: function(el, className) {
+ return _children(el, CLASS_NAME, className);
+ },
+
+ /**
+ * Examines child elements and returns all children which equal the given tag.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {array<Element>} list of children equaling the tag name
+ */
+ childrenByTag: function(el, tagName) {
+ return _children(el, TAG_NAME, tagName);
+ },
+
+ /**
+ * Examines parent nodes and returns the first parent that matches the given selector.
+ *
+ * @param {Element} el child element
+ * @param {string} selector CSS selector to match parent nodes against
+ * @param {Element=} untilElement stop when reaching this element
+ * @return {(Element|null)} null if no parent node matched the selector
+ */
+ parentBySel: function(el, selector, untilElement) {
+ return _parent(el, SELECTOR, selector, untilElement);
+ },
+
+ /**
+ * Examines parent nodes and returns the first parent that has the given CSS class set.
+ *
+ * @param {Element} el child element
+ * @param {string} className CSS class name
+ * @param {Element=} untilElement stop when reaching this element
+ * @return {(Element|null)} null if there is no parent node with given class
+ */
+ parentByClass: function(el, className, untilElement) {
+ return _parent(el, CLASS_NAME, className, untilElement);
+ },
+
+ /**
+ * Examines parent nodes and returns the first parent which equals the given tag.
+ *
+ * @param {Element} el child element
+ * @param {string} tagName element tag name
+ * @param {Element=} untilElement stop when reaching this element
+ * @return {(Element|null)} null if there is no parent node of given tag type
+ */
+ parentByTag: function(el, tagName, untilElement) {
+ return _parent(el, TAG_NAME, tagName, untilElement);
+ },
+
+ /**
+ * Returns the next element sibling.
+ *
+ * @param {Element} el element
+ * @return {(Element|null)} null if there is no next sibling element
+ */
+ next: function(el) {
+ return _sibling(el, 'nextElementSibling', NONE, null);
+ },
+
+ /**
+ * Returns the next element sibling that matches the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match parent nodes against
+ * @return {(Element|null)} null if there is no next sibling element or it does not match the selector
+ */
+ nextBySel: function(el, selector) {
+ return _sibling(el, 'nextElementSibling', SELECTOR, selector);
+ },
+
+ /**
+ * Returns the next element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {(Element|null)} null if there is no next sibling element or it does not have the class set
+ */
+ nextByClass: function(el, className) {
+ return _sibling(el, 'nextElementSibling', CLASS_NAME, className);
+ },
+
+ /**
+ * Returns the next element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {(Element|null)} null if there is no next sibling element or it does not have the class set
+ */
+ nextByTag: function(el, tagName) {
+ return _sibling(el, 'nextElementSibling', TAG_NAME, tagName);
+ },
+
+ /**
+ * Returns the previous element sibling.
+ *
+ * @param {Element} el element
+ * @return {(Element|null)} null if there is no previous sibling element
+ */
+ prev: function(el) {
+ return _sibling(el, 'previousElementSibling', NONE, null);
+ },
+
+ /**
+ * Returns the previous element sibling that matches the given selector.
+ *
+ * @param {Element} el element
+ * @param {string} selector CSS selector to match parent nodes against
+ * @return {(Element|null)} null if there is no previous sibling element or it does not match the selector
+ */
+ prevBySel: function(el, selector) {
+ return _sibling(el, 'previousElementSibling', SELECTOR, selector);
+ },
+
+ /**
+ * Returns the previous element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} className CSS class name
+ * @return {(Element|null)} null if there is no previous sibling element or it does not have the class set
+ */
+ prevByClass: function(el, className) {
+ return _sibling(el, 'previousElementSibling', CLASS_NAME, className);
+ },
+
+ /**
+ * Returns the previous element sibling with given CSS class.
+ *
+ * @param {Element} el element
+ * @param {string} tagName element tag name
+ * @return {(Element|null)} null if there is no previous sibling element or it does not have the class set
+ */
+ prevByTag: function(el, tagName) {
+ return _sibling(el, 'previousElementSibling', TAG_NAME, tagName);
+ }
+ };
+});
+
+/**
+ * Provides the confirmation dialog overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Confirmation
+ */
+define('WoltLabSuite/Core/Ui/Confirmation',['Core', 'Language', 'Ui/Dialog'], function(Core, Language, UiDialog) {
+ "use strict";
+
+ var _active = false;
+ var _confirmButton = null;
+ var _content = null;
+ var _options = {};
+ var _text = null;
+
+ /**
+ * Confirmation dialog overlay.
+ *
+ * @exports WoltLabSuite/Core/Ui/Confirmation
+ */
+ return {
+ /**
+ * Shows the confirmation dialog.
+ *
+ * Possible options:
+ * - cancel: callback if user cancels the dialog
+ * - confirm: callback if user confirm the dialog
+ * - legacyCallback: WCF 2.0/2.1 compatible callback with string parameter
+ * - message: displayed confirmation message
+ * - parameters: list of parameters passed to the callback on confirm
+ * - template: optional HTML string to be inserted below the `message`
+ *
+ * @param {object<string, *>} options confirmation options
+ */
+ show: function(options) {
+ if (UiDialog === undefined) UiDialog = require('Ui/Dialog');
+
+ if (_active) {
+ return;
+ }
+
+ _options = Core.extend({
+ cancel: null,
+ confirm: null,
+ legacyCallback: null,
+ message: '',
+ messageIsHtml: false,
+ parameters: {},
+ template: ''
+ }, options);
+
+ _options.message = (typeof _options.message === 'string') ? _options.message.trim() : '';
+ if (!_options.message.length) {
+ throw new Error("Expected a non-empty string for option 'message'.");
+ }
+
+ if (typeof _options.confirm !== 'function' && typeof _options.legacyCallback !== 'function') {
+ throw new TypeError("Expected a valid callback for option 'confirm'.");
+ }
+
+ if (_content === null) {
+ this._createDialog();
+ }
+
+ _content.innerHTML = (typeof _options.template === 'string') ? _options.template.trim() : '';
+ if (_options.messageIsHtml) _text.innerHTML = _options.message;
+ else _text.textContent = _options.message;
+
+ _active = true;
+
+ UiDialog.open(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfSystemConfirmation',
+ options: {
+ onClose: this._onClose.bind(this),
+ onShow: this._onShow.bind(this),
+ title: Language.get('wcf.global.confirmation.title')
+ }
+ };
+ },
+
+ /**
+ * Returns content container element.
+ *
+ * @return {Element} content container element
+ */
+ getContentElement: function() {
+ return _content;
+ },
+
+ /**
+ * Creates the dialog DOM elements.
+ */
+ _createDialog: function() {
+ var dialog = elCreate('div');
+ elAttr(dialog, 'id', 'wcfSystemConfirmation');
+ dialog.classList.add('systemConfirmation');
+
+ _text = elCreate('p');
+ dialog.appendChild(_text);
+
+ _content = elCreate('div');
+ elAttr(_content, 'id', 'wcfSystemConfirmationContent');
+ dialog.appendChild(_content);
+
+ var formSubmit = elCreate('div');
+ formSubmit.classList.add('formSubmit');
+ dialog.appendChild(formSubmit);
+
+ _confirmButton = elCreate('button');
+ _confirmButton.classList.add('buttonPrimary');
+ _confirmButton.textContent = Language.get('wcf.global.confirmation.confirm');
+ _confirmButton.addEventListener(WCF_CLICK_EVENT, this._confirm.bind(this));
+ formSubmit.appendChild(_confirmButton);
+
+ var cancelButton = elCreate('button');
+ cancelButton.textContent = Language.get('wcf.global.confirmation.cancel');
+ cancelButton.addEventListener(WCF_CLICK_EVENT, function() { UiDialog.close('wcfSystemConfirmation'); });
+ formSubmit.appendChild(cancelButton);
+
+ document.body.appendChild(dialog);
+ },
+
+ /**
+ * Invoked if the user confirms the dialog.
+ */
+ _confirm: function() {
+ if (typeof _options.legacyCallback === 'function') {
+ _options.legacyCallback('confirm', _options.parameters, _content);
+ }
+ else {
+ _options.confirm(_options.parameters, _content);
+ }
+
+ _active = false;
+ UiDialog.close('wcfSystemConfirmation');
+ },
+
+ /**
+ * Invoked on dialog close or if user cancels the dialog.
+ */
+ _onClose: function() {
+ if (_active) {
+ _confirmButton.blur();
+ _active = false;
+
+ if (typeof _options.legacyCallback === 'function') {
+ _options.legacyCallback('cancel', _options.parameters, _content);
+ }
+ else if (typeof _options.cancel === 'function') {
+ _options.cancel(_options.parameters);
+ }
+ }
+ },
+
+ /**
+ * Sets the focus on the confirm button on dialog open for proper keyboard support.
+ */
+ _onShow: function() {
+ _confirmButton.blur();
+ _confirmButton.focus();
+ }
+ };
+});
+
+/**
+ * Provides consistent support for media queries and body scrolling.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Screen
+ */
+define('WoltLabSuite/Core/Ui/Screen',['Core', 'Dictionary', 'Environment'], function(Core, Dictionary, Environment) {
+ "use strict";
+
+ var _dialogContainer = null;
+ var _mql = new Dictionary();
+ var _scrollDisableCounter = 0;
+ var _scrollOffsetFrom = null;
+ var _scrollTop = 0;
+ var _pageOverlayCounter = 0;
+
+ var _mqMap = Dictionary.fromObject({
+ 'screen-xs': '(max-width: 544px)', /* smartphone */
+ 'screen-sm': '(min-width: 545px) and (max-width: 768px)', /* tablet (portrait) */
+ 'screen-sm-down': '(max-width: 768px)', /* smartphone + tablet (portrait) */
+ 'screen-sm-up': '(min-width: 545px)', /* tablet (portrait) + tablet (landscape) + desktop */
+ 'screen-sm-md': '(min-width: 545px) and (max-width: 1024px)', /* tablet (portrait) + tablet (landscape) */
+ 'screen-md': '(min-width: 769px) and (max-width: 1024px)', /* tablet (landscape) */
+ 'screen-md-down': '(max-width: 1024px)', /* smartphone + tablet (portrait) + tablet (landscape) */
+ 'screen-md-up': '(min-width: 769px)', /* tablet (landscape) + desktop */
+ 'screen-lg': '(min-width: 1025px)' /* desktop */
+ });
+
+ // Microsoft Edge rewrites the media queries to whatever it
+ // pleases, causing the input and output query to mismatch
+ var _mqMapEdge = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Screen
+ */
+ return {
+ /**
+ * Registers event listeners for media query match/unmatch.
+ *
+ * The `callbacks` object may contain the following keys:
+ * - `match`, triggered when media query matches
+ * - `unmatch`, triggered when media query no longer matches
+ * - `setup`, invoked when media query first matches
+ *
+ * Returns a UUID that is used to internal identify the callbacks, can be used
+ * to remove binding by calling the `remove` method.
+ *
+ * @param {string} query media query
+ * @param {object} callbacks callback functions
+ * @return {string} UUID for listener removal
+ */
+ on: function(query, callbacks) {
+ var uuid = Core.getUuid(), queryObject = this._getQueryObject(query);
+
+ if (typeof callbacks.match === 'function') {
+ queryObject.callbacksMatch.set(uuid, callbacks.match);
+ }
+
+ if (typeof callbacks.unmatch === 'function') {
+ queryObject.callbacksUnmatch.set(uuid, callbacks.unmatch);
+ }
+
+ if (typeof callbacks.setup === 'function') {
+ if (queryObject.mql.matches) {
+ callbacks.setup();
+ }
+ else {
+ queryObject.callbacksSetup.set(uuid, callbacks.setup);
+ }
+ }
+
+ return uuid;
+ },
+
+ /**
+ * Removes all listeners identified by their common UUID.
+ *
+ * @param {string} query must match the `query` argument used when calling `on()`
+ * @param {string} uuid UUID received when calling `on()`
+ */
+ remove: function(query, uuid) {
+ var queryObject = this._getQueryObject(query);
+
+ queryObject.callbacksMatch.delete(uuid);
+ queryObject.callbacksUnmatch.delete(uuid);
+ queryObject.callbacksSetup.delete(uuid);
+ },
+
+ /**
+ * Returns a boolean value if a media query expression currently matches.
+ *
+ * @param {string} query CSS media query
+ * @returns {boolean} true if query matches
+ */
+ is: function(query) {
+ return this._getQueryObject(query).mql.matches;
+ },
+
+ /**
+ * Disables scrolling of body element.
+ */
+ scrollDisable: function() {
+ if (_scrollDisableCounter === 0) {
+ _scrollTop = document.body.scrollTop;
+ _scrollOffsetFrom = 'body';
+ if (!_scrollTop) {
+ _scrollTop = document.documentElement.scrollTop;
+ _scrollOffsetFrom = 'documentElement';
+ }
+
+ var pageContainer = elById('pageContainer');
+
+ // setting translateY causes Mobile Safari to snap
+ if (Environment.platform() === 'ios') {
+ pageContainer.style.setProperty('position', 'relative', '');
+ pageContainer.style.setProperty('top', '-' + _scrollTop + 'px', '');
+ }
+ else {
+ pageContainer.style.setProperty('margin-top', '-' + _scrollTop + 'px', '');
+ }
+
+ document.documentElement.classList.add('disableScrolling');
+ }
+
+ _scrollDisableCounter++;
+ },
+
+ /**
+ * Re-enables scrolling of body element.
+ */
+ scrollEnable: function() {
+ if (_scrollDisableCounter) {
+ _scrollDisableCounter--;
+
+ if (_scrollDisableCounter === 0) {
+ document.documentElement.classList.remove('disableScrolling');
+
+ var pageContainer = elById('pageContainer');
+ if (Environment.platform() === 'ios') {
+ pageContainer.style.removeProperty('position');
+ pageContainer.style.removeProperty('top');
+ }
+ else {
+ pageContainer.style.removeProperty('margin-top');
+ }
+
+ if (_scrollTop) {
+ document[_scrollOffsetFrom].scrollTop = ~~_scrollTop;
+ }
+ }
+ }
+ },
+
+ /**
+ * Indicates that at least one page overlay is currently open.
+ */
+ pageOverlayOpen: function() {
+ if (_pageOverlayCounter === 0) {
+ document.documentElement.classList.add('pageOverlayActive');
+ }
+
+ _pageOverlayCounter++;
+ },
+
+ /**
+ * Marks one page overlay as closed.
+ */
+ pageOverlayClose: function() {
+ if (_pageOverlayCounter) {
+ _pageOverlayCounter--;
+
+ if (_pageOverlayCounter === 0) {
+ document.documentElement.classList.remove('pageOverlayActive');
+ }
+ }
+ },
+
+ /**
+ * Returns true if at least one page overlay is currently open.
+ *
+ * @returns {boolean}
+ */
+ pageOverlayIsActive: function() {
+ return _pageOverlayCounter > 0;
+ },
+
+ /**
+ * Sets the dialog container element. This method is used to
+ * circumvent a possible circular dependency, due to `Ui/Dialog`
+ * requiring the `Ui/Screen` module itself.
+ *
+ * @param {Element} container dialog container element
+ */
+ setDialogContainer: function (container) {
+ _dialogContainer = container;
+ },
+
+ /**
+ *
+ * @param {string} query CSS media query
+ * @return {Object} object containing callbacks and MediaQueryList
+ * @protected
+ */
+ _getQueryObject: function(query) {
+ if (typeof query !== 'string' || query.trim() === '') {
+ throw new TypeError("Expected a non-empty string for parameter 'query'.");
+ }
+
+ // Microsoft Edge rewrites the media queries to whatever it
+ // pleases, causing the input and output query to mismatch
+ if (_mqMapEdge.has(query)) query = _mqMapEdge.get(query);
+
+ if (_mqMap.has(query)) query = _mqMap.get(query);
+
+ var queryObject = _mql.get(query);
+ if (!queryObject) {
+ queryObject = {
+ callbacksMatch: new Dictionary(),
+ callbacksUnmatch: new Dictionary(),
+ callbacksSetup: new Dictionary(),
+ mql: window.matchMedia(query)
+ };
+ queryObject.mql.addListener(this._mqlChange.bind(this));
+
+ _mql.set(query, queryObject);
+
+ if (query !== queryObject.mql.media) {
+ _mqMapEdge.set(queryObject.mql.media, query);
+ }
+ }
+
+ return queryObject;
+ },
+
+ /**
+ * Triggered whenever a registered media query now matches or no longer matches.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _mqlChange: function(event) {
+ var queryObject = this._getQueryObject(event.media);
+ if (event.matches) {
+ if (queryObject.callbacksSetup.size) {
+ queryObject.callbacksSetup.forEach(function(callback) {
+ callback();
+ });
+
+ // discard all setup callbacks after execution
+ queryObject.callbacksSetup = new Dictionary();
+ }
+ else {
+ queryObject.callbacksMatch.forEach(function (callback) {
+ callback();
+ });
+ }
+ }
+ else {
+ queryObject.callbacksUnmatch.forEach(function(callback) {
+ callback();
+ });
+ }
+ }
+ };
+});
+
+/**
+ * Provides reliable checks for common key presses, uses `Event.key` on supported browsers
+ * or the deprecated `Event.which`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Event/Key
+ */
+define('WoltLabSuite/Core/Event/Key',[], function() {
+ "use strict";
+
+ function _isKey(event, key, which) {
+ if (!(event instanceof Event)) {
+ throw new TypeError("Expected a valid event when testing for key '" + key + "'.");
+ }
+
+ return event.key === key || event.which === which;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Event/Key
+ */
+ return {
+ /**
+ * Returns true if the pressed key equals 'ArrowDown'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowDown: function(event) {
+ return _isKey(event, 'ArrowDown', 40);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'ArrowLeft'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowLeft: function(event) {
+ return _isKey(event, 'ArrowLeft', 37);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'ArrowRight'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowRight: function(event) {
+ return _isKey(event, 'ArrowRight', 39);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'ArrowUp'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ ArrowUp: function(event) {
+ return _isKey(event, 'ArrowUp', 38);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Comma'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Comma: function(event) {
+ return _isKey(event, ',', 44);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'End'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ End: function(event) {
+ return _isKey(event, 'End', 35);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Enter'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Enter: function(event) {
+ return _isKey(event, 'Enter', 13);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Escape'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Escape: function(event) {
+ return _isKey(event, 'Escape', 27);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Home'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Home: function(event) {
+ return _isKey(event, 'Home', 36);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Space'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Space: function(event) {
+ return _isKey(event, 'Space', 32);
+ },
+
+ /**
+ * Returns true if the pressed key equals 'Tab'.
+ *
+ * @param {Event} event event object
+ * @return {boolean}
+ */
+ Tab: function(event) {
+ return _isKey(event, 'Tab', 9);
+ }
+ };
+});
+
+/**
+ * Utility class to align elements relatively to another.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Alignment
+ */
+define('WoltLabSuite/Core/Ui/Alignment',['Core', 'Language', 'Dom/Traverse', 'Dom/Util'], function(Core, Language, DomTraverse, DomUtil) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Alignment
+ */
+ return {
+ /**
+ * Sets the alignment for target element relatively to the reference element.
+ *
+ * @param {Element} el target element
+ * @param {Element} ref reference element
+ * @param {Object<string, *>} options list of options to alter the behavior
+ */
+ set: function(el, ref, options) {
+ options = Core.extend({
+ // offset to reference element
+ verticalOffset: 0,
+
+ // align the pointer element, expects .elementPointer as a direct child of given element
+ pointer: false,
+
+ // offset from/left side, ignored for center alignment
+ pointerOffset: 4,
+
+ // use static pointer positions, expects two items: class to move it to the bottom and the second to move it to the right
+ pointerClassNames: [],
+
+ // alternate element used to calculate dimensions
+ refDimensionsElement: null,
+
+ // preferred alignment, possible values: left/right/center and top/bottom
+ horizontal: 'left',
+ vertical: 'bottom',
+
+ // allow flipping over axis, possible values: both, horizontal, vertical and none
+ allowFlip: 'both'
+ }, options);
+
+ if (!Array.isArray(options.pointerClassNames) || options.pointerClassNames.length !== (options.pointer ? 1 : 2)) options.pointerClassNames = [];
+ if (['left', 'right', 'center'].indexOf(options.horizontal) === -1) options.horizontal = 'left';
+ if (options.vertical !== 'bottom') options.vertical = 'top';
+ if (['both', 'horizontal', 'vertical', 'none'].indexOf(options.allowFlip) === -1) options.allowFlip = 'both';
+
+ // place element in the upper left corner to prevent calculation issues due to possible scrollbars
+ DomUtil.setStyles(el, {
+ bottom: 'auto !important',
+ left: '0 !important',
+ right: 'auto !important',
+ top: '0 !important',
+ visibility: 'hidden !important'
+ });
+
+ var elDimensions = DomUtil.outerDimensions(el);
+ var refDimensions = DomUtil.outerDimensions((options.refDimensionsElement instanceof Element ? options.refDimensionsElement : ref));
+ var refOffsets = DomUtil.offset(ref);
+ var windowHeight = window.innerHeight;
+ var windowWidth = document.body.clientWidth;
+
+ var horizontal = { result: null };
+ var alignCenter = false;
+ if (options.horizontal === 'center') {
+ alignCenter = true;
+ horizontal = this._tryAlignmentHorizontal(options.horizontal, elDimensions, refDimensions, refOffsets, windowWidth);
+
+ if (!horizontal.result) {
+ if (options.allowFlip === 'both' || options.allowFlip === 'horizontal') {
+ options.horizontal = 'left';
+ }
+ else {
+ horizontal.result = true;
+ }
+ }
+ }
+
+ // in rtl languages we simply swap the value for 'horizontal'
+ if (Language.get('wcf.global.pageDirection') === 'rtl') {
+ options.horizontal = (options.horizontal === 'left') ? 'right' : 'left';
+ }
+
+ if (!horizontal.result) {
+ var horizontalCenter = horizontal;
+ horizontal = this._tryAlignmentHorizontal(options.horizontal, elDimensions, refDimensions, refOffsets, windowWidth);
+ if (!horizontal.result && (options.allowFlip === 'both' || options.allowFlip === 'horizontal')) {
+ var horizontalFlipped = this._tryAlignmentHorizontal((options.horizontal === 'left' ? 'right' : 'left'), elDimensions, refDimensions, refOffsets, windowWidth);
+ // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
+ if (horizontalFlipped.result) {
+ horizontal = horizontalFlipped;
+ }
+ else if (alignCenter) {
+ horizontal = horizontalCenter;
+ }
+ }
+ }
+
+ var left = horizontal.left;
+ var right = horizontal.right;
+
+ var vertical = this._tryAlignmentVertical(options.vertical, elDimensions, refDimensions, refOffsets, windowHeight, options.verticalOffset);
+ if (!vertical.result && (options.allowFlip === 'both' || options.allowFlip === 'vertical')) {
+ var verticalFlipped = this._tryAlignmentVertical((options.vertical === 'top' ? 'bottom' : 'top'), elDimensions, refDimensions, refOffsets, windowHeight, options.verticalOffset);
+ // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
+ if (verticalFlipped.result) {
+ vertical = verticalFlipped;
+ }
+ }
+
+ var bottom = vertical.bottom;
+ var top = vertical.top;
+
+ // set pointer position
+ if (options.pointer) {
+ var pointer = DomTraverse.childrenByClass(el, 'elementPointer');
+ pointer = pointer[0] || null;
+ if (pointer === null) {
+ throw new Error("Expected the .elementPointer element to be a direct children.");
+ }
+
+ if (horizontal.align === 'center') {
+ pointer.classList.add('center');
+
+ pointer.classList.remove('left');
+ pointer.classList.remove('right');
+ }
+ else {
+ pointer.classList.add(horizontal.align);
+
+ pointer.classList.remove('center');
+ pointer.classList.remove(horizontal.align === 'left' ? 'right' : 'left');
+ }
+
+ if (vertical.align === 'top') {
+ pointer.classList.add('flipVertical');
+ }
+ else {
+ pointer.classList.remove('flipVertical');
+ }
+ }
+ else if (options.pointerClassNames.length === 2) {
+ var pointerBottom = 0;
+ var pointerRight = 1;
+
+ el.classList[(top === 'auto' ? 'add' : 'remove')](options.pointerClassNames[pointerBottom]);
+ el.classList[(left === 'auto' ? 'add' : 'remove')](options.pointerClassNames[pointerRight]);
+ }
+
+ if (bottom !== 'auto') bottom = Math.round(bottom) + 'px';
+ if (left !== 'auto') left = Math.ceil(left) + 'px';
+ if (right !== 'auto') right = Math.floor(right) + 'px';
+ if (top !== 'auto') top = Math.round(top) + 'px';
+
+ DomUtil.setStyles(el, {
+ bottom: bottom,
+ left: left,
+ right: right,
+ top: top
+ });
+
+ elShow(el);
+ el.style.removeProperty('visibility');
+ },
+
+ /**
+ * Calculates left/right position and verifies if the element would be still within the page's boundaries.
+ *
+ * @param {string} align align to this side of the reference element
+ * @param {Object<string, int>} elDimensions element dimensions
+ * @param {Object<string, int>} refDimensions reference element dimensions
+ * @param {Object<string, int>} refOffsets position of reference element relative to the document
+ * @param {int} windowWidth window width
+ * @returns {Object<string, *>} calculation results
+ */
+ _tryAlignmentHorizontal: function(align, elDimensions, refDimensions, refOffsets, windowWidth) {
+ var left = 'auto';
+ var right = 'auto';
+ var result = true;
+
+ if (align === 'left') {
+ left = refOffsets.left;
+ if (left + elDimensions.width > windowWidth) {
+ result = false;
+ }
+ }
+ else if (align === 'right') {
+ if (refOffsets.left + refDimensions.width < elDimensions.width) {
+ result = false;
+ }
+ else {
+ right = windowWidth - (refOffsets.left + refDimensions.width);
+ if (right < 0) {
+ result = false;
+ }
+ }
+ }
+ else {
+ left = refOffsets.left + (refDimensions.width / 2) - (elDimensions.width / 2);
+ left = ~~left;
+
+ if (left < 0 || left + elDimensions.width > windowWidth) {
+ result = false;
+ }
+ }
+
+ return {
+ align: align,
+ left: left,
+ right: right,
+ result: result
+ };
+ },
+
+ /**
+ * Calculates top/bottom position and verifies if the element would be still within the page's boundaries.
+ *
+ * @param {string} align align to this side of the reference element
+ * @param {Object<string, int>} elDimensions element dimensions
+ * @param {Object<string, int>} refDimensions reference element dimensions
+ * @param {Object<string, int>} refOffsets position of reference element relative to the document
+ * @param {int} windowHeight window height
+ * @param {int} verticalOffset desired gap between element and reference element
+ * @returns {object<string, *>} calculation results
+ */
+ _tryAlignmentVertical: function(align, elDimensions, refDimensions, refOffsets, windowHeight, verticalOffset) {
+ var bottom = 'auto';
+ var top = 'auto';
+ var result = true;
+
+ if (align === 'top') {
+ var bodyHeight = document.body.clientHeight;
+ bottom = (bodyHeight - refOffsets.top) + verticalOffset;
+ if (bodyHeight - (bottom + elDimensions.height) < (window.scrollY || window.pageYOffset)) {
+ result = false;
+ }
+ }
+ else {
+ top = refOffsets.top + refDimensions.height + verticalOffset;
+ if (top + elDimensions.height - (window.scrollY || window.pageYOffset) > windowHeight) {
+ result = false;
+ }
+ }
+
+ return {
+ align: align,
+ bottom: bottom,
+ top: top,
+ result: result
+ };
+ }
+ };
+});
+
+/**
+ * Allows to be informed when a click event bubbled up to the document's body.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/CloseOverlay
+ */
+define('WoltLabSuite/Core/Ui/CloseOverlay',['CallbackList'], function(CallbackList) {
+ "use strict";
+
+ var _callbackList = new CallbackList();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/CloseOverlay
+ */
+ var UiCloseOverlay = {
+ /**
+ * Sets up global event listener for bubbled clicks events.
+ */
+ setup: function() {
+ document.body.addEventListener(WCF_CLICK_EVENT, this.execute.bind(this));
+ },
+
+ /**
+ * @see WoltLabSuite/Core/CallbackList#add
+ */
+ add: _callbackList.add.bind(_callbackList),
+
+ /**
+ * @see WoltLabSuite/Core/CallbackList#remove
+ */
+ remove: _callbackList.remove.bind(_callbackList),
+
+ /**
+ * Invokes all registered callbacks.
+ */
+ execute: function() {
+ _callbackList.forEach(null, function(callback) {
+ callback();
+ });
+ }
+ };
+
+ UiCloseOverlay.setup();
+
+ return UiCloseOverlay;
+});
+
+/**
+ * Simple dropdown implementation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dropdown/Simple
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Dropdown/Simple',[ 'CallbackList', 'Core', 'Dictionary', 'EventKey', 'Ui/Alignment', 'Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'Ui/CloseOverlay'],
+ function(CallbackList, Core, Dictionary, EventKey, UiAlignment, DomChangeListener, DomTraverse, DomUtil, UiCloseOverlay)
+{
+ "use strict";
+
+ var _availableDropdowns = null;
+ var _callbacks = new CallbackList();
+ var _didInit = false;
+ var _dropdowns = new Dictionary();
+ var _menus = new Dictionary();
+ var _menuContainer = null;
+ var _callbackDropdownMenuKeyDown = null;
+ var _activeTargetId = '';
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dropdown/Simple
+ */
+ return {
+ /**
+ * Performs initial setup such as setting up dropdowns and binding listeners.
+ */
+ setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _menuContainer = elCreate('div');
+ _menuContainer.className = 'dropdownMenuContainer';
+ document.body.appendChild(_menuContainer);
+
+ _availableDropdowns = elByClass('dropdownToggle');
+
+ this.initAll();
+
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.closeAll.bind(this));
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Dropdown/Simple', this.initAll.bind(this));
+
+ document.addEventListener('scroll', this._onScroll.bind(this));
+
+ // expose on window object for backward compatibility
+ window.bc_wcfSimpleDropdown = this;
+
+ _callbackDropdownMenuKeyDown = this._dropdownMenuKeyDown.bind(this);
+ },
+
+ /**
+ * Loops through all possible dropdowns and registers new ones.
+ */
+ initAll: function() {
+ for (var i = 0, length = _availableDropdowns.length; i < length; i++) {
+ this.init(_availableDropdowns[i], false);
+ }
+ },
+
+ /**
+ * Initializes a dropdown.
+ *
+ * @param {Element} button
+ * @param {boolean|Event} isLazyInitialization
+ */
+ init: function(button, isLazyInitialization) {
+ this.setup();
+
+ elAttr(button, 'role', 'button');
+ elAttr(button, 'tabindex', '0');
+ elAttr(button, 'aria-haspopup', true);
+ elAttr(button, 'aria-expanded', false);
+
+ if (button.classList.contains('jsDropdownEnabled') || elData(button, 'target')) {
+ return false;
+ }
+
+ var dropdown = DomTraverse.parentByClass(button, 'dropdown');
+ if (dropdown === null) {
+ throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a parent with .dropdown.");
+ }
+
+ var menu = DomTraverse.nextByClass(button, 'dropdownMenu');
+ if (menu === null) {
+ throw new Error("Invalid dropdown passed, button '" + DomUtil.identify(button) + "' does not have a menu as next sibling.");
+ }
+
+ // move menu into global container
+ _menuContainer.appendChild(menu);
+
+ var containerId = DomUtil.identify(dropdown);
+ if (!_dropdowns.has(containerId)) {
+ button.classList.add('jsDropdownEnabled');
+ button.addEventListener(WCF_CLICK_EVENT, this._toggle.bind(this));
+ button.addEventListener('keydown', this._handleKeyDown.bind(this));
+
+ _dropdowns.set(containerId, dropdown);
+ _menus.set(containerId, menu);
+
+ if (!containerId.match(/^wcf\d+$/)) {
+ elData(menu, 'source', containerId);
+ }
+
+ // prevent page scrolling
+ if (menu.childElementCount && menu.children[0].classList.contains('scrollableDropdownMenu')) {
+ menu = menu.children[0];
+ elData(menu, 'scroll-to-active', true);
+
+ var menuHeight = null, menuRealHeight = null;
+ menu.addEventListener('wheel', function (event) {
+ if (menuHeight === null) menuHeight = menu.clientHeight;
+ if (menuRealHeight === null) menuRealHeight = menu.scrollHeight;
+
+ // negative value: scrolling up
+ if (event.deltaY < 0 && menu.scrollTop === 0) {
+ event.preventDefault();
+ }
+ else if (event.deltaY > 0 && (menu.scrollTop + menuHeight === menuRealHeight)) {
+ event.preventDefault();
+ }
+ }, { passive: false });
+ }
+ }
+
+ elData(button, 'target', containerId);
+
+ if (isLazyInitialization) {
+ setTimeout(function() {
+ elData(button, 'dropdown-lazy-init', (isLazyInitialization instanceof MouseEvent));
+
+ Core.triggerEvent(button, WCF_CLICK_EVENT);
+
+ setTimeout(function() {
+ button.removeAttribute('data-dropdown-lazy-init');
+ }, 10);
+ }, 10);
+ }
+ },
+
+ /**
+ * Initializes a remote-controlled dropdown.
+ *
+ * @param {Element} dropdown dropdown wrapper element
+ * @param {Element} menu menu list element
+ */
+ initFragment: function(dropdown, menu) {
+ this.setup();
+
+ var containerId = DomUtil.identify(dropdown);
+ if (_dropdowns.has(containerId)) {
+ return;
+ }
+
+ _dropdowns.set(containerId, dropdown);
+ _menuContainer.appendChild(menu);
+
+ _menus.set(containerId, menu);
+ },
+
+ /**
+ * Registers a callback for open/close events.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {function(string, string)} callback
+ */
+ registerCallback: function(containerId, callback) {
+ _callbacks.add(containerId, callback);
+ },
+
+ /**
+ * Returns the requested dropdown wrapper element.
+ *
+ * @return {Element} dropdown wrapper element
+ */
+ getDropdown: function(containerId) {
+ return _dropdowns.get(containerId);
+ },
+
+ /**
+ * Returns the requested dropdown menu list element.
+ *
+ * @return {Element} menu list element
+ */
+ getDropdownMenu: function(containerId) {
+ return _menus.get(containerId);
+ },
+
+ /**
+ * Toggles the requested dropdown between opened and closed.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {Element=} referenceElement alternative reference element, used for reusable dropdown menus
+ * @param {boolean=} disableAutoFocus
+ */
+ toggleDropdown: function(containerId, referenceElement, disableAutoFocus) {
+ this._toggle(null, containerId, referenceElement, disableAutoFocus);
+ },
+
+ /**
+ * Calculates and sets the alignment of given dropdown.
+ *
+ * @param {Element} dropdown dropdown wrapper element
+ * @param {Element} dropdownMenu menu list element
+ * @param {Element=} alternateElement alternative reference element for alignment
+ */
+ setAlignment: function(dropdown, dropdownMenu, alternateElement) {
+ // check if button belongs to an i18n textarea
+ var button = elBySel('.dropdownToggle', dropdown), refDimensionsElement;
+ if (button !== null && button.parentNode.classList.contains('inputAddonTextarea')) {
+ refDimensionsElement = button;
+ }
+
+ UiAlignment.set(dropdownMenu, alternateElement || dropdown, {
+ pointerClassNames: ['dropdownArrowBottom', 'dropdownArrowRight'],
+ refDimensionsElement: refDimensionsElement || null,
+
+ // alignment
+ horizontal: (elData(dropdownMenu, 'dropdown-alignment-horizontal') === 'right') ? 'right' : 'left',
+ vertical: (elData(dropdownMenu, 'dropdown-alignment-vertical') === 'top') ? 'top' : 'bottom',
+
+ allowFlip: elData(dropdownMenu, 'dropdown-allow-flip') || 'both'
+ });
+ },
+
+ /**
+ * Calculates and sets the alignment of the dropdown identified by given id.
+ *
+ * @param {string} containerId dropdown wrapper id
+ */
+ setAlignmentById: function(containerId) {
+ var dropdown = _dropdowns.get(containerId);
+ if (dropdown === undefined) {
+ throw new Error("Unknown dropdown identifier '" + containerId + "'.");
+ }
+
+ var menu = _menus.get(containerId);
+
+ this.setAlignment(dropdown, menu);
+ },
+
+ /**
+ * Returns true if target dropdown exists and is open.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @return {boolean} true if dropdown exists and is open
+ */
+ isOpen: function(containerId) {
+ var menu = _menus.get(containerId);
+ return (menu !== undefined && menu.classList.contains('dropdownOpen'));
+ },
+
+ /**
+ * Opens the dropdown unless it is already open.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {boolean=} disableAutoFocus
+ */
+ open: function(containerId, disableAutoFocus) {
+ var menu = _menus.get(containerId);
+ if (menu !== undefined && !menu.classList.contains('dropdownOpen')) {
+ this.toggleDropdown(containerId, undefined, disableAutoFocus);
+ }
+ },
+
+ /**
+ * Closes the dropdown identified by given id without notifying callbacks.
+ *
+ * @param {string} containerId dropdown wrapper id
+ */
+ close: function(containerId) {
+ var dropdown = _dropdowns.get(containerId);
+ if (dropdown !== undefined) {
+ dropdown.classList.remove('dropdownOpen');
+ _menus.get(containerId).classList.remove('dropdownOpen');
+ }
+ },
+
+ /**
+ * Closes all dropdowns.
+ */
+ closeAll: function() {
+ _dropdowns.forEach((function(dropdown, containerId) {
+ if (dropdown.classList.contains('dropdownOpen')) {
+ dropdown.classList.remove('dropdownOpen');
+ _menus.get(containerId).classList.remove('dropdownOpen');
+
+ this._notifyCallbacks(containerId, 'close');
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Destroys a dropdown identified by given id.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @return {boolean} false for unknown dropdowns
+ */
+ destroy: function(containerId) {
+ if (!_dropdowns.has(containerId)) {
+ return false;
+ }
+
+ try {
+ this.close(containerId);
+
+ elRemove(_menus.get(containerId));
+ }
+ catch (e) {
+ // the elements might not exist anymore thus ignore all errors while cleaning up
+ }
+
+ _menus.delete(containerId);
+ _dropdowns.delete(containerId);
+
+ return true;
+ },
+
+ /**
+ * Handles dropdown positions in overlays when scrolling in the overlay.
+ *
+ * @param {Event} event event object
+ */
+ _onDialogScroll: function(event) {
+ var dialogContent = event.currentTarget;
+ //noinspection JSCheckFunctionSignatures
+ var dropdowns = elBySelAll('.dropdown.dropdownOpen', dialogContent);
+
+ for (var i = 0, length = dropdowns.length; i < length; i++) {
+ var dropdown = dropdowns[i];
+ var containerId = DomUtil.identify(dropdown);
+ var offset = DomUtil.offset(dropdown);
+ var dialogOffset = DomUtil.offset(dialogContent);
+
+ // check if dropdown toggle is still (partially) visible
+ if (offset.top + dropdown.clientHeight <= dialogOffset.top) {
+ // top check
+ this.toggleDropdown(containerId);
+ }
+ else if (offset.top >= dialogOffset.top + dialogContent.offsetHeight) {
+ // bottom check
+ this.toggleDropdown(containerId);
+ }
+ else if (offset.left <= dialogOffset.left) {
+ // left check
+ this.toggleDropdown(containerId);
+ }
+ else if (offset.left >= dialogOffset.left + dialogContent.offsetWidth) {
+ // right check
+ this.toggleDropdown(containerId);
+ }
+ else {
+ this.setAlignment(_dropdowns.get(containerId), _menus.get(containerId));
+ }
+ }
+ },
+
+ /**
+ * Recalculates dropdown positions on page scroll.
+ */
+ _onScroll: function() {
+ _dropdowns.forEach((function(dropdown, containerId) {
+ if (dropdown.classList.contains('dropdownOpen')) {
+ if (elDataBool(dropdown, 'is-overlay-dropdown-button')) {
+ this.setAlignment(dropdown, _menus.get(containerId));
+ }
+ else {
+ var menu = _menus.get(dropdown.id);
+ if (!elDataBool(menu, 'dropdown-ignore-page-scroll')) {
+ this.close(containerId);
+ }
+ }
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Notifies callbacks on status change.
+ *
+ * @param {string} containerId dropdown wrapper id
+ * @param {string} action can be either 'open' or 'close'
+ */
+ _notifyCallbacks: function(containerId, action) {
+ _callbacks.forEach(containerId, function(callback) {
+ callback(containerId, action);
+ });
+ },
+
+ /**
+ * Toggles the dropdown's state between open and close.
+ *
+ * @param {?Event} event event object, should be 'null' if targetId is given
+ * @param {string?} targetId dropdown wrapper id
+ * @param {Element=} alternateElement alternative reference element for alignment
+ * @param {boolean=} disableAutoFocus
+ * @return {boolean} 'false' if event is not null
+ */
+ _toggle: function(event, targetId, alternateElement, disableAutoFocus) {
+ if (event !== null) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ //noinspection JSCheckFunctionSignatures
+ targetId = elData(event.currentTarget, 'target');
+
+ if (disableAutoFocus === undefined && event instanceof MouseEvent) {
+ disableAutoFocus = true;
+ }
+ }
+
+ var dropdown = _dropdowns.get(targetId), preventToggle = false;
+ if (dropdown !== undefined) {
+ var button;
+
+ // check if the dropdown is still the same, as some components (e.g. page actions)
+ // re-create the parent of a button
+ if (event) {
+ button = event.currentTarget, parent = button.parentNode;
+ if (parent !== dropdown) {
+ parent.classList.add('dropdown');
+ parent.id = dropdown.id;
+
+ // remove dropdown class and id from old parent
+ dropdown.classList.remove('dropdown');
+ dropdown.id = '';
+
+ dropdown = parent;
+ _dropdowns.set(targetId, parent);
+ }
+ }
+
+ if (disableAutoFocus === undefined) {
+ button = dropdown.closest('.dropdownToggle');
+ if (!button) {
+ button = elBySel('.dropdownToggle', dropdown);
+
+ if (!button && dropdown.id) {
+ button = elBySel('[data-target="' + dropdown.id + '"]');
+ }
+ }
+
+ if (button && elDataBool(button, 'dropdown-lazy-init')) {
+ disableAutoFocus = true;
+ }
+ }
+
+ // Repeated clicks on the dropdown button will not cause it to close, the only way
+ // to close it is by clicking somewhere else in the document or on another dropdown
+ // toggle. This is used with the search bar to prevent the dropdown from closing by
+ // setting the caret position in the search input field.
+ if (elDataBool(dropdown, 'dropdown-prevent-toggle') && dropdown.classList.contains('dropdownOpen')) {
+ preventToggle = true;
+ }
+
+ // check if 'isOverlayDropdownButton' is set which indicates that the dropdown toggle is within an overlay
+ if (elData(dropdown, 'is-overlay-dropdown-button') === '') {
+ var dialogContent = DomTraverse.parentByClass(dropdown, 'dialogContent');
+ elData(dropdown, 'is-overlay-dropdown-button', (dialogContent !== null));
+
+ if (dialogContent !== null) {
+ dialogContent.addEventListener('scroll', this._onDialogScroll.bind(this));
+ }
+ }
+ }
+
+ // close all dropdowns
+ _activeTargetId = '';
+ _dropdowns.forEach((function(dropdown, containerId) {
+ var menu = _menus.get(containerId);
+
+ if (dropdown.classList.contains('dropdownOpen')) {
+ if (preventToggle === false) {
+ dropdown.classList.remove('dropdownOpen');
+ menu.classList.remove('dropdownOpen');
+
+ var button = elBySel('.dropdownToggle', dropdown);
+ if (button) elAttr(button, 'aria-expanded', false);
+
+ this._notifyCallbacks(containerId, 'close');
+ }
+ else {
+ _activeTargetId = targetId;
+ }
+ }
+ else if (containerId === targetId && menu.childElementCount > 0) {
+ _activeTargetId = targetId;
+ dropdown.classList.add('dropdownOpen');
+ menu.classList.add('dropdownOpen');
+
+ var button = elBySel('.dropdownToggle', dropdown);
+ if (button) elAttr(button, 'aria-expanded', true);
+
+ if (menu.childElementCount && elDataBool(menu.children[0], 'scroll-to-active')) {
+ var list = menu.children[0];
+ list.removeAttribute('data-scroll-to-active');
+
+ var active = null;
+ for (var i = 0, length = list.childElementCount; i < length; i++) {
+ if (list.children[i].classList.contains('active')) {
+ active = list.children[i];
+ break;
+ }
+ }
+
+ if (active) {
+ list.scrollTop = Math.max((active.offsetTop + active.clientHeight) - menu.clientHeight, 0);
+ }
+ }
+
+ var itemList = elBySel('.scrollableDropdownMenu', menu);
+ if (itemList !== null) {
+ itemList.classList[(itemList.scrollHeight > itemList.clientHeight ? 'add' : 'remove')]('forceScrollbar');
+ }
+
+ this._notifyCallbacks(containerId, 'open');
+
+ var firstListItem = null;
+ if (!disableAutoFocus) {
+ elAttr(menu, 'role', 'menu');
+ elAttr(menu, 'tabindex', -1);
+ menu.removeEventListener('keydown', _callbackDropdownMenuKeyDown);
+ menu.addEventListener('keydown', _callbackDropdownMenuKeyDown);
+ elBySelAll('li', menu, function (listItem) {
+ if (!listItem.clientHeight) return;
+ if (firstListItem === null) firstListItem = listItem;
+ else if (listItem.classList.contains('active')) firstListItem = listItem;
+
+ elAttr(listItem, 'role', 'menuitem');
+ elAttr(listItem, 'tabindex', -1);
+ });
+ }
+
+ this.setAlignment(dropdown, menu, alternateElement);
+
+ if (firstListItem !== null) {
+ firstListItem.focus();
+ }
+ }
+ }).bind(this));
+
+ //noinspection JSDeprecatedSymbols
+ window.WCF.Dropdown.Interactive.Handler.closeAll();
+
+ return (event === null);
+ },
+
+ _handleKeyDown: function(event) {
+ if (EventKey.Enter(event) || EventKey.Space(event)) {
+ event.preventDefault();
+ this._toggle(event);
+ }
+ },
+
+ _dropdownMenuKeyDown: function(event) {
+ var button, dropdown;
+
+ var activeItem = document.activeElement;
+ if (activeItem.nodeName !== 'LI') {
+ return;
+ }
+
+ if (EventKey.ArrowDown(event) || EventKey.ArrowUp(event) || EventKey.End(event) || EventKey.Home(event)) {
+ event.preventDefault();
+
+ var listItems = Array.prototype.slice.call(elBySelAll('li', activeItem.closest('.dropdownMenu')));
+ if (EventKey.ArrowUp(event) || EventKey.End(event)) {
+ listItems.reverse();
+ }
+ var newActiveItem = null;
+ var isValidItem = function(listItem) {
+ return !listItem.classList.contains('dropdownDivider') && listItem.clientHeight > 0;
+ };
+
+ var activeIndex = listItems.indexOf(activeItem);
+ if (EventKey.End(event) || EventKey.Home(event)) {
+ activeIndex = -1;
+ }
+
+ for (var i = activeIndex + 1; i < listItems.length; i++) {
+ if (isValidItem(listItems[i])) {
+ newActiveItem = listItems[i];
+ break;
+ }
+ }
+
+ if (newActiveItem === null) {
+ for (i = 0; i < listItems.length; i++) {
+ if (isValidItem(listItems[i])) {
+ newActiveItem = listItems[i];
+ break;
+ }
+ }
+ }
+
+ newActiveItem.focus();
+ }
+ else if (EventKey.Enter(event) || EventKey.Space(event)) {
+ event.preventDefault();
+
+ var target = activeItem;
+ if (target.childElementCount === 1 && (target.children[0].nodeName === 'SPAN' || target.children[0].nodeName === 'A')) {
+ target = target.children[0];
+ }
+
+ dropdown = _dropdowns.get(_activeTargetId);
+ button = elBySel('.dropdownToggle', dropdown);
+ require(['Core'], function(Core) {
+ var mouseEvent = elData(dropdown, 'a11y-mouse-event') || 'click';
+ Core.triggerEvent(target, mouseEvent);
+
+ if (button) button.focus();
+ });
+ }
+ else if (EventKey.Escape(event) || EventKey.Tab(event)) {
+ event.preventDefault();
+
+ dropdown = _dropdowns.get(_activeTargetId);
+ button = elBySel('.dropdownToggle', dropdown);
+ // Remote controlled drop-down menus may not have a dedicated toggle button, instead the
+ // `dropdown` element itself is the button.
+ if (button === null && !dropdown.classList.contains('dropdown')) {
+ button = dropdown;
+ }
+
+ this._toggle(null, _activeTargetId);
+ if (button) button.focus();
+ }
+ }
+ };
+});
+
+/**
+ * Developer tools for WoltLab Suite.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Devtools
+ */
+define('WoltLabSuite/Core/Devtools',[], function() {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ return {
+ help: function () {},
+ toggleEditorAutosave: function () {},
+ toggleEventLogging: function () {},
+ _internal_: {
+ enable: function () {},
+ editorAutosave: function () {},
+ eventLog: function() {}
+ }
+ };
+ }
+
+ var _settings = {
+ editorAutosave: true,
+ eventLogging: false
+ };
+
+ var _updateConfig = function () {
+ if (window.sessionStorage) {
+ window.sessionStorage.setItem("__wsc_devtools_config", JSON.stringify(_settings));
+ }
+ };
+
+ var Devtools = {
+ /**
+ * Prints the list of available commands.
+ */
+ help: function () {
+ window.console.log("");
+ window.console.log("%cAvailable commands:", "text-decoration: underline");
+
+ var cmds = [];
+ for (var cmd in Devtools) {
+ if (cmd !== '_internal_' && Devtools.hasOwnProperty(cmd)) {
+ cmds.push(cmd);
+ }
+ }
+ cmds.sort().forEach(function(cmd) {
+ window.console.log("\tDevtools." + cmd + "()");
+ });
+
+ window.console.log("");
+ },
+
+ /**
+ * Disables/re-enables the editor autosave feature.
+ *
+ * @param {boolean} forceDisable
+ */
+ toggleEditorAutosave: function(forceDisable) {
+ _settings.editorAutosave = (forceDisable === true) ? false : !_settings.editorAutosave;
+ _updateConfig();
+
+ window.console.log("%c\tEditor autosave " + (_settings.editorAutosave ? "enabled" : "disabled"), "font-style: italic");
+ },
+
+ /**
+ * Enables/disables logging for fired event listener events.
+ *
+ * @param {boolean} forceEnable
+ */
+ toggleEventLogging: function(forceEnable) {
+ _settings.eventLogging = (forceEnable === true) ? true : !_settings.eventLogging;
+ _updateConfig();
+
+ window.console.log("%c\tEvent logging " + (_settings.eventLogging ? "enabled" : "disabled"), "font-style: italic");
+ },
+
+ /**
+ * Internal methods not meant to be called directly.
+ */
+ _internal_: {
+ enable: function () {
+ window.Devtools = Devtools;
+
+ window.console.log("%cDevtools for WoltLab Suite loaded", "font-weight: bold");
+
+ if (window.sessionStorage) {
+ var settings = window.sessionStorage.getItem("__wsc_devtools_config");
+ try {
+ if (settings !== null) {
+ _settings = JSON.parse(settings);
+ }
+ }
+ catch (e) {}
+
+ if (!_settings.editorAutosave) Devtools.toggleEditorAutosave(true);
+ if (_settings.eventLogging) Devtools.toggleEventLogging(true);
+ }
+
+ window.console.log("Settings are saved per browser session, enter `Devtools.help()` to learn more.");
+ window.console.log("");
+ },
+
+ editorAutosave: function () {
+ return _settings.editorAutosave;
+ },
+
+ eventLog: function(identifier, action) {
+ if (_settings.eventLogging) {
+ window.console.log("[Devtools.EventLogging] Firing event: " + action + " @ " + identifier);
+ }
+ }
+ }
+ };
+
+ return Devtools;
+});
+
+/**
+ * Versatile event system similar to the WCF-PHP counter part.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Event/Handler
+ */
+define('WoltLabSuite/Core/Event/Handler',['Core', 'Devtools', 'Dictionary'], function(Core, Devtools, Dictionary) {
+ "use strict";
+
+ var _listeners = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Event/Handler
+ */
+ return {
+ /**
+ * Adds an event listener.
+ *
+ * @param {string} identifier event identifier
+ * @param {string} action action name
+ * @param {function(object)} callback callback function
+ * @return {string} uuid required for listener removal
+ */
+ add: function(identifier, action, callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("[WoltLabSuite/Core/Event/Handler] Expected a valid callback for '" + action + "@" + identifier + "'.");
+ }
+
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ actions = new Dictionary();
+ _listeners.set(identifier, actions);
+ }
+
+ var callbacks = actions.get(action);
+ if (callbacks === undefined) {
+ callbacks = new Dictionary();
+ actions.set(action, callbacks);
+ }
+
+ var uuid = Core.getUuid();
+ callbacks.set(uuid, callback);
+
+ return uuid;
+ },
+
+ /**
+ * Fires an event and notifies all listeners.
+ *
+ * @param {string} identifier event identifier
+ * @param {string} action action name
+ * @param {object=} data event data
+ */
+ fire: function(identifier, action, data) {
+ Devtools._internal_.eventLog(identifier, action);
+
+ data = data || {};
+
+ var actions = _listeners.get(identifier);
+ if (actions !== undefined) {
+ var callbacks = actions.get(action);
+ if (callbacks !== undefined) {
+ callbacks.forEach(function(callback) {
+ callback(data);
+ });
+ }
+ }
+ },
+
+ /**
+ * Removes an event listener, requires the uuid returned by add().
+ *
+ * @param {string} identifier event identifier
+ * @param {string} action action name
+ * @param {string} uuid listener uuid
+ */
+ remove: function(identifier, action, uuid) {
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ return;
+ }
+
+ var callbacks = actions.get(action);
+ if (callbacks === undefined) {
+ return;
+ }
+
+ callbacks['delete'](uuid);
+ },
+
+ /**
+ * Removes all event listeners for given action. Omitting the second parameter will
+ * remove all listeners for this identifier.
+ *
+ * @param {string} identifier event identifier
+ * @param {string=} action action name
+ */
+ removeAll: function(identifier, action) {
+ if (typeof action !== 'string') action = undefined;
+
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ return;
+ }
+
+ if (typeof action === 'undefined') {
+ _listeners['delete'](identifier);
+ }
+ else {
+ actions['delete'](action);
+ }
+ },
+
+ /**
+ * Removes all listeners registered for an identifier and ending with a special suffix.
+ * This is commonly used to unbound event handlers for the editor.
+ *
+ * @param {string} identifier event identifier
+ * @param {string} suffix action suffix
+ */
+ removeAllBySuffix: function (identifier, suffix) {
+ var actions = _listeners.get(identifier);
+ if (actions === undefined) {
+ return;
+ }
+
+ suffix = '_' + suffix;
+ var length = suffix.length * -1;
+ actions.forEach((function (callbacks, action) {
+ //noinspection JSUnresolvedFunction
+ if (action.substr(length) === suffix) {
+ this.removeAll(identifier, action);
+ }
+ }).bind(this));
+ }
+ };
+});
+
+/**
+ * List implementation relying on an array or if supported on a Set to hold values.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/List
+ */
+define('WoltLabSuite/Core/List',[], function() {
+ "use strict";
+
+ var _hasSet = objOwns(window, 'Set') && typeof window.Set === 'function';
+
+ /**
+ * @constructor
+ */
+ function List() {
+ this._set = (_hasSet) ? new Set() : [];
+ }
+ List.prototype = {
+ /**
+ * Appends an element to the list, silently rejects adding an already existing value.
+ *
+ * @param {?} value unique element
+ */
+ add: function(value) {
+ if (_hasSet) {
+ this._set.add(value);
+ }
+ else if (!this.has(value)) {
+ this._set.push(value);
+ }
+ },
+
+ /**
+ * Removes all elements from the list.
+ */
+ clear: function() {
+ if (_hasSet) {
+ this._set.clear();
+ }
+ else {
+ this._set = [];
+ }
+ },
+
+ /**
+ * Removes an element from the list, returns true if the element was in the list.
+ *
+ * @param {?} value element
+ * @return {boolean} true if element was in the list
+ */
+ 'delete': function(value) {
+ if (_hasSet) {
+ return this._set['delete'](value);
+ }
+ else {
+ var index = this._set.indexOf(value);
+ if (index === -1) {
+ return false;
+ }
+
+ this._set.splice(index, 1);
+ return true;
+ }
+ },
+
+ /**
+ * Calls `callback` for each element in the list.
+ */
+ forEach: function(callback) {
+ if (_hasSet) {
+ this._set.forEach(callback);
+ }
+ else {
+ for (var i = 0, length = this._set.length; i < length; i++) {
+ callback(this._set[i]);
+ }
+ }
+ },
+
+ /**
+ * Returns true if the list contains the element.
+ *
+ * @param {?} value element
+ * @return {boolean} true if element is in the list
+ */
+ has: function(value) {
+ if (_hasSet) {
+ return this._set.has(value);
+ }
+ else {
+ return (this._set.indexOf(value) !== -1);
+ }
+ }
+ };
+
+ Object.defineProperty(List.prototype, 'size', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
+ if (_hasSet) {
+ return this._set.size;
+ }
+ else {
+ return this._set.length;
+ }
+ }
+ });
+
+ return List;
+});
+
+/**
+ * Modal dialog handler.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dialog
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Dialog',[
+ 'Ajax', 'Core', 'Dictionary',
+ 'Environment', 'Language', 'ObjectMap', 'Dom/ChangeListener',
+ 'Dom/Traverse', 'Dom/Util', 'Ui/Confirmation', 'Ui/Screen', 'Ui/SimpleDropdown',
+ 'EventHandler', 'List', 'EventKey'
+ ],
+ function(
+ Ajax, Core, Dictionary,
+ Environment, Language, ObjectMap, DomChangeListener,
+ DomTraverse, DomUtil, UiConfirmation, UiScreen, UiSimpleDropdown,
+ EventHandler, List, EventKey
+ )
+{
+ "use strict";
+
+ var _activeDialog = null;
+ var _callbackFocus = null;
+ var _container = null;
+ var _dialogs = new Dictionary();
+ var _dialogFullHeight = false;
+ var _dialogObjects = new ObjectMap();
+ var _dialogToObject = new Dictionary();
+ var _focusedBeforeDialog = null;
+ var _keyupListener = null;
+ var _staticDialogs = elByClass('jsStaticDialog');
+ var _validCallbacks = ['onBeforeClose', 'onClose', 'onShow'];
+
+ // list of supported `input[type]` values for dialog submit
+ var _validInputTypes = ['number', 'password', 'search', 'tel', 'text', 'url'];
+
+ var _focusableElements = [
+ 'a[href]:not([tabindex^="-"]):not([inert])',
+ 'area[href]:not([tabindex^="-"]):not([inert])',
+ 'input:not([disabled]):not([inert])',
+ 'select:not([disabled]):not([inert])',
+ 'textarea:not([disabled]):not([inert])',
+ 'button:not([disabled]):not([inert])',
+ 'iframe:not([tabindex^="-"]):not([inert])',
+ 'audio:not([tabindex^="-"]):not([inert])',
+ 'video:not([tabindex^="-"]):not([inert])',
+ '[contenteditable]:not([tabindex^="-"]):not([inert])',
+ '[tabindex]:not([tabindex^="-"]):not([inert])'
+ ];
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dialog
+ */
+ return {
+ /**
+ * Sets up global container and internal variables.
+ */
+ setup: function() {
+ // Fetch Ajax, as it cannot be provided because of a circular dependency
+ if (Ajax === undefined) Ajax = require('Ajax');
+
+ _container = elCreate('div');
+ _container.classList.add('dialogOverlay');
+ elAttr(_container, 'aria-hidden', 'true');
+ _container.addEventListener('mousedown', this._closeOnBackdrop.bind(this));
+ _container.addEventListener('wheel', function (event) {
+ if (event.target === _container) {
+ event.preventDefault();
+ }
+ }, { passive: false });
+
+ elById('content').appendChild(_container);
+
+ _keyupListener = (function(event) {
+ if (event.keyCode === 27) {
+ if (event.target.nodeName !== 'INPUT' && event.target.nodeName !== 'TEXTAREA') {
+ this.close(_activeDialog);
+
+ return false;
+ }
+ }
+
+ return true;
+ }).bind(this);
+
+ UiScreen.on('screen-xs', {
+ match: function() { _dialogFullHeight = true; },
+ unmatch: function() { _dialogFullHeight = false; },
+ setup: function() { _dialogFullHeight = true; }
+ });
+
+ this._initStaticDialogs();
+ DomChangeListener.add('Ui/Dialog', this._initStaticDialogs.bind(this));
+
+ UiScreen.setDialogContainer(_container);
+
+ // mobile safari dynamically shows/hides the bottom browser bar
+ // causing the window height to differ significantly
+ if (Environment.platform() === 'ios') {
+ window.addEventListener('resize', (function () {
+ _dialogs.forEach((function (dialog) {
+ if (!elAttrBool(dialog.dialog, 'aria-hidden')) {
+ this.rebuild(elData(dialog.dialog, 'id'));
+ }
+ }).bind(this));
+ }).bind(this));
+ }
+ },
+
+ _initStaticDialogs: function() {
+ var button, container, id;
+ while (_staticDialogs.length) {
+ button = _staticDialogs[0];
+ button.classList.remove('jsStaticDialog');
+
+ id = elData(button, 'dialog-id');
+ if (id && (container = elById(id))) {
+ ((function(button, container) {
+ container.classList.remove('jsStaticDialogContent');
+ elData(container, 'is-static-dialog', true);
+ elHide(container);
+ button.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+
+ this.openStatic(container.id, null, { title: elData(container, 'title') });
+ }).bind(this));
+ }).bind(this))(button, container);
+ }
+ }
+ },
+
+ /**
+ * Opens the dialog and implicitly creates it on first usage.
+ *
+ * @param {object} callbackObject used to invoke `_dialogSetup()` on first call
+ * @param {(string|DocumentFragment=} html html content or document fragment to use for dialog content
+ * @returns {object<string, *>} dialog data
+ */
+ open: function(callbackObject, html) {
+ var dialogData = _dialogObjects.get(callbackObject);
+ if (Core.isPlainObject(dialogData)) {
+ // dialog already exists
+ return this.openStatic(dialogData.id, html);
+ }
+
+ // initialize a new dialog
+ if (typeof callbackObject._dialogSetup !== 'function') {
+ throw new Error("Callback object does not implement the method '_dialogSetup()'.");
+ }
+
+ var setupData = callbackObject._dialogSetup();
+ if (!Core.isPlainObject(setupData)) {
+ throw new Error("Expected an object literal as return value of '_dialogSetup()'.");
+ }
+
+ dialogData = { id: setupData.id };
+
+ var createOnly = true;
+ if (setupData.source === undefined) {
+ var dialogElement = elById(setupData.id);
+ if (dialogElement === null) {
+ throw new Error("Element id '" + setupData.id + "' is invalid and no source attribute was given. If you want to use the `html` argument instead, please add `source: null` to your dialog configuration.");
+ }
+
+ setupData.source = document.createDocumentFragment();
+ setupData.source.appendChild(dialogElement);
+
+ // remove id and `display: none` from dialog element
+ dialogElement.removeAttribute('id');
+ elShow(dialogElement);
+ }
+ else if (setupData.source === null) {
+ // `null` means there is no static markup and `html` should be used instead
+ setupData.source = html;
+ }
+
+ else if (typeof setupData.source === 'function') {
+ setupData.source();
+ }
+ else if (Core.isPlainObject(setupData.source)) {
+ if (typeof html === 'string' && html.trim() !== '') {
+ setupData.source = html;
+ }
+ else {
+ Ajax.api(this, setupData.source.data, (function (data) {
+ if (data.returnValues && typeof data.returnValues.template === 'string') {
+ this.open(callbackObject, data.returnValues.template);
+
+ if (typeof setupData.source.after === 'function') {
+ setupData.source.after(_dialogs.get(setupData.id).content, data);
+ }
+ }
+ }).bind(this));
+
+ // deferred initialization
+ return {};
+ }
+ }
+ else {
+ if (typeof setupData.source === 'string') {
+ var dialogElement = elCreate('div');
+ elAttr(dialogElement, 'id', setupData.id);
+ DomUtil.setInnerHtml(dialogElement, setupData.source);
+
+ setupData.source = document.createDocumentFragment();
+ setupData.source.appendChild(dialogElement);
+ }
+
+ if (!setupData.source.nodeType || setupData.source.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
+ throw new Error("Expected at least a document fragment as 'source' attribute.");
+ }
+
+ createOnly = false;
+ }
+
+ _dialogObjects.set(callbackObject, dialogData);
+ _dialogToObject.set(setupData.id, callbackObject);
+
+ return this.openStatic(setupData.id, setupData.source, setupData.options, createOnly);
+ },
+
+ /**
+ * Opens an dialog, if the dialog is already open the content container
+ * will be replaced by the HTML string contained in the parameter html.
+ *
+ * If id is an existing element id, html will be ignored and the referenced
+ * element will be appended to the content element instead.
+ *
+ * @param {string} id element id, if exists the html parameter is ignored in favor of the existing element
+ * @param {?(string|DocumentFragment)} html content html
+ * @param {object<string, *>} options list of options, is completely ignored if the dialog already exists
+ * @param {boolean=} createOnly create the dialog but do not open it
+ * @return {object<string, *>} dialog data
+ */
+ openStatic: function(id, html, options, createOnly) {
+ UiScreen.pageOverlayOpen();
+
+ if (Environment.platform() !== 'desktop') {
+ if (!this.isOpen(id)) {
+ UiScreen.scrollDisable();
+ }
+ }
+
+ if (_dialogs.has(id)) {
+ this._updateDialog(id, html);
+ }
+ else {
+ options = Core.extend({
+ backdropCloseOnClick: true,
+ closable: true,
+ closeButtonLabel: Language.get('wcf.global.button.close'),
+ closeConfirmMessage: '',
+ disableContentPadding: false,
+ title: '',
+
+ // callbacks
+ onBeforeClose: null,
+ onClose: null,
+ onShow: null
+ }, options);
+
+ if (!options.closable) options.backdropCloseOnClick = false;
+ if (options.closeConfirmMessage) {
+ options.onBeforeClose = (function(id) {
+ UiConfirmation.show({
+ confirm: this.close.bind(this, id),
+ message: options.closeConfirmMessage
+ });
+ }).bind(this);
+ }
+
+ this._createDialog(id, html, options);
+ }
+
+ var data = _dialogs.get(id);
+
+ // iOS breaks `position: fixed` when input elements or `contenteditable`
+ // are focused, this will freeze the screen and force Safari to scroll
+ // to the input field
+ if (Environment.platform() === 'ios') {
+ window.setTimeout((function () {
+ var input = elBySel('input, textarea', data.content);
+ if (input !== null) {
+ input.focus();
+ }
+ }).bind(this), 200);
+ }
+
+ return data;
+ },
+
+ /**
+ * Sets the dialog title.
+ *
+ * @param {(string|object)} id element id
+ * @param {string} title dialog title
+ */
+ setTitle: function(id, title) {
+ id = this._getDialogId(id);
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ var dialogTitle = elByClass('dialogTitle', data.dialog);
+ if (dialogTitle.length) {
+ dialogTitle[0].textContent = title;
+ }
+ },
+
+ /**
+ * Sets a callback function on runtime.
+ *
+ * @param {(string|object)} id element id
+ * @param {string} key callback identifier
+ * @param {?function} value callback function or `null`
+ */
+ setCallback: function(id, key, value) {
+ if (typeof id === 'object') {
+ var dialogData = _dialogObjects.get(id);
+ if (dialogData !== undefined) {
+ id = dialogData.id;
+ }
+ }
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ if (_validCallbacks.indexOf(key) === -1) {
+ throw new Error("Invalid callback identifier, '" + key + "' is not recognized.");
+ }
+
+ if (typeof value !== 'function' && value !== null) {
+ throw new Error("Only functions or the 'null' value are acceptable callback values ('" + typeof value+ "' given).");
+ }
+
+ data[key] = value;
+ },
+
+ /**
+ * Creates the DOM for a new dialog and opens it.
+ *
+ * @param {string} id element id, if exists the html parameter is ignored in favor of the existing element
+ * @param {?(string|DocumentFragment)} html content html
+ * @param {object<string, *>} options list of options
+ * @param {boolean=} createOnly create the dialog but do not open it
+ */
+ _createDialog: function(id, html, options, createOnly) {
+ var element = null;
+ if (html === null) {
+ element = elById(id);
+ if (element === null) {
+ throw new Error("Expected either a HTML string or an existing element id.");
+ }
+ }
+
+ var dialog = elCreate('div');
+ dialog.classList.add('dialogContainer');
+ elAttr(dialog, 'aria-hidden', 'true');
+ elAttr(dialog, 'role', 'dialog');
+ elData(dialog, 'id', id);
+
+ var header = elCreate('header');
+ dialog.appendChild(header);
+
+ var titleId = DomUtil.getUniqueId();
+ elAttr(dialog, 'aria-labelledby', titleId);
+
+ var title = elCreate('span');
+ title.classList.add('dialogTitle');
+ title.textContent = options.title;
+ elAttr(title, 'id', titleId);
+ header.appendChild(title);
+
+ if (options.closable) {
+ var closeButton = elCreate('a');
+ closeButton.className = 'dialogCloseButton jsTooltip';
+ elAttr(closeButton, 'role', 'button');
+ elAttr(closeButton, 'tabindex', '0');
+ elAttr(closeButton, 'title', options.closeButtonLabel);
+ elAttr(closeButton, 'aria-label', options.closeButtonLabel);
+ closeButton.addEventListener(WCF_CLICK_EVENT, this._close.bind(this));
+ header.appendChild(closeButton);
+
+ var span = elCreate('span');
+ span.className = 'icon icon24 fa-times';
+ closeButton.appendChild(span);
+ }
+
+ var contentContainer = elCreate('div');
+ contentContainer.classList.add('dialogContent');
+ if (options.disableContentPadding) contentContainer.classList.add('dialogContentNoPadding');
+ dialog.appendChild(contentContainer);
+
+ contentContainer.addEventListener('wheel', function (event) {
+ var allowScroll = false;
+ var element = event.target, clientHeight, scrollHeight, scrollTop;
+ while (true) {
+ clientHeight = element.clientHeight;
+ scrollHeight = element.scrollHeight;
+
+ if (clientHeight < scrollHeight) {
+ scrollTop = element.scrollTop;
+
+ // negative value: scrolling up
+ if (event.deltaY < 0 && scrollTop > 0) {
+ allowScroll = true;
+ break;
+ }
+ else if (event.deltaY > 0 && (scrollTop + clientHeight < scrollHeight)) {
+ allowScroll = true;
+ break;
+ }
+ }
+
+ if (!element || element === contentContainer) {
+ break;
+ }
+
+ element = element.parentNode;
+ }
+
+ if (allowScroll === false) {
+ event.preventDefault();
+ }
+ }, { passive: false });
+
+ var content;
+ if (element === null) {
+ if (typeof html === 'string') {
+ content = elCreate('div');
+ content.id = id;
+ DomUtil.setInnerHtml(content, html);
+ }
+ else if (html instanceof DocumentFragment) {
+ var children = [], node;
+ for (var i = 0, length = html.childNodes.length; i < length; i++) {
+ node = html.childNodes[i];
+
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ children.push(node);
+ }
+ }
+
+ if (children[0].nodeName !== 'DIV' || children.length > 1) {
+ content = elCreate('div');
+ content.id = id;
+ content.appendChild(html);
+ }
+ else {
+ content = children[0];
+ }
+ }
+ else {
+ throw new TypeError("'html' must either be a string or a DocumentFragment");
+ }
+ }
+ else {
+ content = element;
+ }
+
+ contentContainer.appendChild(content);
+
+ if (content.style.getPropertyValue('display') === 'none') {
+ elShow(content);
+ }
+
+ _dialogs.set(id, {
+ backdropCloseOnClick: options.backdropCloseOnClick,
+ closable: options.closable,
+ content: content,
+ dialog: dialog,
+ header: header,
+ onBeforeClose: options.onBeforeClose,
+ onClose: options.onClose,
+ onShow: options.onShow,
+
+ submitButton: null,
+ inputFields: new List()
+ });
+
+ DomUtil.prepend(dialog, _container);
+
+ if (typeof options.onSetup === 'function') {
+ options.onSetup(content);
+ }
+
+ if (createOnly !== true) {
+ this._updateDialog(id, null);
+ }
+ },
+
+ /**
+ * Updates the dialog's content element.
+ *
+ * @param {string} id element id
+ * @param {?string} html content html, prevent changes by passing null
+ */
+ _updateDialog: function(id, html) {
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ if (typeof html === 'string') {
+ DomUtil.setInnerHtml(data.content, html);
+ }
+
+ if (elAttr(data.dialog, 'aria-hidden') === 'true') {
+ if (_callbackFocus === null) {
+ _callbackFocus = this._maintainFocus.bind(this);
+ document.body.addEventListener('focus', _callbackFocus, { capture: true });
+ }
+
+ if (data.closable && elAttr(_container, 'aria-hidden') === 'true') {
+ window.addEventListener('keyup', _keyupListener);
+ }
+
+ elAttr(data.dialog, 'aria-hidden', 'false');
+ elAttr(_container, 'aria-hidden', 'false');
+ elData(_container, 'close-on-click', (data.backdropCloseOnClick ? 'true' : 'false'));
+ _activeDialog = id;
+
+ // Keep a reference to the currently focused element to be able to restore it later.
+ _focusedBeforeDialog = document.activeElement;
+
+ // Set the focus to the first focusable child of the dialog element.
+ var closeButton = elBySel('.dialogCloseButton', data.header);
+ if (closeButton) elAttr(closeButton, 'inert', true);
+ this._setFocusToFirstItem(data.dialog);
+ if (closeButton) closeButton.removeAttribute('inert');
+
+ if (typeof data.onShow === 'function') {
+ data.onShow(data.content);
+ }
+
+ if (elDataBool(data.content, 'is-static-dialog')) {
+ EventHandler.fire('com.woltlab.wcf.dialog', 'openStatic', {
+ content: data.content,
+ id: id
+ });
+ }
+
+ // close existing dropdowns
+ UiSimpleDropdown.closeAll();
+ window.WCF.Dropdown.Interactive.Handler.closeAll();
+ }
+
+ this.rebuild(id);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * @param {Event} event
+ */
+ _maintainFocus: function(event) {
+ if (_activeDialog) {
+ var data = _dialogs.get(_activeDialog);
+ if (!data.dialog.contains(event.target) && !event.target.closest('.dropdownMenuContainer') && !event.target.closest('.datePicker')) {
+ this._setFocusToFirstItem(data.dialog, true);
+ }
+ }
+ },
+
+ /**
+ * @param {Element} dialog
+ * @param {boolean} maintain
+ */
+ _setFocusToFirstItem: function(dialog, maintain) {
+ var focusElement = this._getFirstFocusableChild(dialog);
+ if (focusElement !== null) {
+ if (maintain) {
+ if (focusElement.id === 'username' || focusElement.name === 'username') {
+ if (Environment.browser() === 'safari' && Environment.platform() === 'ios') {
+ // iOS Safari's username/password autofill breaks if the input field is focused
+ focusElement = null;
+ }
+ }
+ }
+
+ if (focusElement) focusElement.focus();
+ }
+ },
+
+ /**
+ * @param {Element} node
+ * @returns {?Element}
+ */
+ _getFirstFocusableChild: function(node) {
+ var nodeList = elBySelAll(_focusableElements.join(','), node);
+ for (var i = 0, length = nodeList.length; i < length; i++) {
+ if (nodeList[i].offsetWidth && nodeList[i].offsetHeight && nodeList[i].getClientRects().length) {
+ return nodeList[i];
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Rebuilds dialog identified by given id.
+ *
+ * @param {string} id element id
+ */
+ rebuild: function(id) {
+ id = this._getDialogId(id);
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ // ignore non-active dialogs
+ if (elAttr(data.dialog, 'aria-hidden') === 'true') {
+ return;
+ }
+
+ var contentContainer = data.content.parentNode;
+
+ var formSubmit = elBySel('.formSubmit', data.content);
+ var unavailableHeight = 0;
+ if (formSubmit !== null) {
+ contentContainer.classList.add('dialogForm');
+ formSubmit.classList.add('dialogFormSubmit');
+
+ unavailableHeight += DomUtil.outerHeight(formSubmit);
+
+ // Calculated height can be a fractional value and depending on the
+ // browser the results can vary. By subtracting a single pixel we're
+ // working around fractional values, without visually changing anything.
+ unavailableHeight -= 1;
+
+ contentContainer.style.setProperty('margin-bottom', unavailableHeight + 'px', '');
+ }
+ else {
+ contentContainer.classList.remove('dialogForm');
+ contentContainer.style.removeProperty('margin-bottom');
+ }
+
+ unavailableHeight += DomUtil.outerHeight(data.header);
+
+ var maximumHeight = (window.innerHeight * (_dialogFullHeight ? 1 : 0.8)) - unavailableHeight;
+ contentContainer.style.setProperty('max-height', ~~maximumHeight + 'px', '');
+
+ // Chrome and Safari use heavy anti-aliasing when the dialog's width
+ // cannot be evenly divided, causing the whole text to become blurry
+ if (Environment.browser() === 'chrome' || Environment.browser() === 'safari') {
+ // `clientWidth` will report an integer value that isn't rounded properly (e.g. 0.59 -> 0)
+ var floatWidth = parseFloat(window.getComputedStyle(data.content).width);
+ var needsFix = (Math.round(floatWidth) % 2) !== 0;
+
+ data.content.parentNode.classList[(needsFix ? 'add' : 'remove')]('jsWebKitFractionalPixel');
+ }
+
+ var callbackObject = _dialogToObject.get(id);
+ //noinspection JSUnresolvedVariable
+ if (callbackObject !== undefined && typeof callbackObject._dialogSubmit === 'function') {
+ var inputFields = elBySelAll('input[data-dialog-submit-on-enter="true"]', data.content);
+
+ var submitButton = elBySel('.formSubmit > input[type="submit"], .formSubmit > button[data-type="submit"]', data.content);
+ if (submitButton === null) {
+ // check if there is at least one input field with submit handling,
+ // otherwise we'll assume the dialog has not been populated yet
+ if (inputFields.length === 0) {
+ console.warn("Broken dialog, expected a submit button.", data.content);
+ }
+
+ return;
+ }
+
+ if (data.submitButton !== submitButton) {
+ data.submitButton = submitButton;
+
+ submitButton.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ this._submit(id);
+ }).bind(this));
+
+ // bind input fields
+ var inputField, _callbackKeydown = null;
+ for (var i = 0, length = inputFields.length; i < length; i++) {
+ inputField = inputFields[i];
+
+ if (data.inputFields.has(inputField)) continue;
+
+ if (_validInputTypes.indexOf(inputField.type) === -1) {
+ console.warn("Unsupported input type.", inputField);
+ continue;
+ }
+
+ data.inputFields.add(inputField);
+
+ if (_callbackKeydown === null) {
+ _callbackKeydown = (function (event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ this._submit(id);
+ }
+ }).bind(this);
+ }
+ inputField.addEventListener('keydown', _callbackKeydown);
+ }
+ }
+ }
+ },
+
+ /**
+ * Submits the dialog.
+ *
+ * @param {string} id dialog id
+ * @protected
+ */
+ _submit: function (id) {
+ var data = _dialogs.get(id);
+
+ var isValid = true;
+ data.inputFields.forEach(function (inputField) {
+ if (inputField.required) {
+ if (inputField.value.trim() === '') {
+ elInnerError(inputField, Language.get('wcf.global.form.error.empty'));
+
+ isValid = false;
+ }
+ else {
+ elInnerError(inputField, false);
+ }
+ }
+ });
+
+ if (isValid) {
+ //noinspection JSUnresolvedFunction
+ _dialogToObject.get(id)._dialogSubmit();
+ }
+ },
+
+ /**
+ * Handles clicks on the close button or the backdrop if enabled.
+ *
+ * @param {object} event click event
+ * @return {boolean} false if the event should be cancelled
+ */
+ _close: function(event) {
+ event.preventDefault();
+
+ var data = _dialogs.get(_activeDialog);
+ if (typeof data.onBeforeClose === 'function') {
+ data.onBeforeClose(_activeDialog);
+
+ return false;
+ }
+
+ this.close(_activeDialog);
+ },
+
+ /**
+ * Closes the current active dialog by clicks on the backdrop.
+ *
+ * @param {object} event event object
+ */
+ _closeOnBackdrop: function(event) {
+ if (event.target !== _container) {
+ return true;
+ }
+
+ if (elData(_container, 'close-on-click') === 'true') {
+ this._close(event);
+ }
+ else {
+ event.preventDefault();
+ }
+ },
+
+ /**
+ * Closes a dialog identified by given id.
+ *
+ * @param {(string|object)} id element id or callback object
+ */
+ close: function(id) {
+ id = this._getDialogId(id);
+
+ var data = _dialogs.get(id);
+ if (data === undefined) {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ elAttr(data.dialog, 'aria-hidden', 'true');
+
+ // avoid keyboard focus on a now hidden element
+ if (document.activeElement.closest('.dialogContainer') === data.dialog) {
+ document.activeElement.blur();
+ }
+
+ if (typeof data.onClose === 'function') {
+ data.onClose(id);
+ }
+
+ // get next active dialog
+ _activeDialog = null;
+ for (var i = 0; i < _container.childElementCount; i++) {
+ var child = _container.children[i];
+ if (elAttr(child, 'aria-hidden') === 'false') {
+ _activeDialog = elData(child, 'id');
+ break;
+ }
+ }
+
+ if (_activeDialog === null) {
+ elAttr(_container, 'aria-hidden', 'true');
+ elData(_container, 'close-on-click', 'false');
+
+ if (data.closable) {
+ window.removeEventListener('keyup', _keyupListener);
+ }
+
+ UiScreen.pageOverlayClose();
+ }
+ else {
+ data = _dialogs.get(_activeDialog);
+ elData(_container, 'close-on-click', (data.backdropCloseOnClick ? 'true' : 'false'));
+ }
+
+ if (Environment.platform() !== 'desktop') {
+ UiScreen.scrollEnable();
+ }
+ },
+
+ /**
+ * Returns the dialog data for given element id.
+ *
+ * @param {(string|object)} id element id or callback object
+ * @return {(object|undefined)} dialog data or undefined if element id is unknown
+ */
+ getDialog: function(id) {
+ return _dialogs.get(this._getDialogId(id));
+ },
+
+ /**
+ * Returns true for open dialogs.
+ *
+ * @param {(string|object)} id element id or callback object
+ * @return {boolean}
+ */
+ isOpen: function(id) {
+ var data = this.getDialog(id);
+ return (data !== undefined && elAttr(data.dialog, 'aria-hidden') === 'false');
+ },
+
+ /**
+ * Destroys a dialog instance.
+ *
+ * @param {Object} callbackObject the same object that was used to invoke `_dialogSetup()` on first call
+ */
+ destroy: function(callbackObject) {
+ if (typeof callbackObject !== 'object' || callbackObject instanceof String) {
+ throw new TypeError("Expected the callback object as parameter.");
+ }
+
+ if (_dialogObjects.has(callbackObject)) {
+ var id = _dialogObjects.get(callbackObject).id;
+ if (this.isOpen(id)) {
+ this.close(id);
+ }
+
+ _dialogs.delete(id);
+ _dialogObjects.delete(callbackObject);
+ }
+ },
+
+ /**
+ * Returns a dialog's id.
+ *
+ * @param {(string|object)} id element id or callback object
+ * @return {string}
+ * @protected
+ */
+ _getDialogId: function(id) {
+ if (typeof id === 'object') {
+ var dialogData = _dialogObjects.get(id);
+ if (dialogData !== undefined) {
+ return dialogData.id;
+ }
+ }
+
+ return id.toString();
+ },
+
+ _ajaxSetup: function() {
+ return {};
+ }
+ };
+});
+
+/**
+ * Provides the AJAX status overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax/Status
+ */
+define('WoltLabSuite/Core/Ajax/Status',['Language'], function(Language) {
+ "use strict";
+
+ var _activeRequests = 0;
+ var _overlay = null;
+ var _timeoutShow = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ajax/Status
+ */
+ var AjaxStatus = {
+ /**
+ * Initializes the status overlay on first usage.
+ */
+ _init: function() {
+ _overlay = elCreate('div');
+ _overlay.classList.add('spinner');
+ elAttr(_overlay, 'role', 'status');
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ _overlay.appendChild(icon);
+
+ var title = elCreate('span');
+ title.textContent = Language.get('wcf.global.loading');
+ _overlay.appendChild(title);
+
+ document.body.appendChild(_overlay);
+ },
+
+ /**
+ * Shows the loading overlay.
+ */
+ show: function() {
+ if (_overlay === null) {
+ this._init();
+ }
+
+ _activeRequests++;
+
+ if (_timeoutShow === null) {
+ _timeoutShow = window.setTimeout(function() {
+ if (_activeRequests) {
+ _overlay.classList.add('active');
+ }
+
+ _timeoutShow = null;
+ }, 250);
+ }
+ },
+
+ /**
+ * Hides the loading overlay.
+ */
+ hide: function() {
+ _activeRequests--;
+
+ if (_activeRequests === 0) {
+ if (_timeoutShow !== null) {
+ window.clearTimeout(_timeoutShow);
+ }
+
+ _overlay.classList.remove('active');
+ }
+ }
+ };
+
+ return AjaxStatus;
+});
+
+/**
+ * Versatile AJAX request handling.
+ *
+ * In case you want to issue JSONP requests, please use `AjaxJsonp` instead.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax/Request
+ */
+define('WoltLabSuite/Core/Ajax/Request',['Core', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Ui/Dialog', 'WoltLabSuite/Core/Ajax/Status'], function(Core, Language, DomChangeListener, DomUtil, UiDialog, AjaxStatus) {
+ "use strict";
+
+ var _didInit = false;
+ var _ignoreAllErrors = false;
+
+ /**
+ * @constructor
+ */
+ function AjaxRequest(options) {
+ this._data = null;
+ this._options = {};
+ this._previousXhr = null;
+ this._xhr = null;
+
+ this._init(options);
+ }
+ AjaxRequest.prototype = {
+ /**
+ * Initializes the request options.
+ *
+ * @param {Object} options request options
+ */
+ _init: function(options) {
+ this._options = Core.extend({
+ // request data
+ data: {},
+ contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
+ responseType: 'application/json',
+ type: 'POST',
+ url: '',
+ withCredentials: false,
+
+ // behavior
+ autoAbort: false,
+ ignoreError: false,
+ pinData: false,
+ silent: false,
+ includeRequestedWith: true,
+
+ // callbacks
+ failure: null,
+ finalize: null,
+ success: null,
+ progress: null,
+ uploadProgress: null,
+
+ callbackObject: null
+ }, options);
+
+ if (typeof options.callbackObject === 'object') {
+ this._options.callbackObject = options.callbackObject;
+ }
+
+ this._options.url = Core.convertLegacyUrl(this._options.url);
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
+ }
+
+ if (this._options.url.indexOf(WSC_API_URL) === 0) {
+ this._options.includeRequestedWith = true;
+ // always include credentials when querying the very own server
+ this._options.withCredentials = true;
+ }
+
+ if (this._options.pinData) {
+ this._data = Core.extend({}, this._options.data);
+ }
+
+ if (this._options.callbackObject !== null) {
+ if (typeof this._options.callbackObject._ajaxFailure === 'function') this._options.failure = this._options.callbackObject._ajaxFailure.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxFinalize === 'function') this._options.finalize = this._options.callbackObject._ajaxFinalize.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxSuccess === 'function') this._options.success = this._options.callbackObject._ajaxSuccess.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxProgress === 'function') this._options.progress = this._options.callbackObject._ajaxProgress.bind(this._options.callbackObject);
+ if (typeof this._options.callbackObject._ajaxUploadProgress === 'function') this._options.uploadProgress = this._options.callbackObject._ajaxUploadProgress.bind(this._options.callbackObject);
+ }
+
+ if (_didInit === false) {
+ _didInit = true;
+
+ window.addEventListener('beforeunload', function() { _ignoreAllErrors = true; });
+ }
+ },
+
+ /**
+ * Dispatches a request, optionally aborting a currently active request.
+ *
+ * @param {boolean} abortPrevious abort currently active request
+ */
+ sendRequest: function(abortPrevious) {
+ if (abortPrevious === true || this._options.autoAbort) {
+ this.abortPrevious();
+ }
+
+ if (!this._options.silent) {
+ AjaxStatus.show();
+ }
+
+ if (this._xhr instanceof XMLHttpRequest) {
+ this._previousXhr = this._xhr;
+ }
+
+ this._xhr = new XMLHttpRequest();
+ this._xhr.open(this._options.type, this._options.url, true);
+ if (this._options.contentType) {
+ this._xhr.setRequestHeader('Content-Type', this._options.contentType);
+ }
+ if (this._options.withCredentials || this._options.includeRequestedWith) {
+ this._xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ }
+ if (this._options.withCredentials) {
+ this._xhr.withCredentials = true;
+ }
+
+ var self = this;
+ var options = Core.clone(this._options);
+ this._xhr.onload = function() {
+ if (this.readyState === XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300 || this.status === 304) {
+ if (options.responseType && this.getResponseHeader('Content-Type').indexOf(options.responseType) !== 0) {
+ // request succeeded but invalid response type
+ self._failure(this, options);
+ }
+ else {
+ self._success(this, options);
+ }
+ }
+ else {
+ self._failure(this, options);
+ }
+ }
+ };
+ this._xhr.onerror = function() {
+ self._failure(this, options);
+ };
+
+ if (this._options.progress) {
+ this._xhr.onprogress = this._options.progress;
+ }
+ if (this._options.uploadProgress) {
+ this._xhr.upload.onprogress = this._options.uploadProgress;
+ }
+
+ if (this._options.type === 'POST') {
+ var data = this._options.data;
+ if (typeof data === 'object' && Core.getType(data) !== 'FormData') {
+ data = Core.serialize(data);
+ }
+
+ this._xhr.send(data);
+ }
+ else {
+ this._xhr.send();
+ }
+ },
+
+ /**
+ * Aborts a previous request.
+ */
+ abortPrevious: function() {
+ if (this._previousXhr === null) {
+ return;
+ }
+
+ this._previousXhr.abort();
+ this._previousXhr = null;
+
+ if (!this._options.silent) {
+ AjaxStatus.hide();
+ }
+ },
+
+ /**
+ * Sets a specific option.
+ *
+ * @param {string} key option name
+ * @param {?} value option value
+ */
+ setOption: function(key, value) {
+ this._options[key] = value;
+ },
+
+ /**
+ * Returns an option by key or undefined.
+ *
+ * @param {string} key option name
+ * @return {(*|null)} option value or null
+ */
+ getOption: function(key) {
+ if (objOwns(this._options, key)) {
+ return this._options[key];
+ }
+
+ return null;
+ },
+
+ /**
+ * Sets request data while honoring pinned data from setup callback.
+ *
+ * @param {Object} data request data
+ */
+ setData: function(data) {
+ if (this._data !== null && Core.getType(data) !== 'FormData') {
+ data = Core.extend(this._data, data);
+ }
+
+ this._options.data = data;
+ },
+
+ /**
+ * Handles a successful request.
+ *
+ * @param {XMLHttpRequest} xhr request object
+ * @param {Object} options request options
+ */
+ _success: function(xhr, options) {
+ if (!options.silent) {
+ AjaxStatus.hide();
+ }
+
+ if (typeof options.success === 'function') {
+ var data = null;
+ if (xhr.getResponseHeader('Content-Type') === 'application/json') {
+ try {
+ data = JSON.parse(xhr.responseText);
+ }
+ catch (e) {
+ // invalid JSON
+ this._failure(xhr, options);
+
+ return;
+ }
+
+ // trim HTML before processing, see http://jquery.com/upgrade-guide/1.9/#jquery-htmlstring-versus-jquery-selectorstring
+ if (data && data.returnValues && data.returnValues.template !== undefined) {
+ data.returnValues.template = data.returnValues.template.trim();
+ }
+
+ // force-invoke the background queue
+ if (data && data.forceBackgroundQueuePerform) {
+ require(['WoltLabSuite/Core/BackgroundQueue'], function(BackgroundQueue) {
+ BackgroundQueue.invoke();
+ });
+ }
+ }
+
+ options.success(data, xhr.responseText, xhr, options.data);
+ }
+
+ this._finalize(options);
+ },
+
+ /**
+ * Handles failed requests, this can be both a successful request with
+ * a non-success status code or an entirely failed request.
+ *
+ * @param {XMLHttpRequest} xhr request object
+ * @param {Object} options request options
+ */
+ _failure: function (xhr, options) {
+ if (_ignoreAllErrors) {
+ return;
+ }
+
+ if (!options.silent) {
+ AjaxStatus.hide();
+ }
+
+ var data = null;
+ try {
+ data = JSON.parse(xhr.responseText);
+ }
+ catch (e) {}
+
+ var showError = true;
+ if (typeof options.failure === 'function') {
+ showError = options.failure((data || {}), (xhr.responseText || ''), xhr, options.data);
+ }
+
+ if (options.ignoreError !== true && showError !== false) {
+ var html = this.getErrorHtml(data, xhr);
+
+ if (html) {
+ if (UiDialog === undefined) UiDialog = require('Ui/Dialog');
+ UiDialog.openStatic(DomUtil.getUniqueId(), html, {
+ title: Language.get('wcf.global.error.title')
+ });
+ }
+ }
+
+ this._finalize(options);
+ },
+
+ /**
+ * Returns the inner HTML for an error/exception display.
+ *
+ * @param {Object} data
+ * @param {XMLHttpRequest} xhr
+ * @return {string}
+ */
+ getErrorHtml: function(data, xhr) {
+ var details = '';
+ var message = '';
+
+ if (data !== null) {
+ if (data.file && data.line) {
+ details += '<br><p>File:</p><p>' + data.file + ' in line ' + data.line + '</p>';
+ }
+
+ if (data.stacktrace) details += '<br><p>Stacktrace:</p><p>' + data.stacktrace + '</p>';
+ else if (data.exceptionID) details += '<br><p>Exception ID: <code>' + data.exceptionID + '</code></p>';
+
+ message = data.message;
+
+ data.previous.forEach(function(previous) {
+ details += '<hr><p>' + previous.message + '</p>';
+ details += '<br><p>Stacktrace</p><p>' + previous.stacktrace + '</p>';
+ });
+ }
+ else {
+ message = xhr.responseText;
+ }
+
+ if (!message || message === 'undefined') {
+ if (!ENABLE_DEBUG_MODE && !ENABLE_PRODUCTION_DEBUG_MODE) return null;
+
+ message = 'XMLHttpRequest failed without a responseText. Check your browser console.'
+ }
+
+ return '<div class="ajaxDebugMessage"><p>' + message + '</p>' + details + '</div>';
+ },
+
+ /**
+ * Finalizes a request.
+ *
+ * @param {Object} options request options
+ */
+ _finalize: function(options) {
+ if (typeof options.finalize === 'function') {
+ options.finalize(this._xhr);
+ }
+
+ this._previousXhr = null;
+
+ DomChangeListener.trigger();
+
+ // fix anchor tags generated through WCF::getAnchor()
+ var links = elBySelAll('a[href*="#"]');
+ for (var i = 0, length = links.length; i < length; i++) {
+ var link = links[i];
+ var href = elAttr(link, 'href');
+ if (href.indexOf('AJAXProxy') !== -1 || href.indexOf('ajax-proxy') !== -1) {
+ href = href.substr(href.indexOf('#'));
+ elAttr(link, 'href', document.location.toString().replace(/#.*/, '') + href);
+ }
+ }
+ }
+ };
+
+ return AjaxRequest;
+});
+
+/**
+ * Handles AJAX requests.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax
+ */
+define('WoltLabSuite/Core/Ajax',['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectMap) {
+ "use strict";
+
+ var _requests = new ObjectMap();
+
+ /**
+ * @exports WoltLabSuite/Core/Ajax
+ */
+ return {
+ /**
+ * Shorthand function to perform a request against the WCF-API with overrides
+ * for success and failure callbacks.
+ *
+ * @param {object} callbackObject callback object
+ * @param {object<string, *>=} data request data
+ * @param {function=} success success callback
+ * @param {function=} failure failure callback
+ * @return {AjaxRequest}
+ */
+ api: function(callbackObject, data, success, failure) {
+ // Fetch AjaxRequest, as it cannot be provided because of a circular dependency
+ if (AjaxRequest === undefined) AjaxRequest = require('AjaxRequest');
+
+ if (typeof data !== 'object') data = {};
+
+ var request = _requests.get(callbackObject);
+ if (request === undefined) {
+ if (typeof callbackObject._ajaxSetup !== 'function') {
+ throw new TypeError("Callback object must implement at least _ajaxSetup().");
+ }
+
+ var options = callbackObject._ajaxSetup();
+
+ options.pinData = true;
+ options.callbackObject = callbackObject;
+
+ if (!options.url) {
+ options.url = 'index.php?ajax-proxy/&t=' + SECURITY_TOKEN;
+ options.withCredentials = true;
+ }
+
+ request = new AjaxRequest(options);
+
+ _requests.set(callbackObject, request);
+ }
+
+ var oldSuccess = null;
+ var oldFailure = null;
+
+ if (typeof success === 'function') {
+ oldSuccess = request.getOption('success');
+ request.setOption('success', success);
+ }
+ if (typeof failure === 'function') {
+ oldFailure = request.getOption('failure');
+ request.setOption('failure', failure);
+ }
+
+ request.setData(data);
+ request.sendRequest();
+
+ // restore callbacks
+ if (oldSuccess !== null) request.setOption('success', oldSuccess);
+ if (oldFailure !== null) request.setOption('failure', oldFailure);
+
+ return request;
+ },
+
+ /**
+ * Shorthand function to perform a single request against the WCF-API.
+ *
+ * Please use `Ajax.api` if you're about to repeatedly send requests because this
+ * method will spawn an new and rather expensive `AjaxRequest` with each call.
+ *
+ * @param {object<string, *>} options request options
+ */
+ apiOnce: function(options) {
+ // Fetch AjaxRequest, as it cannot be provided because of a circular dependency
+ if (AjaxRequest === undefined) AjaxRequest = require('AjaxRequest');
+
+ options.pinData = false;
+ options.callbackObject = null;
+ if (!options.url) {
+ options.url = 'index.php?ajax-proxy/&t=' + SECURITY_TOKEN;
+ options.withCredentials = true;
+ }
+
+ var request = new AjaxRequest(options);
+ request.sendRequest(false);
+ },
+
+ /**
+ * Returns the request object used for an earlier call to `api()`.
+ *
+ * @param {Object} callbackObject callback object
+ * @return {AjaxRequest}
+ */
+ getRequestObject: function(callbackObject) {
+ if (!_requests.has(callbackObject)) {
+ throw new Error('Expected a previously used callback object, provided object is unknown.');
+ }
+
+ return _requests.get(callbackObject);
+ }
+ };
+});
+
+/**
+ * Manages the invocation of the background queue.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/BackgroundQueue
+ */
+define('WoltLabSuite/Core/BackgroundQueue',['Ajax'], function(Ajax) {
+ "use strict";
+
+ var _invocations = 0;
+ var _isBusy = false;
+ var _url = '';
+
+ /**
+ * @exports WoltLabSuite/Core/BackgroundQueue
+ */
+ return {
+ /**
+ * Sets the url of the background queue perform action.
+ *
+ * @param {string} url background queue perform url
+ */
+ setUrl: function (url) {
+ _url = url;
+ },
+
+ /**
+ * Invokes the background queue.
+ */
+ invoke: function () {
+ if (_url === '') {
+ console.error('The background queue has not been initialized yet.');
+ return;
+ }
+
+ if (_isBusy) return;
+
+ _isBusy = true;
+
+ Ajax.api(this);
+ },
+
+ _ajaxSuccess: function (data) {
+ _invocations++;
+
+ // invoke the queue up to 5 times in a row
+ if (data > 0 && _invocations < 5) {
+ window.setTimeout(function () {
+ _isBusy = false;
+ this.invoke();
+ }.bind(this), 1000);
+ }
+ else {
+ _isBusy = false;
+ _invocations = 0;
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ url: _url,
+ ignoreError: true,
+ silent: true
+ }
+ }
+ }
+});
+
+/**
+ * @license MIT or GPL-2.0
+ * @fileOverview Favico animations
+ * @author Miroslav Magda, http://blog.ejci.net
+ * @source: https://github.com/ejci/favico.js
+ * @version 0.3.10
+ */
+
+/**
+ * Create new favico instance
+ * @param {Object} Options
+ * @return {Object} Favico object
+ * @example
+ * var favico = new Favico({
+ * bgColor : '#d00',
+ * textColor : '#fff',
+ * fontFamily : 'sans-serif',
+ * fontStyle : 'bold',
+ * type : 'circle',
+ * position : 'down',
+ * animation : 'slide',
+ * elementId: false,
+ * element: null,
+ * dataUrl: function(url){},
+ * win: window
+ * });
+ */
+(function () {
+
+ var Favico = (function (opt) {
+ 'use strict';
+ opt = (opt) ? opt : {};
+ var _def = {
+ bgColor: '#d00',
+ textColor: '#fff',
+ fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,...
+ fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900
+ type: 'circle',
+ position: 'down', // down, up, left, leftup (upleft)
+ animation: 'slide',
+ elementId: false,
+ element: null,
+ dataUrl: false,
+ win: window
+ };
+ var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc;
+
+ _browser = {};
+ _browser.ff = typeof InstallTrigger != 'undefined';
+ _browser.chrome = !!window.chrome;
+ _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0;
+ _browser.ie = /*@cc_on!@*/false;
+ _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
+ _browser.supported = (_browser.chrome || _browser.ff || _browser.opera);
+
+ var _queue = [];
+ _readyCb = function () {
+ };
+ _ready = _stop = false;
+ /**
+ * Initialize favico
+ */
+ var init = function () {
+ //merge initial options
+ _opt = merge(_def, opt);
+ _opt.bgColor = hexToRgb(_opt.bgColor);
+ _opt.textColor = hexToRgb(_opt.textColor);
+ _opt.position = _opt.position.toLowerCase();
+ _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation;
+
+ _doc = _opt.win.document;
+
+ var isUp = _opt.position.indexOf('up') > -1;
+ var isLeft = _opt.position.indexOf('left') > -1;
+
+ //transform the animations
+ if (isUp || isLeft) {
+ for (var a in animation.types) {
+ for (var i = 0; i < animation.types[a].length; i++) {
+ var step = animation.types[a][i];
+
+ if (isUp) {
+ if (step.y < 0.6) {
+ step.y = step.y - 0.4;
+ } else {
+ step.y = step.y - 2 * step.y + (1 - step.w);
+ }
+ }
+
+ if (isLeft) {
+ if (step.x < 0.6) {
+ step.x = step.x - 0.4;
+ } else {
+ step.x = step.x - 2 * step.x + (1 - step.h);
+ }
+ }
+
+ animation.types[a][i] = step;
+ }
+ }
+ }
+ _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type;
+
+ _orig = link. getIcons();
+ //create temp canvas
+ _canvas = document.createElement('canvas');
+ //create temp image
+ _img = document.createElement('img');
+ var lastIcon = _orig[_orig.length - 1];
+ if (lastIcon.hasAttribute('href')) {
+ _img.setAttribute('crossOrigin', 'anonymous');
+ //get width/height
+ _img.onload = function () {
+ _h = (_img.height > 0) ? _img.height : 32;
+ _w = (_img.width > 0) ? _img.width : 32;
+ _canvas.height = _h;
+ _canvas.width = _w;
+ _context = _canvas.getContext('2d');
+ icon.ready();
+ };
+ _img.setAttribute('src', lastIcon.getAttribute('href'));
+ } else {
+ _h = 32;
+ _w = 32;
+ _img.height = _h;
+ _img.width = _w;
+ _canvas.height = _h;
+ _canvas.width = _w;
+ _context = _canvas.getContext('2d');
+ icon.ready();
+ }
+
+ };
+ /**
+ * Icon namespace
+ */
+ var icon = {};
+ /**
+ * Icon is ready (reset icon) and start animation (if ther is any)
+ */
+ icon.ready = function () {
+ _ready = true;
+ icon.reset();
+ _readyCb();
+ };
+ /**
+ * Reset icon to default state
+ */
+ icon.reset = function () {
+ //reset
+ if (!_ready) {
+ return;
+ }
+ _queue = [];
+ _lastBadge = false;
+ _running = false;
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(_img, 0, 0, _w, _h);
+ //_stop=true;
+ link.setIcon(_canvas);
+ //webcam('stop');
+ //video('stop');
+ window.clearTimeout(_animTimeout);
+ window.clearTimeout(_drawTimeout);
+ };
+ /**
+ * Start animation
+ */
+ icon.start = function () {
+ if (!_ready || _running) {
+ return;
+ }
+ var finished = function () {
+ _lastBadge = _queue[0];
+ _running = false;
+ if (_queue.length > 0) {
+ _queue.shift();
+ icon.start();
+ } else {
+
+ }
+ };
+ if (_queue.length > 0) {
+ _running = true;
+ var run = function () {
+ // apply options for this animation
+ ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function (a) {
+ if (a in _queue[0].options) {
+ _opt[a] = _queue[0].options[a];
+ }
+ });
+ animation.run(_queue[0].options, function () {
+ finished();
+ }, false);
+ };
+ if (_lastBadge) {
+ animation.run(_lastBadge.options, function () {
+ run();
+ }, true);
+ } else {
+ run();
+ }
+ }
+ };
+
+ /**
+ * Badge types
+ */
+ var type = {};
+ var options = function (opt) {
+ opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n;
+ opt.x = _w * opt.x;
+ opt.y = _h * opt.y;
+ opt.w = _w * opt.w;
+ opt.h = _h * opt.h;
+ opt.len = ("" + opt.n).length;
+ return opt;
+ };
+ /**
+ * Generate circle
+ * @param {Object} opt Badge options
+ */
+ type.circle = function (opt) {
+ opt = options(opt);
+ var more = false;
+ if (opt.len === 2) {
+ opt.x = opt.x - opt.w * 0.4;
+ opt.w = opt.w * 1.4;
+ more = true;
+ } else if (opt.len >= 3) {
+ opt.x = opt.x - opt.w * 0.65;
+ opt.w = opt.w * 1.65;
+ more = true;
+ }
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(_img, 0, 0, _w, _h);
+ _context.beginPath();
+ _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px " + _opt.fontFamily;
+ _context.textAlign = 'center';
+ if (more) {
+ _context.moveTo(opt.x + opt.w / 2, opt.y);
+ _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y);
+ _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2);
+ _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2);
+ _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h);
+ _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h);
+ _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2);
+ _context.lineTo(opt.x, opt.y + opt.h / 2);
+ _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y);
+ } else {
+ _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI);
+ }
+ _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
+ _context.fill();
+ _context.closePath();
+ _context.beginPath();
+ _context.stroke();
+ _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
+ //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ if ((typeof opt.n) === 'number' && opt.n > 999) {
+ _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
+ } else {
+ _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ }
+ _context.closePath();
+ };
+ /**
+ * Generate rectangle
+ * @param {Object} opt Badge options
+ */
+ type.rectangle = function (opt) {
+ opt = options(opt);
+ var more = false;
+ if (opt.len === 2) {
+ opt.x = opt.x - opt.w * 0.4;
+ opt.w = opt.w * 1.4;
+ more = true;
+ } else if (opt.len >= 3) {
+ opt.x = opt.x - opt.w * 0.65;
+ opt.w = opt.w * 1.65;
+ more = true;
+ }
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(_img, 0, 0, _w, _h);
+ _context.beginPath();
+ _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + "px " + _opt.fontFamily;
+ _context.textAlign = 'center';
+ _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
+ _context.fillRect(opt.x, opt.y, opt.w, opt.h);
+ _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
+ //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ if ((typeof opt.n) === 'number' && opt.n > 999) {
+ _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
+ } else {
+ _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
+ }
+ _context.closePath();
+ };
+
+ /**
+ * Set badge
+ */
+ var badge = function (number, opts) {
+ opts = ((typeof opts) === 'string' ? {
+ animation: opts
+ } : opts) || {};
+ _readyCb = function () {
+ try {
+ if (typeof (number) === 'number' ? (number > 0) : (number !== '')) {
+ var q = {
+ type: 'badge',
+ options: {
+ n: number
+ }
+ };
+ if ('animation' in opts && animation.types['' + opts.animation]) {
+ q.options.animation = '' + opts.animation;
+ }
+ if ('type' in opts && type['' + opts.type]) {
+ q.options.type = '' + opts.type;
+ }
+ ['bgColor', 'textColor'].forEach(function (o) {
+ if (o in opts) {
+ q.options[o] = hexToRgb(opts[o]);
+ }
+ });
+ ['fontStyle', 'fontFamily'].forEach(function (o) {
+ if (o in opts) {
+ q.options[o] = opts[o];
+ }
+ });
+ _queue.push(q);
+ if (_queue.length > 100) {
+ throw new Error('Too many badges requests in queue.');
+ }
+ icon.start();
+ } else {
+ icon.reset();
+ }
+ } catch (e) {
+ throw new Error('Error setting badge. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+
+ /**
+ * Set image as icon
+ */
+ var image = function (imageElement) {
+ _readyCb = function () {
+ try {
+ var w = imageElement.width;
+ var h = imageElement.height;
+ var newImg = document.createElement('img');
+ var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
+ newImg.setAttribute('crossOrigin', 'anonymous');
+ newImg.onload=function(){
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(newImg, 0, 0, _w, _h);
+ link.setIcon(_canvas);
+ };
+ newImg.setAttribute('src', imageElement.getAttribute('src'));
+ newImg.height = (h / ratio);
+ newImg.width = (w / ratio);
+ } catch (e) {
+ throw new Error('Error setting image. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+ /**
+ * Set the icon from a source url. Won't work with badges.
+ */
+ var rawImageSrc = function (url) {
+ _readyCb = function() {
+ link.setIconSrc(url);
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+ /**
+ * Set video as icon
+ */
+ var video = function (videoElement) {
+ _readyCb = function () {
+ try {
+ if (videoElement === 'stop') {
+ _stop = true;
+ icon.reset();
+ _stop = false;
+ return;
+ }
+ //var w = videoElement.width;
+ //var h = videoElement.height;
+ //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
+ videoElement.addEventListener('play', function () {
+ drawVideo(this);
+ }, false);
+
+ } catch (e) {
+ throw new Error('Error setting video. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ };
+ /**
+ * Set video as icon
+ */
+ var webcam = function (action) {
+ //UR
+ if (!window.URL || !window.URL.createObjectURL) {
+ window.URL = window.URL || {};
+ window.URL.createObjectURL = function (obj) {
+ return obj;
+ };
+ }
+ if (_browser.supported) {
+ var newVideo = false;
+ navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
+ _readyCb = function () {
+ try {
+ if (action === 'stop') {
+ _stop = true;
+ icon.reset();
+ _stop = false;
+ return;
+ }
+ newVideo = document.createElement('video');
+ newVideo.width = _w;
+ newVideo.height = _h;
+ navigator.getUserMedia({
+ video: true,
+ audio: false
+ }, function (stream) {
+ newVideo.src = URL.createObjectURL(stream);
+ newVideo.play();
+ drawVideo(newVideo);
+ }, function () {
+ });
+ } catch (e) {
+ throw new Error('Error setting webcam. Message: ' + e.message);
+ }
+ };
+ if (_ready) {
+ _readyCb();
+ }
+ }
+
+ };
+
+ var setOpt = function (key, value) {
+ var opts = key;
+ if (!(value == null && Object.prototype.toString.call(key) == '[object Object]')) {
+ opts = {};
+ opts[key] = value;
+ }
+
+ var keys = Object.keys(opts);
+ for (var i = 0; i < keys.length; i++) {
+ if (keys[i] == 'bgColor' || keys[i] == 'textColor') {
+ _opt[keys[i]] = hexToRgb(opts[keys[i]]);
+ } else {
+ _opt[keys[i]] = opts[keys[i]];
+ }
+ }
+
+ _queue.push(_lastBadge);
+ icon.start();
+ };
+
+ /**
+ * Draw video to context and repeat :)
+ */
+ function drawVideo(video) {
+ if (video.paused || video.ended || _stop) {
+ return false;
+ }
+ //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl)
+ try {
+ _context.clearRect(0, 0, _w, _h);
+ _context.drawImage(video, 0, 0, _w, _h);
+ } catch (e) {
+
+ }
+ _drawTimeout = setTimeout(function () {
+ drawVideo(video);
+ }, animation.duration);
+ link.setIcon(_canvas);
+ }
+
+ var link = {};
+ /**
+ * Get icons from HEAD tag or create a new <link> element
+ */
+ link.getIcons = function () {
+ var elms = [];
+ //get link element
+ var getLinks = function () {
+ var icons = [];
+ var links = _doc.getElementsByTagName('head')[0].getElementsByTagName('link');
+ for (var i = 0; i < links.length; i++) {
+ if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute('rel'))) {
+ icons.push(links[i]);
+ }
+ }
+ return icons;
+ };
+ if (_opt.element) {
+ elms = [_opt.element];
+ } else if (_opt.elementId) {
+ //if img element identified by elementId
+ elms = [_doc.getElementById(_opt.elementId)];
+ elms[0].setAttribute('href', elms[0].getAttribute('src'));
+ } else {
+ //if link element
+ elms = getLinks();
+ if (elms.length === 0) {
+ elms = [_doc.createElement('link')];
+ elms[0].setAttribute('rel', 'icon');
+ _doc.getElementsByTagName('head')[0].appendChild(elms[0]);
+ }
+ }
+ elms.forEach(function(item) {
+ item.setAttribute('type', 'image/png');
+ });
+ return elms;
+ };
+ link.setIcon = function (canvas) {
+ var url = canvas.toDataURL('image/png');
+ link.setIconSrc(url);
+ };
+ link.setIconSrc = function (url) {
+ if (_opt.dataUrl) {
+ //if using custom exporter
+ _opt.dataUrl(url);
+ }
+ if (_opt.element) {
+ _opt.element.setAttribute('href', url);
+ _opt.element.setAttribute('src', url);
+ } else if (_opt.elementId) {
+ //if is attached to element (image)
+ var elm = _doc.getElementById(_opt.elementId);
+ elm.setAttribute('href', url);
+ elm.setAttribute('src', url);
+ } else {
+ //if is attached to fav icon
+ if (_browser.ff || _browser.opera) {
+ //for FF we need to "recreate" element, atach to dom and remove old <link>
+ //var originalType = _orig.getAttribute('rel');
+ var old = _orig[_orig.length - 1];
+ var newIcon = _doc.createElement('link');
+ _orig = [newIcon];
+ //_orig.setAttribute('rel', originalType);
+ if (_browser.opera) {
+ newIcon.setAttribute('rel', 'icon');
+ }
+ newIcon.setAttribute('rel', 'icon');
+ newIcon.setAttribute('type', 'image/png');
+ _doc.getElementsByTagName('head')[0].appendChild(newIcon);
+ newIcon.setAttribute('href', url);
+ if (old.parentNode) {
+ old.parentNode.removeChild(old);
+ }
+ } else {
+ _orig.forEach(function(icon) {
+ icon.setAttribute('href', url);
+ });
+ }
+ }
+ };
+
+ //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139
+ //HEX to RGB convertor
+ function hexToRgb(hex) {
+ var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+ hex = hex.replace(shorthandRegex, function (m, r, g, b) {
+ return r + r + g + g + b + b;
+ });
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : false;
+ }
+
+ /**
+ * Merge options
+ */
+ function merge(def, opt) {
+ var mergedOpt = {};
+ var attrname;
+ for (attrname in def) {
+ mergedOpt[attrname] = def[attrname];
+ }
+ for (attrname in opt) {
+ mergedOpt[attrname] = opt[attrname];
+ }
+ return mergedOpt;
+ }
+
+ /**
+ * Cross-browser page visibility shim
+ * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible
+ */
+ function isPageHidden() {
+ return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden;
+ }
+
+ /**
+ * @namespace animation
+ */
+ var animation = {};
+ /**
+ * Animation "frame" duration
+ */
+ animation.duration = 40;
+ /**
+ * Animation types (none,fade,pop,slide)
+ */
+ animation.types = {};
+ animation.types.fade = [{
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.0
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.1
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.2
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.3
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.4
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.5
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.6
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.7
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.8
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 0.9
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1.0
+ }];
+ animation.types.none = [{
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ animation.types.pop = [{
+ x: 1,
+ y: 1,
+ w: 0,
+ h: 0,
+ o: 1
+ }, {
+ x: 0.9,
+ y: 0.9,
+ w: 0.1,
+ h: 0.1,
+ o: 1
+ }, {
+ x: 0.8,
+ y: 0.8,
+ w: 0.2,
+ h: 0.2,
+ o: 1
+ }, {
+ x: 0.7,
+ y: 0.7,
+ w: 0.3,
+ h: 0.3,
+ o: 1
+ }, {
+ x: 0.6,
+ y: 0.6,
+ w: 0.4,
+ h: 0.4,
+ o: 1
+ }, {
+ x: 0.5,
+ y: 0.5,
+ w: 0.5,
+ h: 0.5,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ animation.types.popFade = [{
+ x: 0.75,
+ y: 0.75,
+ w: 0,
+ h: 0,
+ o: 0
+ }, {
+ x: 0.65,
+ y: 0.65,
+ w: 0.1,
+ h: 0.1,
+ o: 0.2
+ }, {
+ x: 0.6,
+ y: 0.6,
+ w: 0.2,
+ h: 0.2,
+ o: 0.4
+ }, {
+ x: 0.55,
+ y: 0.55,
+ w: 0.3,
+ h: 0.3,
+ o: 0.6
+ }, {
+ x: 0.50,
+ y: 0.50,
+ w: 0.4,
+ h: 0.4,
+ o: 0.8
+ }, {
+ x: 0.45,
+ y: 0.45,
+ w: 0.5,
+ h: 0.5,
+ o: 0.9
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ animation.types.slide = [{
+ x: 0.4,
+ y: 1,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.9,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.9,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.8,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.7,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.6,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.5,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }, {
+ x: 0.4,
+ y: 0.4,
+ w: 0.6,
+ h: 0.6,
+ o: 1
+ }];
+ /**
+ * Run animation
+ * @param {Object} opt Animation options
+ * @param {Object} cb Callabak after all steps are done
+ * @param {Object} revert Reverse order? true|false
+ * @param {Object} step Optional step number (frame bumber)
+ */
+ animation.run = function (opt, cb, revert, step) {
+ var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation];
+ if (revert === true) {
+ step = (typeof step !== 'undefined') ? step : animationType.length - 1;
+ } else {
+ step = (typeof step !== 'undefined') ? step : 0;
+ }
+ cb = (cb) ? cb : function () {
+ };
+ if ((step < animationType.length) && (step >= 0)) {
+ type[_opt.type](merge(opt, animationType[step]));
+ _animTimeout = setTimeout(function () {
+ if (revert) {
+ step = step - 1;
+ } else {
+ step = step + 1;
+ }
+ animation.run(opt, cb, revert, step);
+ }, animation.duration);
+
+ link.setIcon(_canvas);
+ } else {
+ cb();
+ return;
+ }
+ };
+ //auto init
+ init();
+ return {
+ badge: badge,
+ video: video,
+ image: image,
+ rawImageSrc: rawImageSrc,
+ webcam: webcam,
+ setOpt: setOpt,
+ reset: icon.reset,
+ browser: {
+ supported: _browser.supported
+ }
+ };
+ });
+
+ // AMD / RequireJS
+ if (typeof define !== 'undefined' && define.amd) {
+ define('favico',[], function () {
+ return Favico;
+ });
+ }
+ // CommonJS
+ else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Favico;
+ }
+ // included directly via <script> tag
+ else {
+ this.Favico = Favico;
+ }
+
+})();
+
+/*!
+ * enquire.js v2.1.2 - Awesome Media Queries in JavaScript
+ * Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/enquire.js
+ * License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+;(function (name, context, factory) {
+ var matchMedia = window.matchMedia;
+
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = factory(matchMedia);
+ }
+ else if (typeof define === 'function' && define.amd) {
+ define('enquire',[],function() {
+ return (context[name] = factory(matchMedia));
+ });
+ }
+ else {
+ context[name] = factory(matchMedia);
+ }
+}('enquire', this, function (matchMedia) {
+
+ 'use strict';
+
+ /*jshint unused:false */
+ /**
+ * Helper function for iterating over a collection
+ *
+ * @param collection
+ * @param fn
+ */
+ function each(collection, fn) {
+ var i = 0,
+ length = collection.length,
+ cont;
+
+ for(i; i < length; i++) {
+ cont = fn(collection[i], i);
+ if(cont === false) {
+ break; //allow early exit
+ }
+ }
+ }
+
+ /**
+ * Helper function for determining whether target object is an array
+ *
+ * @param target the object under test
+ * @return {Boolean} true if array, false otherwise
+ */
+ function isArray(target) {
+ return Object.prototype.toString.apply(target) === '[object Array]';
+ }
+
+ /**
+ * Helper function for determining whether target object is a function
+ *
+ * @param target the object under test
+ * @return {Boolean} true if function, false otherwise
+ */
+ function isFunction(target) {
+ return typeof target === 'function';
+ }
+
+ /**
+ * Delegate to handle a media query being matched and unmatched.
+ *
+ * @param {object} options
+ * @param {function} options.match callback for when the media query is matched
+ * @param {function} [options.unmatch] callback for when the media query is unmatched
+ * @param {function} [options.setup] one-time callback triggered the first time a query is matched
+ * @param {boolean} [options.deferSetup=false] should the setup callback be run immediately, rather than first time query is matched?
+ * @constructor
+ */
+ function QueryHandler(options) {
+ this.options = options;
+ !options.deferSetup && this.setup();
+ }
+ QueryHandler.prototype = {
+
+ /**
+ * coordinates setup of the handler
+ *
+ * @function
+ */
+ setup : function() {
+ if(this.options.setup) {
+ this.options.setup();
+ }
+ this.initialised = true;
+ },
+
+ /**
+ * coordinates setup and triggering of the handler
+ *
+ * @function
+ */
+ on : function() {
+ !this.initialised && this.setup();
+ this.options.match && this.options.match();
+ },
+
+ /**
+ * coordinates the unmatch event for the handler
+ *
+ * @function
+ */
+ off : function() {
+ this.options.unmatch && this.options.unmatch();
+ },
+
+ /**
+ * called when a handler is to be destroyed.
+ * delegates to the destroy or unmatch callbacks, depending on availability.
+ *
+ * @function
+ */
+ destroy : function() {
+ this.options.destroy ? this.options.destroy() : this.off();
+ },
+
+ /**
+ * determines equality by reference.
+ * if object is supplied compare options, if function, compare match callback
+ *
+ * @function
+ * @param {object || function} [target] the target for comparison
+ */
+ equals : function(target) {
+ return this.options === target || this.options.match === target;
+ }
+
+ };
+ /**
+ * Represents a single media query, manages it's state and registered handlers for this query
+ *
+ * @constructor
+ * @param {string} query the media query string
+ * @param {boolean} [isUnconditional=false] whether the media query should run regardless of whether the conditions are met. Primarily for helping older browsers deal with mobile-first design
+ */
+ function MediaQuery(query, isUnconditional) {
+ this.query = query;
+ this.isUnconditional = isUnconditional;
+ this.handlers = [];
+ this.mql = matchMedia(query);
+
+ var self = this;
+ this.listener = function(mql) {
+ self.mql = mql;
+ self.assess();
+ };
+ this.mql.addListener(this.listener);
+ }
+ MediaQuery.prototype = {
+
+ /**
+ * add a handler for this query, triggering if already active
+ *
+ * @param {object} handler
+ * @param {function} handler.match callback for when query is activated
+ * @param {function} [handler.unmatch] callback for when query is deactivated
+ * @param {function} [handler.setup] callback for immediate execution when a query handler is registered
+ * @param {boolean} [handler.deferSetup=false] should the setup callback be deferred until the first time the handler is matched?
+ */
+ addHandler : function(handler) {
+ var qh = new QueryHandler(handler);
+ this.handlers.push(qh);
+
+ this.matches() && qh.on();
+ },
+
+ /**
+ * removes the given handler from the collection, and calls it's destroy methods
+ *
+ * @param {object || function} handler the handler to remove
+ */
+ removeHandler : function(handler) {
+ var handlers = this.handlers;
+ each(handlers, function(h, i) {
+ if(h.equals(handler)) {
+ h.destroy();
+ return !handlers.splice(i,1); //remove from array and exit each early
+ }
+ });
+ },
+
+ /**
+ * Determine whether the media query should be considered a match
+ *
+ * @return {Boolean} true if media query can be considered a match, false otherwise
+ */
+ matches : function() {
+ return this.mql.matches || this.isUnconditional;
+ },
+
+ /**
+ * Clears all handlers and unbinds events
+ */
+ clear : function() {
+ each(this.handlers, function(handler) {
+ handler.destroy();
+ });
+ this.mql.removeListener(this.listener);
+ this.handlers.length = 0; //clear array
+ },
+
+ /*
+ * Assesses the query, turning on all handlers if it matches, turning them off if it doesn't match
+ */
+ assess : function() {
+ var action = this.matches() ? 'on' : 'off';
+
+ each(this.handlers, function(handler) {
+ handler[action]();
+ });
+ }
+ };
+ /**
+ * Allows for registration of query handlers.
+ * Manages the query handler's state and is responsible for wiring up browser events
+ *
+ * @constructor
+ */
+ function MediaQueryDispatch () {
+ if(!matchMedia) {
+ throw new Error('matchMedia not present, legacy browsers require a polyfill');
+ }
+
+ this.queries = {};
+ this.browserIsIncapable = !matchMedia('only all').matches;
+ }
+
+ MediaQueryDispatch.prototype = {
+
+ /**
+ * Registers a handler for the given media query
+ *
+ * @param {string} q the media query
+ * @param {object || Array || Function} options either a single query handler object, a function, or an array of query handlers
+ * @param {function} options.match fired when query matched
+ * @param {function} [options.unmatch] fired when a query is no longer matched
+ * @param {function} [options.setup] fired when handler first triggered
+ * @param {boolean} [options.deferSetup=false] whether setup should be run immediately or deferred until query is first matched
+ * @param {boolean} [shouldDegrade=false] whether this particular media query should always run on incapable browsers
+ */
+ register : function(q, options, shouldDegrade) {
+ var queries = this.queries,
+ isUnconditional = shouldDegrade && this.browserIsIncapable;
+
+ if(!queries[q]) {
+ queries[q] = new MediaQuery(q, isUnconditional);
+ }
+
+ //normalise to object in an array
+ if(isFunction(options)) {
+ options = { match : options };
+ }
+ if(!isArray(options)) {
+ options = [options];
+ }
+ each(options, function(handler) {
+ if (isFunction(handler)) {
+ handler = { match : handler };
+ }
+ queries[q].addHandler(handler);
+ });
+
+ return this;
+ },
+
+ /**
+ * unregisters a query and all it's handlers, or a specific handler for a query
+ *
+ * @param {string} q the media query to target
+ * @param {object || function} [handler] specific handler to unregister
+ */
+ unregister : function(q, handler) {
+ var query = this.queries[q];
+
+ if(query) {
+ if(handler) {
+ query.removeHandler(handler);
+ }
+ else {
+ query.clear();
+ delete this.queries[q];
+ }
+ }
+
+ return this;
+ }
+ };
+
+ return new MediaQueryDispatch();
+
+}));
+/* perfect-scrollbar v0.6.16 */
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+'use strict';
+
+var ps = require('../main');
+
+if (typeof define === 'function' && define.amd) {
+ // AMD
+ define('perfect-scrollbar',ps);
+} else {
+ // Add to a global object.
+ window.PerfectScrollbar = ps;
+ if (typeof window.Ps === 'undefined') {
+ window.Ps = ps;
+ }
+}
+
+},{"../main":7}],2:[function(require,module,exports){
+'use strict';
+
+function oldAdd(element, className) {
+ var classes = element.className.split(' ');
+ if (classes.indexOf(className) < 0) {
+ classes.push(className);
+ }
+ element.className = classes.join(' ');
+}
+
+function oldRemove(element, className) {
+ var classes = element.className.split(' ');
+ var idx = classes.indexOf(className);
+ if (idx >= 0) {
+ classes.splice(idx, 1);
+ }
+ element.className = classes.join(' ');
+}
+
+exports.add = function (element, className) {
+ if (element.classList) {
+ element.classList.add(className);
+ } else {
+ oldAdd(element, className);
+ }
+};
+
+exports.remove = function (element, className) {
+ if (element.classList) {
+ element.classList.remove(className);
+ } else {
+ oldRemove(element, className);
+ }
+};
+
+exports.list = function (element) {
+ if (element.classList) {
+ return Array.prototype.slice.apply(element.classList);
+ } else {
+ return element.className.split(' ');
+ }
+};
+
+},{}],3:[function(require,module,exports){
+'use strict';
+
+var DOM = {};
+
+DOM.e = function (tagName, className) {
+ var element = document.createElement(tagName);
+ element.className = className;
+ return element;
+};
+
+DOM.appendTo = function (child, parent) {
+ parent.appendChild(child);
+ return child;
+};
+
+function cssGet(element, styleName) {
+ return window.getComputedStyle(element)[styleName];
+}
+
+function cssSet(element, styleName, styleValue) {
+ if (typeof styleValue === 'number') {
+ styleValue = styleValue.toString() + 'px';
+ }
+ element.style[styleName] = styleValue;
+ return element;
+}
+
+function cssMultiSet(element, obj) {
+ for (var key in obj) {
+ var val = obj[key];
+ if (typeof val === 'number') {
+ val = val.toString() + 'px';
+ }
+ element.style[key] = val;
+ }
+ return element;
+}
+
+DOM.css = function (element, styleNameOrObject, styleValue) {
+ if (typeof styleNameOrObject === 'object') {
+ // multiple set with object
+ return cssMultiSet(element, styleNameOrObject);
+ } else {
+ if (typeof styleValue === 'undefined') {
+ return cssGet(element, styleNameOrObject);
+ } else {
+ return cssSet(element, styleNameOrObject, styleValue);
+ }
+ }
+};
+
+DOM.matches = function (element, query) {
+ if (typeof element.matches !== 'undefined') {
+ return element.matches(query);
+ } else {
+ if (typeof element.matchesSelector !== 'undefined') {
+ return element.matchesSelector(query);
+ } else if (typeof element.webkitMatchesSelector !== 'undefined') {
+ return element.webkitMatchesSelector(query);
+ } else if (typeof element.mozMatchesSelector !== 'undefined') {
+ return element.mozMatchesSelector(query);
+ } else if (typeof element.msMatchesSelector !== 'undefined') {
+ return element.msMatchesSelector(query);
+ }
+ }
+};
+
+DOM.remove = function (element) {
+ if (typeof element.remove !== 'undefined') {
+ element.remove();
+ } else {
+ if (element.parentNode) {
+ element.parentNode.removeChild(element);
+ }
+ }
+};
+
+DOM.queryChildren = function (element, selector) {
+ return Array.prototype.filter.call(element.childNodes, function (child) {
+ return DOM.matches(child, selector);
+ });
+};
+
+module.exports = DOM;
+
+},{}],4:[function(require,module,exports){
+'use strict';
+
+var EventElement = function (element) {
+ this.element = element;
+ this.events = {};
+};
+
+EventElement.prototype.bind = function (eventName, handler) {
+ if (typeof this.events[eventName] === 'undefined') {
+ this.events[eventName] = [];
+ }
+ this.events[eventName].push(handler);
+ this.element.addEventListener(eventName, handler, false);
+};
+
+EventElement.prototype.unbind = function (eventName, handler) {
+ var isHandlerProvided = (typeof handler !== 'undefined');
+ this.events[eventName] = this.events[eventName].filter(function (hdlr) {
+ if (isHandlerProvided && hdlr !== handler) {
+ return true;
+ }
+ this.element.removeEventListener(eventName, hdlr, false);
+ return false;
+ }, this);
+};
+
+EventElement.prototype.unbindAll = function () {
+ for (var name in this.events) {
+ this.unbind(name);
+ }
+};
+
+var EventManager = function () {
+ this.eventElements = [];
+};
+
+EventManager.prototype.eventElement = function (element) {
+ var ee = this.eventElements.filter(function (eventElement) {
+ return eventElement.element === element;
+ })[0];
+ if (typeof ee === 'undefined') {
+ ee = new EventElement(element);
+ this.eventElements.push(ee);
+ }
+ return ee;
+};
+
+EventManager.prototype.bind = function (element, eventName, handler) {
+ this.eventElement(element).bind(eventName, handler);
+};
+
+EventManager.prototype.unbind = function (element, eventName, handler) {
+ this.eventElement(element).unbind(eventName, handler);
+};
+
+EventManager.prototype.unbindAll = function () {
+ for (var i = 0; i < this.eventElements.length; i++) {
+ this.eventElements[i].unbindAll();
+ }
+};
+
+EventManager.prototype.once = function (element, eventName, handler) {
+ var ee = this.eventElement(element);
+ var onceHandler = function (e) {
+ ee.unbind(eventName, onceHandler);
+ handler(e);
+ };
+ ee.bind(eventName, onceHandler);
+};
+
+module.exports = EventManager;
+
+},{}],5:[function(require,module,exports){
+'use strict';
+
+module.exports = (function () {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return function () {
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+ s4() + '-' + s4() + s4() + s4();
+ };
+})();
+
+},{}],6:[function(require,module,exports){
+'use strict';
+
+var cls = require('./class');
+var dom = require('./dom');
+
+var toInt = exports.toInt = function (x) {
+ return parseInt(x, 10) || 0;
+};
+
+var clone = exports.clone = function (obj) {
+ if (!obj) {
+ return null;
+ } else if (obj.constructor === Array) {
+ return obj.map(clone);
+ } else if (typeof obj === 'object') {
+ var result = {};
+ for (var key in obj) {
+ result[key] = clone(obj[key]);
+ }
+ return result;
+ } else {
+ return obj;
+ }
+};
+
+exports.extend = function (original, source) {
+ var result = clone(original);
+ for (var key in source) {
+ result[key] = clone(source[key]);
+ }
+ return result;
+};
+
+exports.isEditable = function (el) {
+ return dom.matches(el, "input,[contenteditable]") ||
+ dom.matches(el, "select,[contenteditable]") ||
+ dom.matches(el, "textarea,[contenteditable]") ||
+ dom.matches(el, "button,[contenteditable]");
+};
+
+exports.removePsClasses = function (element) {
+ var clsList = cls.list(element);
+ for (var i = 0; i < clsList.length; i++) {
+ var className = clsList[i];
+ if (className.indexOf('ps-') === 0) {
+ cls.remove(element, className);
+ }
+ }
+};
+
+exports.outerWidth = function (element) {
+ return toInt(dom.css(element, 'width')) +
+ toInt(dom.css(element, 'paddingLeft')) +
+ toInt(dom.css(element, 'paddingRight')) +
+ toInt(dom.css(element, 'borderLeftWidth')) +
+ toInt(dom.css(element, 'borderRightWidth'));
+};
+
+exports.startScrolling = function (element, axis) {
+ cls.add(element, 'ps-in-scrolling');
+ if (typeof axis !== 'undefined') {
+ cls.add(element, 'ps-' + axis);
+ } else {
+ cls.add(element, 'ps-x');
+ cls.add(element, 'ps-y');
+ }
+};
+
+exports.stopScrolling = function (element, axis) {
+ cls.remove(element, 'ps-in-scrolling');
+ if (typeof axis !== 'undefined') {
+ cls.remove(element, 'ps-' + axis);
+ } else {
+ cls.remove(element, 'ps-x');
+ cls.remove(element, 'ps-y');
+ }
+};
+
+exports.env = {
+ isWebKit: 'WebkitAppearance' in document.documentElement.style,
+ supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch),
+ supportsIePointer: window.navigator.msMaxTouchPoints !== null
+};
+
+},{"./class":2,"./dom":3}],7:[function(require,module,exports){
+'use strict';
+
+var destroy = require('./plugin/destroy');
+var initialize = require('./plugin/initialize');
+var update = require('./plugin/update');
+
+module.exports = {
+ initialize: initialize,
+ update: update,
+ destroy: destroy
+};
+
+},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":21}],8:[function(require,module,exports){
+'use strict';
+
+module.exports = {
+ handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'],
+ maxScrollbarLength: null,
+ minScrollbarLength: null,
+ scrollXMarginOffset: 0,
+ scrollYMarginOffset: 0,
+ suppressScrollX: false,
+ suppressScrollY: false,
+ swipePropagation: true,
+ useBothWheelAxes: false,
+ wheelPropagation: false,
+ wheelSpeed: 1,
+ theme: 'default'
+};
+
+},{}],9:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var dom = require('../lib/dom');
+var instances = require('./instances');
+
+module.exports = function (element) {
+ var i = instances.get(element);
+
+ if (!i) {
+ return;
+ }
+
+ i.event.unbindAll();
+ dom.remove(i.scrollbarX);
+ dom.remove(i.scrollbarY);
+ dom.remove(i.scrollbarXRail);
+ dom.remove(i.scrollbarYRail);
+ _.removePsClasses(element);
+
+ instances.remove(element);
+};
+
+},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(require,module,exports){
+'use strict';
+
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindClickRailHandler(element, i) {
+ function pageOffset(el) {
+ return el.getBoundingClientRect();
+ }
+ var stopPropagation = function (e) { e.stopPropagation(); };
+
+ i.event.bind(i.scrollbarY, 'click', stopPropagation);
+ i.event.bind(i.scrollbarYRail, 'click', function (e) {
+ var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top;
+ var direction = positionTop > i.scrollbarYTop ? 1 : -1;
+
+ updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight);
+ updateGeometry(element);
+
+ e.stopPropagation();
+ });
+
+ i.event.bind(i.scrollbarX, 'click', stopPropagation);
+ i.event.bind(i.scrollbarXRail, 'click', function (e) {
+ var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left;
+ var direction = positionLeft > i.scrollbarXLeft ? 1 : -1;
+
+ updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth);
+ updateGeometry(element);
+
+ e.stopPropagation();
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindClickRailHandler(element, i);
+};
+
+},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var dom = require('../../lib/dom');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindMouseScrollXHandler(element, i) {
+ var currentLeft = null;
+ var currentPageX = null;
+
+ function updateScrollLeft(deltaX) {
+ var newLeft = currentLeft + (deltaX * i.railXRatio);
+ var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth));
+
+ if (newLeft < 0) {
+ i.scrollbarXLeft = 0;
+ } else if (newLeft > maxLeft) {
+ i.scrollbarXLeft = maxLeft;
+ } else {
+ i.scrollbarXLeft = newLeft;
+ }
+
+ var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment;
+ updateScroll(element, 'left', scrollLeft);
+ }
+
+ var mouseMoveHandler = function (e) {
+ updateScrollLeft(e.pageX - currentPageX);
+ updateGeometry(element);
+ e.stopPropagation();
+ e.preventDefault();
+ };
+
+ var mouseUpHandler = function () {
+ _.stopScrolling(element, 'x');
+ i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ };
+
+ i.event.bind(i.scrollbarX, 'mousedown', function (e) {
+ currentPageX = e.pageX;
+ currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio;
+ _.startScrolling(element, 'x');
+
+ i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);
+
+ e.stopPropagation();
+ e.preventDefault();
+ });
+}
+
+function bindMouseScrollYHandler(element, i) {
+ var currentTop = null;
+ var currentPageY = null;
+
+ function updateScrollTop(deltaY) {
+ var newTop = currentTop + (deltaY * i.railYRatio);
+ var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight));
+
+ if (newTop < 0) {
+ i.scrollbarYTop = 0;
+ } else if (newTop > maxTop) {
+ i.scrollbarYTop = maxTop;
+ } else {
+ i.scrollbarYTop = newTop;
+ }
+
+ var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight)));
+ updateScroll(element, 'top', scrollTop);
+ }
+
+ var mouseMoveHandler = function (e) {
+ updateScrollTop(e.pageY - currentPageY);
+ updateGeometry(element);
+ e.stopPropagation();
+ e.preventDefault();
+ };
+
+ var mouseUpHandler = function () {
+ _.stopScrolling(element, 'y');
+ i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ };
+
+ i.event.bind(i.scrollbarY, 'mousedown', function (e) {
+ currentPageY = e.pageY;
+ currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio;
+ _.startScrolling(element, 'y');
+
+ i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);
+ i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);
+
+ e.stopPropagation();
+ e.preventDefault();
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindMouseScrollXHandler(element, i);
+ bindMouseScrollYHandler(element, i);
+};
+
+},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var dom = require('../../lib/dom');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindKeyboardHandler(element, i) {
+ var hovered = false;
+ i.event.bind(element, 'mouseenter', function () {
+ hovered = true;
+ });
+ i.event.bind(element, 'mouseleave', function () {
+ hovered = false;
+ });
+
+ var shouldPrevent = false;
+ function shouldPreventDefault(deltaX, deltaY) {
+ var scrollTop = element.scrollTop;
+ if (deltaX === 0) {
+ if (!i.scrollbarYActive) {
+ return false;
+ }
+ if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+
+ var scrollLeft = element.scrollLeft;
+ if (deltaY === 0) {
+ if (!i.scrollbarXActive) {
+ return false;
+ }
+ if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+ return true;
+ }
+
+ i.event.bind(i.ownerDocument, 'keydown', function (e) {
+ if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) {
+ return;
+ }
+
+ var focused = dom.matches(i.scrollbarX, ':focus') ||
+ dom.matches(i.scrollbarY, ':focus');
+
+ if (!hovered && !focused) {
+ return;
+ }
+
+ var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement;
+ if (activeElement) {
+ if (activeElement.tagName === 'IFRAME') {
+ activeElement = activeElement.contentDocument.activeElement;
+ } else {
+ // go deeper if element is a webcomponent
+ while (activeElement.shadowRoot) {
+ activeElement = activeElement.shadowRoot.activeElement;
+ }
+ }
+ if (_.isEditable(activeElement)) {
+ return;
+ }
+ }
+
+ var deltaX = 0;
+ var deltaY = 0;
+
+ switch (e.which) {
+ case 37: // left
+ if (e.metaKey) {
+ deltaX = -i.contentWidth;
+ } else if (e.altKey) {
+ deltaX = -i.containerWidth;
+ } else {
+ deltaX = -30;
+ }
+ break;
+ case 38: // up
+ if (e.metaKey) {
+ deltaY = i.contentHeight;
+ } else if (e.altKey) {
+ deltaY = i.containerHeight;
+ } else {
+ deltaY = 30;
+ }
+ break;
+ case 39: // right
+ if (e.metaKey) {
+ deltaX = i.contentWidth;
+ } else if (e.altKey) {
+ deltaX = i.containerWidth;
+ } else {
+ deltaX = 30;
+ }
+ break;
+ case 40: // down
+ if (e.metaKey) {
+ deltaY = -i.contentHeight;
+ } else if (e.altKey) {
+ deltaY = -i.containerHeight;
+ } else {
+ deltaY = -30;
+ }
+ break;
+ case 33: // page up
+ deltaY = 90;
+ break;
+ case 32: // space bar
+ if (e.shiftKey) {
+ deltaY = 90;
+ } else {
+ deltaY = -90;
+ }
+ break;
+ case 34: // page down
+ deltaY = -90;
+ break;
+ case 35: // end
+ if (e.ctrlKey) {
+ deltaY = -i.contentHeight;
+ } else {
+ deltaY = -i.containerHeight;
+ }
+ break;
+ case 36: // home
+ if (e.ctrlKey) {
+ deltaY = element.scrollTop;
+ } else {
+ deltaY = i.containerHeight;
+ }
+ break;
+ default:
+ return;
+ }
+
+ updateScroll(element, 'top', element.scrollTop - deltaY);
+ updateScroll(element, 'left', element.scrollLeft + deltaX);
+ updateGeometry(element);
+
+ shouldPrevent = shouldPreventDefault(deltaX, deltaY);
+ if (shouldPrevent) {
+ e.preventDefault();
+ }
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindKeyboardHandler(element, i);
+};
+
+},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(require,module,exports){
+'use strict';
+
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindMouseWheelHandler(element, i) {
+ var shouldPrevent = false;
+
+ function shouldPreventDefault(deltaX, deltaY) {
+ var scrollTop = element.scrollTop;
+ if (deltaX === 0) {
+ if (!i.scrollbarYActive) {
+ return false;
+ }
+ if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+
+ var scrollLeft = element.scrollLeft;
+ if (deltaY === 0) {
+ if (!i.scrollbarXActive) {
+ return false;
+ }
+ if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) {
+ return !i.settings.wheelPropagation;
+ }
+ }
+ return true;
+ }
+
+ function getDeltaFromEvent(e) {
+ var deltaX = e.deltaX;
+ var deltaY = -1 * e.deltaY;
+
+ if (typeof deltaX === "undefined" || typeof deltaY === "undefined") {
+ // OS X Safari
+ deltaX = -1 * e.wheelDeltaX / 6;
+ deltaY = e.wheelDeltaY / 6;
+ }
+
+ if (e.deltaMode && e.deltaMode === 1) {
+ // Firefox in deltaMode 1: Line scrolling
+ deltaX *= 10;
+ deltaY *= 10;
+ }
+
+ if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) {
+ // IE in some mouse drivers
+ deltaX = 0;
+ deltaY = e.wheelDelta;
+ }
+
+ if (e.shiftKey) {
+ // reverse axis with shift key
+ return [-deltaY, -deltaX];
+ }
+ return [deltaX, deltaY];
+ }
+
+ function shouldBeConsumedByChild(deltaX, deltaY) {
+ var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover');
+ if (child) {
+ if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) {
+ // if not scrollable
+ return false;
+ }
+
+ var maxScrollTop = child.scrollHeight - child.clientHeight;
+ if (maxScrollTop > 0) {
+ if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) {
+ return true;
+ }
+ }
+ var maxScrollLeft = child.scrollLeft - child.clientWidth;
+ if (maxScrollLeft > 0) {
+ if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ function mousewheelHandler(e) {
+ var delta = getDeltaFromEvent(e);
+
+ var deltaX = delta[0];
+ var deltaY = delta[1];
+
+ if (shouldBeConsumedByChild(deltaX, deltaY)) {
+ return;
+ }
+
+ shouldPrevent = false;
+ if (!i.settings.useBothWheelAxes) {
+ // deltaX will only be used for horizontal scrolling and deltaY will
+ // only be used for vertical scrolling - this is the default
+ updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed));
+ updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed));
+ } else if (i.scrollbarYActive && !i.scrollbarXActive) {
+ // only vertical scrollbar is active and useBothWheelAxes option is
+ // active, so let's scroll vertical bar using both mouse wheel axes
+ if (deltaY) {
+ updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed));
+ } else {
+ updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed));
+ }
+ shouldPrevent = true;
+ } else if (i.scrollbarXActive && !i.scrollbarYActive) {
+ // useBothWheelAxes and only horizontal bar is active, so use both
+ // wheel axes for horizontal bar
+ if (deltaX) {
+ updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed));
+ } else {
+ updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed));
+ }
+ shouldPrevent = true;
+ }
+
+ updateGeometry(element);
+
+ shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY));
+ if (shouldPrevent) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ if (typeof window.onwheel !== "undefined") {
+ i.event.bind(element, 'wheel', mousewheelHandler);
+ } else if (typeof window.onmousewheel !== "undefined") {
+ i.event.bind(element, 'mousewheel', mousewheelHandler);
+ }
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindMouseWheelHandler(element, i);
+};
+
+},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(require,module,exports){
+'use strict';
+
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+
+function bindNativeScrollHandler(element, i) {
+ i.event.bind(element, 'scroll', function () {
+ updateGeometry(element);
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindNativeScrollHandler(element, i);
+};
+
+},{"../instances":18,"../update-geometry":19}],15:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindSelectionHandler(element, i) {
+ function getRangeNode() {
+ var selection = window.getSelection ? window.getSelection() :
+ document.getSelection ? document.getSelection() : '';
+ if (selection.toString().length === 0) {
+ return null;
+ } else {
+ return selection.getRangeAt(0).commonAncestorContainer;
+ }
+ }
+
+ var scrollingLoop = null;
+ var scrollDiff = {top: 0, left: 0};
+ function startScrolling() {
+ if (!scrollingLoop) {
+ scrollingLoop = setInterval(function () {
+ if (!instances.get(element)) {
+ clearInterval(scrollingLoop);
+ return;
+ }
+
+ updateScroll(element, 'top', element.scrollTop + scrollDiff.top);
+ updateScroll(element, 'left', element.scrollLeft + scrollDiff.left);
+ updateGeometry(element);
+ }, 50); // every .1 sec
+ }
+ }
+ function stopScrolling() {
+ if (scrollingLoop) {
+ clearInterval(scrollingLoop);
+ scrollingLoop = null;
+ }
+ _.stopScrolling(element);
+ }
+
+ var isSelected = false;
+ i.event.bind(i.ownerDocument, 'selectionchange', function () {
+ if (element.contains(getRangeNode())) {
+ isSelected = true;
+ } else {
+ isSelected = false;
+ stopScrolling();
+ }
+ });
+ i.event.bind(window, 'mouseup', function () {
+ if (isSelected) {
+ isSelected = false;
+ stopScrolling();
+ }
+ });
+ i.event.bind(window, 'keyup', function () {
+ if (isSelected) {
+ isSelected = false;
+ stopScrolling();
+ }
+ });
+
+ i.event.bind(window, 'mousemove', function (e) {
+ if (isSelected) {
+ var mousePosition = {x: e.pageX, y: e.pageY};
+ var containerGeometry = {
+ left: element.offsetLeft,
+ right: element.offsetLeft + element.offsetWidth,
+ top: element.offsetTop,
+ bottom: element.offsetTop + element.offsetHeight
+ };
+
+ if (mousePosition.x < containerGeometry.left + 3) {
+ scrollDiff.left = -5;
+ _.startScrolling(element, 'x');
+ } else if (mousePosition.x > containerGeometry.right - 3) {
+ scrollDiff.left = 5;
+ _.startScrolling(element, 'x');
+ } else {
+ scrollDiff.left = 0;
+ }
+
+ if (mousePosition.y < containerGeometry.top + 3) {
+ if (containerGeometry.top + 3 - mousePosition.y < 5) {
+ scrollDiff.top = -5;
+ } else {
+ scrollDiff.top = -20;
+ }
+ _.startScrolling(element, 'y');
+ } else if (mousePosition.y > containerGeometry.bottom - 3) {
+ if (mousePosition.y - containerGeometry.bottom + 3 < 5) {
+ scrollDiff.top = 5;
+ } else {
+ scrollDiff.top = 20;
+ }
+ _.startScrolling(element, 'y');
+ } else {
+ scrollDiff.top = 0;
+ }
+
+ if (scrollDiff.top === 0 && scrollDiff.left === 0) {
+ stopScrolling();
+ } else {
+ startScrolling();
+ }
+ }
+ });
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+ bindSelectionHandler(element, i);
+};
+
+},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(require,module,exports){
+'use strict';
+
+var _ = require('../../lib/helper');
+var instances = require('../instances');
+var updateGeometry = require('../update-geometry');
+var updateScroll = require('../update-scroll');
+
+function bindTouchHandler(element, i, supportsTouch, supportsIePointer) {
+ function shouldPreventDefault(deltaX, deltaY) {
+ var scrollTop = element.scrollTop;
+ var scrollLeft = element.scrollLeft;
+ var magnitudeX = Math.abs(deltaX);
+ var magnitudeY = Math.abs(deltaY);
+
+ if (magnitudeY > magnitudeX) {
+ // user is perhaps trying to swipe up/down the page
+
+ if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) ||
+ ((deltaY > 0) && (scrollTop === 0))) {
+ return !i.settings.swipePropagation;
+ }
+ } else if (magnitudeX > magnitudeY) {
+ // user is perhaps trying to swipe left/right across the page
+
+ if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) ||
+ ((deltaX > 0) && (scrollLeft === 0))) {
+ return !i.settings.swipePropagation;
+ }
+ }
+
+ return true;
+ }
+
+ function applyTouchMove(differenceX, differenceY) {
+ updateScroll(element, 'top', element.scrollTop - differenceY);
+ updateScroll(element, 'left', element.scrollLeft - differenceX);
+
+ updateGeometry(element);
+ }
+
+ var startOffset = {};
+ var startTime = 0;
+ var speed = {};
+ var easingLoop = null;
+ var inGlobalTouch = false;
+ var inLocalTouch = false;
+
+ function globalTouchStart() {
+ inGlobalTouch = true;
+ }
+ function globalTouchEnd() {
+ inGlobalTouch = false;
+ }
+
+ function getTouch(e) {
+ if (e.targetTouches) {
+ return e.targetTouches[0];
+ } else {
+ // Maybe IE pointer
+ return e;
+ }
+ }
+ function shouldHandle(e) {
+ if (e.targetTouches && e.targetTouches.length === 1) {
+ return true;
+ }
+ if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
+ return true;
+ }
+ return false;
+ }
+ function touchStart(e) {
+ if (shouldHandle(e)) {
+ inLocalTouch = true;
+
+ var touch = getTouch(e);
+
+ startOffset.pageX = touch.pageX;
+ startOffset.pageY = touch.pageY;
+
+ startTime = (new Date()).getTime();
+
+ if (easingLoop !== null) {
+ clearInterval(easingLoop);
+ }
+
+ e.stopPropagation();
+ }
+ }
+ function touchMove(e) {
+ if (!inLocalTouch && i.settings.swipePropagation) {
+ touchStart(e);
+ }
+ if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) {
+ var touch = getTouch(e);
+
+ var currentOffset = {pageX: touch.pageX, pageY: touch.pageY};
+
+ var differenceX = currentOffset.pageX - startOffset.pageX;
+ var differenceY = currentOffset.pageY - startOffset.pageY;
+
+ applyTouchMove(differenceX, differenceY);
+ startOffset = currentOffset;
+
+ var currentTime = (new Date()).getTime();
+
+ var timeGap = currentTime - startTime;
+ if (timeGap > 0) {
+ speed.x = differenceX / timeGap;
+ speed.y = differenceY / timeGap;
+ startTime = currentTime;
+ }
+
+ if (shouldPreventDefault(differenceX, differenceY)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+ }
+ function touchEnd() {
+ if (!inGlobalTouch && inLocalTouch) {
+ inLocalTouch = false;
+
+ clearInterval(easingLoop);
+ easingLoop = setInterval(function () {
+ if (!instances.get(element)) {
+ clearInterval(easingLoop);
+ return;
+ }
+
+ if (!speed.x && !speed.y) {
+ clearInterval(easingLoop);
+ return;
+ }
+
+ if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {
+ clearInterval(easingLoop);
+ return;
+ }
+
+ applyTouchMove(speed.x * 30, speed.y * 30);
+
+ speed.x *= 0.8;
+ speed.y *= 0.8;
+ }, 10);
+ }
+ }
+
+ if (supportsTouch) {
+ i.event.bind(window, 'touchstart', globalTouchStart);
+ i.event.bind(window, 'touchend', globalTouchEnd);
+ i.event.bind(element, 'touchstart', touchStart);
+ i.event.bind(element, 'touchmove', touchMove);
+ i.event.bind(element, 'touchend', touchEnd);
+ } else if (supportsIePointer) {
+ if (window.PointerEvent) {
+ i.event.bind(window, 'pointerdown', globalTouchStart);
+ i.event.bind(window, 'pointerup', globalTouchEnd);
+ i.event.bind(element, 'pointerdown', touchStart);
+ i.event.bind(element, 'pointermove', touchMove);
+ i.event.bind(element, 'pointerup', touchEnd);
+ } else if (window.MSPointerEvent) {
+ i.event.bind(window, 'MSPointerDown', globalTouchStart);
+ i.event.bind(window, 'MSPointerUp', globalTouchEnd);
+ i.event.bind(element, 'MSPointerDown', touchStart);
+ i.event.bind(element, 'MSPointerMove', touchMove);
+ i.event.bind(element, 'MSPointerUp', touchEnd);
+ }
+ }
+}
+
+module.exports = function (element) {
+ if (!_.env.supportsTouch && !_.env.supportsIePointer) {
+ return;
+ }
+
+ var i = instances.get(element);
+ bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer);
+};
+
+},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var cls = require('../lib/class');
+var instances = require('./instances');
+var updateGeometry = require('./update-geometry');
+
+// Handlers
+var handlers = {
+ 'click-rail': require('./handler/click-rail'),
+ 'drag-scrollbar': require('./handler/drag-scrollbar'),
+ 'keyboard': require('./handler/keyboard'),
+ 'wheel': require('./handler/mouse-wheel'),
+ 'touch': require('./handler/touch'),
+ 'selection': require('./handler/selection')
+};
+var nativeScrollHandler = require('./handler/native-scroll');
+
+module.exports = function (element, userSettings) {
+ userSettings = typeof userSettings === 'object' ? userSettings : {};
+
+ cls.add(element, 'ps-container');
+
+ // Create a plugin instance.
+ var i = instances.add(element);
+
+ i.settings = _.extend(i.settings, userSettings);
+ cls.add(element, 'ps-theme-' + i.settings.theme);
+
+ i.settings.handlers.forEach(function (handlerName) {
+ handlers[handlerName](element);
+ });
+
+ nativeScrollHandler(element);
+
+ updateGeometry(element);
+};
+
+},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var cls = require('../lib/class');
+var defaultSettings = require('./default-setting');
+var dom = require('../lib/dom');
+var EventManager = require('../lib/event-manager');
+var guid = require('../lib/guid');
+
+var instances = {};
+
+function Instance(element) {
+ var i = this;
+
+ i.settings = _.clone(defaultSettings);
+ i.containerWidth = null;
+ i.containerHeight = null;
+ i.contentWidth = null;
+ i.contentHeight = null;
+
+ i.isRtl = dom.css(element, 'direction') === "rtl";
+ i.isNegativeScroll = (function () {
+ var originalScrollLeft = element.scrollLeft;
+ var result = null;
+ element.scrollLeft = -1;
+ result = element.scrollLeft < 0;
+ element.scrollLeft = originalScrollLeft;
+ return result;
+ })();
+ i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0;
+ i.event = new EventManager();
+ i.ownerDocument = element.ownerDocument || document;
+
+ function focus() {
+ cls.add(element, 'ps-focus');
+ }
+
+ function blur() {
+ cls.remove(element, 'ps-focus');
+ }
+
+ i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element);
+ i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail);
+ i.scrollbarX.setAttribute('tabindex', 0);
+ i.event.bind(i.scrollbarX, 'focus', focus);
+ i.event.bind(i.scrollbarX, 'blur', blur);
+ i.scrollbarXActive = null;
+ i.scrollbarXWidth = null;
+ i.scrollbarXLeft = null;
+ i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom'));
+ i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN
+ i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top'));
+ i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth'));
+ // Set rail to display:block to calculate margins
+ dom.css(i.scrollbarXRail, 'display', 'block');
+ i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight'));
+ dom.css(i.scrollbarXRail, 'display', '');
+ i.railXWidth = null;
+ i.railXRatio = null;
+
+ i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element);
+ i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail);
+ i.scrollbarY.setAttribute('tabindex', 0);
+ i.event.bind(i.scrollbarY, 'focus', focus);
+ i.event.bind(i.scrollbarY, 'blur', blur);
+ i.scrollbarYActive = null;
+ i.scrollbarYHeight = null;
+ i.scrollbarYTop = null;
+ i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right'));
+ i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN
+ i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left'));
+ i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null;
+ i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth'));
+ dom.css(i.scrollbarYRail, 'display', 'block');
+ i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom'));
+ dom.css(i.scrollbarYRail, 'display', '');
+ i.railYHeight = null;
+ i.railYRatio = null;
+}
+
+function getId(element) {
+ return element.getAttribute('data-ps-id');
+}
+
+function setId(element, id) {
+ element.setAttribute('data-ps-id', id);
+}
+
+function removeId(element) {
+ element.removeAttribute('data-ps-id');
+}
+
+exports.add = function (element) {
+ var newId = guid();
+ setId(element, newId);
+ instances[newId] = new Instance(element);
+ return instances[newId];
+};
+
+exports.remove = function (element) {
+ delete instances[getId(element)];
+ removeId(element);
+};
+
+exports.get = function (element) {
+ return instances[getId(element)];
+};
+
+},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var cls = require('../lib/class');
+var dom = require('../lib/dom');
+var instances = require('./instances');
+var updateScroll = require('./update-scroll');
+
+function getThumbSize(i, thumbSize) {
+ if (i.settings.minScrollbarLength) {
+ thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength);
+ }
+ if (i.settings.maxScrollbarLength) {
+ thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength);
+ }
+ return thumbSize;
+}
+
+function updateCss(element, i) {
+ var xRailOffset = {width: i.railXWidth};
+ if (i.isRtl) {
+ xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth;
+ } else {
+ xRailOffset.left = element.scrollLeft;
+ }
+ if (i.isScrollbarXUsingBottom) {
+ xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop;
+ } else {
+ xRailOffset.top = i.scrollbarXTop + element.scrollTop;
+ }
+ dom.css(i.scrollbarXRail, xRailOffset);
+
+ var yRailOffset = {top: element.scrollTop, height: i.railYHeight};
+ if (i.isScrollbarYUsingRight) {
+ if (i.isRtl) {
+ yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth;
+ } else {
+ yRailOffset.right = i.scrollbarYRight - element.scrollLeft;
+ }
+ } else {
+ if (i.isRtl) {
+ yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth;
+ } else {
+ yRailOffset.left = i.scrollbarYLeft + element.scrollLeft;
+ }
+ }
+ dom.css(i.scrollbarYRail, yRailOffset);
+
+ dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth});
+ dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth});
+}
+
+module.exports = function (element) {
+ var i = instances.get(element);
+
+ i.containerWidth = element.clientWidth;
+ i.containerHeight = element.clientHeight;
+ i.contentWidth = element.scrollWidth;
+ i.contentHeight = element.scrollHeight;
+
+ var existingRails;
+ if (!element.contains(i.scrollbarXRail)) {
+ existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail');
+ if (existingRails.length > 0) {
+ existingRails.forEach(function (rail) {
+ dom.remove(rail);
+ });
+ }
+ dom.appendTo(i.scrollbarXRail, element);
+ }
+ if (!element.contains(i.scrollbarYRail)) {
+ existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail');
+ if (existingRails.length > 0) {
+ existingRails.forEach(function (rail) {
+ dom.remove(rail);
+ });
+ }
+ dom.appendTo(i.scrollbarYRail, element);
+ }
+
+ if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) {
+ i.scrollbarXActive = true;
+ i.railXWidth = i.containerWidth - i.railXMarginWidth;
+ i.railXRatio = i.containerWidth / i.railXWidth;
+ i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth));
+ i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth));
+ } else {
+ i.scrollbarXActive = false;
+ }
+
+ if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) {
+ i.scrollbarYActive = true;
+ i.railYHeight = i.containerHeight - i.railYMarginHeight;
+ i.railYRatio = i.containerHeight / i.railYHeight;
+ i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight));
+ i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight));
+ } else {
+ i.scrollbarYActive = false;
+ }
+
+ if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) {
+ i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth;
+ }
+ if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) {
+ i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight;
+ }
+
+ updateCss(element, i);
+
+ if (i.scrollbarXActive) {
+ cls.add(element, 'ps-active-x');
+ } else {
+ cls.remove(element, 'ps-active-x');
+ i.scrollbarXWidth = 0;
+ i.scrollbarXLeft = 0;
+ updateScroll(element, 'left', 0);
+ }
+ if (i.scrollbarYActive) {
+ cls.add(element, 'ps-active-y');
+ } else {
+ cls.remove(element, 'ps-active-y');
+ i.scrollbarYHeight = 0;
+ i.scrollbarYTop = 0;
+ updateScroll(element, 'top', 0);
+ }
+};
+
+},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(require,module,exports){
+'use strict';
+
+var instances = require('./instances');
+
+var lastTop;
+var lastLeft;
+
+var createDOMEvent = function (name) {
+ var event = document.createEvent("Event");
+ event.initEvent(name, true, true);
+ return event;
+};
+
+module.exports = function (element, axis, value) {
+ if (typeof element === 'undefined') {
+ throw 'You must provide an element to the update-scroll function';
+ }
+
+ if (typeof axis === 'undefined') {
+ throw 'You must provide an axis to the update-scroll function';
+ }
+
+ if (typeof value === 'undefined') {
+ throw 'You must provide a value to the update-scroll function';
+ }
+
+ if (axis === 'top' && value <= 0) {
+ element.scrollTop = value = 0; // don't allow negative scroll
+ element.dispatchEvent(createDOMEvent('ps-y-reach-start'));
+ }
+
+ if (axis === 'left' && value <= 0) {
+ element.scrollLeft = value = 0; // don't allow negative scroll
+ element.dispatchEvent(createDOMEvent('ps-x-reach-start'));
+ }
+
+ var i = instances.get(element);
+
+ if (axis === 'top' && value >= i.contentHeight - i.containerHeight) {
+ // don't allow scroll past container
+ value = i.contentHeight - i.containerHeight;
+ if (value - element.scrollTop <= 1) {
+ // mitigates rounding errors on non-subpixel scroll values
+ value = element.scrollTop;
+ } else {
+ element.scrollTop = value;
+ }
+ element.dispatchEvent(createDOMEvent('ps-y-reach-end'));
+ }
+
+ if (axis === 'left' && value >= i.contentWidth - i.containerWidth) {
+ // don't allow scroll past container
+ value = i.contentWidth - i.containerWidth;
+ if (value - element.scrollLeft <= 1) {
+ // mitigates rounding errors on non-subpixel scroll values
+ value = element.scrollLeft;
+ } else {
+ element.scrollLeft = value;
+ }
+ element.dispatchEvent(createDOMEvent('ps-x-reach-end'));
+ }
+
+ if (!lastTop) {
+ lastTop = element.scrollTop;
+ }
+
+ if (!lastLeft) {
+ lastLeft = element.scrollLeft;
+ }
+
+ if (axis === 'top' && value < lastTop) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-up'));
+ }
+
+ if (axis === 'top' && value > lastTop) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-down'));
+ }
+
+ if (axis === 'left' && value < lastLeft) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-left'));
+ }
+
+ if (axis === 'left' && value > lastLeft) {
+ element.dispatchEvent(createDOMEvent('ps-scroll-right'));
+ }
+
+ if (axis === 'top') {
+ element.scrollTop = lastTop = value;
+ element.dispatchEvent(createDOMEvent('ps-scroll-y'));
+ }
+
+ if (axis === 'left') {
+ element.scrollLeft = lastLeft = value;
+ element.dispatchEvent(createDOMEvent('ps-scroll-x'));
+ }
+
+};
+
+},{"./instances":18}],21:[function(require,module,exports){
+'use strict';
+
+var _ = require('../lib/helper');
+var dom = require('../lib/dom');
+var instances = require('./instances');
+var updateGeometry = require('./update-geometry');
+var updateScroll = require('./update-scroll');
+
+module.exports = function (element) {
+ var i = instances.get(element);
+
+ if (!i) {
+ return;
+ }
+
+ // Recalcuate negative scrollLeft adjustment
+ i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0;
+
+ // Recalculate rail margins
+ dom.css(i.scrollbarXRail, 'display', 'block');
+ dom.css(i.scrollbarYRail, 'display', 'block');
+ i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight'));
+ i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom'));
+
+ // Hide scrollbars not to affect scrollWidth and scrollHeight
+ dom.css(i.scrollbarXRail, 'display', 'none');
+ dom.css(i.scrollbarYRail, 'display', 'none');
+
+ updateGeometry(element);
+
+ // Update top/left scroll to trigger events
+ updateScroll(element, 'top', element.scrollTop);
+ updateScroll(element, 'left', element.scrollLeft);
+
+ dom.css(i.scrollbarXRail, 'display', '');
+ dom.css(i.scrollbarYRail, 'display', '');
+};
+
+},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]);
+
+/**
+ * Provides utility functions for date operations.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Date/Util
+ */
+define('WoltLabSuite/Core/Date/Util',['Language'], function(Language) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Date/Util
+ */
+ var DateUtil = {
+ /**
+ * Returns the formatted date.
+ *
+ * @param {Date} date date object
+ * @returns {string} formatted date
+ */
+ formatDate: function(date) {
+ return this.format(date, Language.get('wcf.date.dateFormat'));
+ },
+
+ /**
+ * Returns the formatted time.
+ *
+ * @param {Date} date date object
+ * @returns {string} formatted time
+ */
+ formatTime: function(date) {
+ return this.format(date, Language.get('wcf.date.timeFormat'));
+ },
+
+ /**
+ * Returns the formatted date time.
+ *
+ * @param {Date} date date object
+ * @returns {string} formatted date time
+ */
+ formatDateTime: function(date) {
+ return this.format(date, Language.get('wcf.date.dateTimeFormat').replace(/%date%/, Language.get('wcf.date.dateFormat')).replace(/%time%/, Language.get('wcf.date.timeFormat')));
+ },
+
+ /**
+ * Formats a date using PHP's `date()` modifiers.
+ *
+ * @param {Date} date date object
+ * @param {string} format output format
+ * @returns {string} formatted date
+ */
+ format: function(date, format) {
+ var char;
+ var out = '';
+
+ // ISO 8601 date, best recognition by PHP's strtotime()
+ if (format === 'c') {
+ format = 'Y-m-dTH:i:sP';
+ }
+
+ for (var i = 0, length = format.length; i < length; i++) {
+ switch (format[i]) {
+ // seconds
+ case 's':
+ // `00` through `59`
+ char = ('0' + date.getSeconds().toString()).slice(-2);
+ break;
+
+ // minutes
+ case 'i':
+ // `00` through `59`
+ char = date.getMinutes();
+ if (char < 10) char = "0" + char;
+ break;
+
+ // hours
+ case 'a':
+ // `am` or `pm`
+ char = (date.getHours() > 11) ? 'pm' : 'am';
+ break;
+ case 'g':
+ // `1` through `12`
+ char = date.getHours();
+ if (char === 0) char = 12;
+ else if (char > 12) char -= 12;
+ break;
+ case 'h':
+ // `01` through `12`
+ char = date.getHours();
+ if (char === 0) char = 12;
+ else if (char > 12) char -= 12;
+
+ char = ('0' + char.toString()).slice(-2);
+ break;
+ case 'A':
+ // `AM` or `PM`
+ char = (date.getHours() > 11) ? 'PM' : 'AM';
+ break;
+ case 'G':
+ // `0` through `23`
+ char = date.getHours();
+ break;
+ case 'H':
+ // `00` through `23`
+ char = date.getHours();
+ char = ('0' + char.toString()).slice(-2);
+ break;
+
+ // day
+ case 'd':
+ // `01` through `31`
+ char = date.getDate();
+ char = ('0' + char.toString()).slice(-2);
+ break;
+ case 'j':
+ // `1` through `31`
+ char = date.getDate();
+ break;
+ case 'l':
+ // `Monday` through `Sunday` (localized)
+ char = Language.get('__days')[date.getDay()];
+ break;
+ case 'D':
+ // `Mon` through `Sun` (localized)
+ char = Language.get('__daysShort')[date.getDay()];
+ break;
+ case 'S':
+ // ignore english ordinal suffix
+ char = '';
+ break;
+
+ // month
+ case 'm':
+ // `01` through `12`
+ char = date.getMonth() + 1;
+ char = ('0' + char.toString()).slice(-2);
+ break;
+ case 'n':
+ // `1` through `12`
+ char = date.getMonth() + 1;
+ break;
+ case 'F':
+ // `January` through `December` (localized)
+ char = Language.get('__months')[date.getMonth()];
+ break;
+ case 'M':
+ // `Jan` through `Dec` (localized)
+ char = Language.get('__monthsShort')[date.getMonth()];
+ break;
+
+ // year
+ case 'y':
+ // `00` through `99`
+ char = date.getYear().toString().replace(/^\d{2}/, '');
+ break;
+ case 'Y':
+ // Examples: `1988` or `2015`
+ char = date.getFullYear();
+ break;
+
+ // timezone
+ case 'P':
+ var offset = date.getTimezoneOffset();
+ char = (offset > 0) ? '-' : '+';
+
+ offset = Math.abs(offset);
+
+ char += ('0' + (~~(offset / 60)).toString()).slice(-2);
+ char += ':';
+ char += ('0' + (offset % 60).toString()).slice(-2);
+
+ break;
+
+ // specials
+ case 'r':
+ char = date.toString();
+ break;
+ case 'U':
+ char = Math.round(date.getTime() / 1000);
+ break;
+
+ // escape sequence
+ case '\\':
+ char = '';
+ if (i + 1 < length) {
+ char = format[++i];
+ }
+ break;
+
+ default:
+ char = format[i];
+ break;
+ }
+
+ out += char;
+ }
+
+ return out;
+ },
+
+ /**
+ * Returns UTC timestamp, if date is not given, current time will be used.
+ *
+ * @param {Date} date target date
+ * @return {int} UTC timestamp in seconds
+ */
+ gmdate: function(date) {
+ if (!(date instanceof Date)) {
+ date = new Date();
+ }
+
+ return Math.round(Date.UTC(
+ date.getUTCFullYear(),
+ date.getUTCMonth(),
+ date.getUTCDay(),
+ date.getUTCHours(),
+ date.getUTCMinutes(),
+ date.getUTCSeconds()
+ ) / 1000);
+ },
+
+ /**
+ * Returns a `time` element based on the given date just like a `time`
+ * element created by `wcf\system\template\plugin\TimeModifierTemplatePlugin`.
+ *
+ * Note: The actual content of the element is empty and is expected
+ * to be automatically updated by `WoltLabSuite/Core/Date/Time/Relative`
+ * (for dates not in the future) after the DOM change listener has been triggered.
+ *
+ * @param {Date} date displayed date
+ * @return {HTMLElement} `time` element
+ */
+ getTimeElement: function(date) {
+ var time = elCreate('time');
+ time.className = 'datetime';
+
+ var formattedDate = this.formatDate(date);
+ var formattedTime = this.formatTime(date);
+
+ elAttr(time, 'datetime', this.format(date, 'c'));
+ elData(time, 'timestamp', (date.getTime() - date.getMilliseconds()) / 1000);
+ elData(time, 'date', formattedDate);
+ elData(time, 'time', formattedTime);
+ elData(time, 'offset', date.getTimezoneOffset() * 60); // PHP returns minutes, JavaScript returns seconds
+
+ if (date.getTime() > Date.now()) {
+ elData(time, 'is-future-date', 'true');
+
+ time.textContent = Language.get('wcf.date.dateTimeFormat').replace('%time%', formattedTime).replace('%date%', formattedDate);
+ }
+
+ return time;
+ },
+
+ /**
+ * Returns a Date object with precise offset (including timezone and local timezone).
+ *
+ * @param {int} timestamp timestamp in milliseconds
+ * @param {int} offset timezone offset in milliseconds
+ * @return {Date} localized date
+ */
+ getTimezoneDate: function(timestamp, offset) {
+ var date = new Date(timestamp);
+ var localOffset = date.getTimezoneOffset() * 60000;
+
+ return new Date((timestamp + localOffset + offset));
+ }
+ };
+
+ return DateUtil;
+});
+
+/**
+ * Provides an object oriented API on top of `setInterval`.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Timer/Repeating
+ */
+define('WoltLabSuite/Core/Timer/Repeating',[], function() {
+ "use strict";
+
+ /**
+ * Creates a new timer that executes the given `callback` every `delta` milliseconds.
+ * It will be created in started mode. Call `stop()` if necessary.
+ * The `callback` will be passed the owning instance of `Repeating`.
+ *
+ * @constructor
+ * @param {function(Repeating)} callback
+ * @param {int} delta
+ */
+ function Repeating(callback, delta) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback as first argument.");
+ }
+ if (delta < 0 || delta > 86400 * 1000) {
+ throw new RangeError("Invalid delta " + delta + ". Delta must be in the interval [0, 86400000].");
+ }
+
+ // curry callback with `this` as the first parameter
+ this._callback = callback.bind(undefined, this);
+
+ this._delta = delta;
+ this._timer = undefined;
+
+ this.restart();
+ }
+ Repeating.prototype = {
+ /**
+ * Stops the timer and restarts it. The next call will occur in `delta` milliseconds.
+ */
+ restart: function() {
+ this.stop();
+
+ this._timer = setInterval(this._callback, this._delta);
+ },
+
+ /**
+ * Stops the timer. It will no longer be called until you call `restart`.
+ */
+ stop: function() {
+ if (this._timer !== undefined) {
+ clearInterval(this._timer);
+ this._timer = undefined;
+ }
+ },
+
+ /**
+ * Changes the `delta` of the timer and `restart`s it.
+ *
+ * @param {int} delta New delta of the timer.
+ */
+ setDelta: function(delta) {
+ this._delta = delta;
+
+ this.restart();
+ }
+ };
+
+ return Repeating;
+});
+
+/**
+ * Transforms <time> elements to display the elapsed time relative to the current time.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Date/Time/Relative
+ */
+define('WoltLabSuite/Core/Date/Time/Relative',['Dom/ChangeListener', 'Language', 'WoltLabSuite/Core/Date/Util', 'WoltLabSuite/Core/Timer/Repeating'], function(DomChangeListener, Language, DateUtil, Repeating) {
+ "use strict";
+
+ var _elements = elByTag('time');
+ var _isActive = true;
+ var _isPending = false;
+ var _offset = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Date/Time/Relative
+ */
+ return {
+ /**
+ * Transforms <time> elements on init and binds event listeners.
+ */
+ setup: function() {
+ new Repeating(this._refresh.bind(this), 60000);
+
+ DomChangeListener.add('WoltLabSuite/Core/Date/Time/Relative', this._refresh.bind(this));
+
+ document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
+ },
+
+ _onVisibilityChange: function () {
+ if (document.hidden) {
+ _isActive = false;
+ _isPending = false;
+ }
+ else {
+ _isActive = true;
+
+ // force immediate refresh
+ if (_isPending) {
+ this._refresh();
+ _isPending = false;
+ }
+ }
+ },
+
+ _refresh: function() {
+ // activity is suspended while the tab is hidden, but force an
+ // immediate refresh once the page is active again
+ if (!_isActive) {
+ if (!_isPending) _isPending = true;
+ return;
+ }
+
+ var date = new Date();
+ var timestamp = (date.getTime() - date.getMilliseconds()) / 1000;
+ if (_offset === null) _offset = timestamp - window.TIME_NOW;
+
+ for (var i = 0, length = _elements.length; i < length; i++) {
+ var element = _elements[i];
+
+ if (!element.classList.contains('datetime') || elData(element, 'is-future-date')) continue;
+
+ var elTimestamp = ~~elData(element, 'timestamp') + _offset;
+ var elDate = elData(element, 'date');
+ var elTime = elData(element, 'time');
+ var elOffset = elData(element, 'offset');
+
+ if (!elAttr(element, 'title')) {
+ elAttr(element, 'title', Language.get('wcf.date.dateTimeFormat').replace(/%date%/, elDate).replace(/%time%/, elTime));
+ }
+
+ // timestamp is less than 60 seconds ago
+ if (elTimestamp >= timestamp || timestamp < (elTimestamp + 60)) {
+ element.textContent = Language.get('wcf.date.relative.now');
+ }
+ // timestamp is less than 60 minutes ago (display 1 hour ago rather than 60 minutes ago)
+ else if (timestamp < (elTimestamp + 3540)) {
+ var minutes = Math.max(Math.round((timestamp - elTimestamp) / 60), 1);
+ element.textContent = Language.get('wcf.date.relative.minutes', { minutes: minutes });
+ }
+ // timestamp is less than 24 hours ago
+ else if (timestamp < (elTimestamp + 86400)) {
+ var hours = Math.round((timestamp - elTimestamp) / 3600);
+ element.textContent = Language.get('wcf.date.relative.hours', { hours: hours });
+ }
+ // timestamp is less than 6 days ago
+ else if (timestamp < (elTimestamp + 518400)) {
+ var midnight = new Date(date.getFullYear(), date.getMonth(), date.getDate());
+ var days = Math.ceil((midnight / 1000 - elTimestamp) / 86400);
+
+ // get day of week
+ var dateObj = DateUtil.getTimezoneDate((elTimestamp * 1000), elOffset * 1000);
+ var dow = dateObj.getDay();
+ var day = Language.get('__days')[dow];
+
+ element.textContent = Language.get('wcf.date.relative.pastDays', { days: days, day: day, time: elTime });
+ }
+ // timestamp is between ~700 million years BC and last week
+ else {
+ element.textContent = Language.get('wcf.date.shortDateTimeFormat').replace(/%date%/, elDate).replace(/%time%/, elTime);
+ }
+ }
+ }
+ };
+});
+
+/**
+ * Provides a touch-friendly fullscreen menu.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/Abstract
+ */
+define('WoltLabSuite/Core/Ui/Page/Menu/Abstract',['Core', 'Environment', 'EventHandler', 'Language', 'ObjectMap', 'Dom/Traverse', 'Dom/Util', 'Ui/Screen'], function(Core, Environment, EventHandler, Language, ObjectMap, DomTraverse, DomUtil, UiScreen) {
+ "use strict";
+
+ var _pageContainer = elById('pageContainer');
+
+ /**
+ * Which edge of the menu is touched? Empty string
+ * if no menu is currently touched.
+ *
+ * One 'left', 'right' or ''.
+ */
+ var _androidTouching = '';
+
+ /**
+ * @param {string} eventIdentifier event namespace
+ * @param {string} elementId menu element id
+ * @param {string} buttonSelector CSS selector for toggle button
+ * @constructor
+ */
+ function UiPageMenuAbstract(eventIdentifier, elementId, buttonSelector) { this.init(eventIdentifier, elementId, buttonSelector); }
+ UiPageMenuAbstract.prototype = {
+ /**
+ * Initializes a touch-friendly fullscreen menu.
+ *
+ * @param {string} eventIdentifier event namespace
+ * @param {string} elementId menu element id
+ * @param {string} buttonSelector CSS selector for toggle button
+ */
+ init: function(eventIdentifier, elementId, buttonSelector) {
+ if (elData(document.body, 'template') === 'packageInstallationSetup') {
+ // work-around for WCFSetup on mobile
+ return;
+ }
+
+ this._activeList = [];
+ this._depth = 0;
+ this._enabled = true;
+ this._eventIdentifier = eventIdentifier;
+ this._items = new ObjectMap();
+ this._menu = elById(elementId);
+ this._removeActiveList = false;
+
+ var callbackOpen = this.open.bind(this);
+ this._button = elBySel(buttonSelector);
+ this._button.addEventListener(WCF_CLICK_EVENT, callbackOpen);
+
+ this._initItems();
+ this._initHeader();
+
+ EventHandler.add(this._eventIdentifier, 'open', callbackOpen);
+ EventHandler.add(this._eventIdentifier, 'close', this.close.bind(this));
+ EventHandler.add(this._eventIdentifier, 'updateButtonState', this._updateButtonState.bind(this));
+
+ var itemList, itemLists = elByClass('menuOverlayItemList', this._menu);
+ this._menu.addEventListener('animationend', (function() {
+ if (!this._menu.classList.contains('open')) {
+ for (var i = 0, length = itemLists.length; i < length; i++) {
+ itemList = itemLists[i];
+
+ // force the main list to be displayed
+ itemList.classList.remove('active');
+ itemList.classList.remove('hidden');
+ }
+ }
+ }).bind(this));
+
+ this._menu.children[0].addEventListener('transitionend', (function() {
+ this._menu.classList.add('allowScroll');
+
+ if (this._removeActiveList) {
+ this._removeActiveList = false;
+
+ var list = this._activeList.pop();
+ if (list) {
+ list.classList.remove('activeList');
+ }
+ }
+ }).bind(this));
+
+ var backdrop = elCreate('div');
+ backdrop.className = 'menuOverlayMobileBackdrop';
+ backdrop.addEventListener(WCF_CLICK_EVENT, this.close.bind(this));
+
+ DomUtil.insertAfter(backdrop, this._menu);
+
+ this._updateButtonState();
+
+ if (Environment.platform() === 'android') {
+ this._initializeAndroid();
+ }
+ },
+
+ /**
+ * Opens the menu.
+ *
+ * @param {Event} event event object
+ * @return {boolean} true if menu has been opened
+ */
+ open: function(event) {
+ if (!this._enabled) {
+ return false;
+ }
+
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ this._menu.classList.add('open');
+ this._menu.classList.add('allowScroll');
+ this._menu.children[0].classList.add('activeList');
+
+ UiScreen.scrollDisable();
+
+ _pageContainer.classList.add('menuOverlay-' + this._menu.id);
+
+ UiScreen.pageOverlayOpen();
+
+ return true;
+ },
+
+ /**
+ * Closes the menu.
+ *
+ * @param {(Event|boolean)} event event object or boolean true to force close the menu
+ * @return {boolean} true if menu was open
+ */
+ close: function(event) {
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ if (this._menu.classList.contains('open')) {
+ this._menu.classList.remove('open');
+
+ UiScreen.scrollEnable();
+ UiScreen.pageOverlayClose();
+
+ _pageContainer.classList.remove('menuOverlay-' + this._menu.id);
+
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Enables the touch menu.
+ */
+ enable: function() {
+ this._enabled = true;
+ },
+
+ /**
+ * Disables the touch menu.
+ */
+ disable: function() {
+ this._enabled = false;
+
+ this.close(true);
+ },
+
+ /**
+ * Initializes the Android Touch Menu.
+ */
+ _initializeAndroid: function() {
+ var appearsAt, backdrop, touchStart;
+ /** @const */ var AT_EDGE = 20;
+ /** @const */ var MOVED_HORIZONTALLY = 5;
+ /** @const */ var MOVED_VERTICALLY = 20;
+
+ // specify on which side of the page the menu appears
+ switch (this._menu.id) {
+ case 'pageUserMenuMobile':
+ appearsAt = 'right';
+ break;
+ case 'pageMainMenuMobile':
+ appearsAt = 'left';
+ break;
+ default:
+ return;
+ }
+
+ backdrop = this._menu.nextElementSibling;
+
+ // horizontal position of the touch start
+ touchStart = null;
+
+ document.addEventListener('touchstart', (function(event) {
+ var touches, isOpen, isLeftEdge, isRightEdge;
+ touches = event.touches;
+
+ isOpen = this._menu.classList.contains('open');
+
+ // check whether we touch the edges of the menu
+ if (appearsAt === 'left') {
+ isLeftEdge = !isOpen && (touches[0].clientX < AT_EDGE);
+ isRightEdge = isOpen && (Math.abs(this._menu.offsetWidth - touches[0].clientX) < AT_EDGE);
+ }
+ else if (appearsAt === 'right') {
+ isLeftEdge = isOpen && (Math.abs(document.body.clientWidth - this._menu.offsetWidth - touches[0].clientX) < AT_EDGE);
+ isRightEdge = !isOpen && ((document.body.clientWidth - touches[0].clientX) < AT_EDGE);
+ }
+
+ // abort if more than one touch
+ if (touches.length > 1) {
+ if (_androidTouching) {
+ Core.triggerEvent(document, 'touchend');
+ }
+ return;
+ }
+
+ // break if a touch is in progress
+ if (_androidTouching) return;
+ // break if no edge has been touched
+ if (!isLeftEdge && !isRightEdge) return;
+ // break if a different menu is open
+ if (UiScreen.pageOverlayIsActive()) {
+ var found = false;
+ for (var i = 0; i < _pageContainer.classList.length; i++) {
+ if (_pageContainer.classList[i] === 'menuOverlay-' + this._menu.id) {
+ found = true;
+ }
+ }
+ if (!found) return;
+ }
+ // break if redactor is in use
+ if (document.documentElement.classList.contains('redactorActive')) return;
+
+ touchStart = {
+ x: touches[0].clientX,
+ y: touches[0].clientY
+ };
+
+ if (isLeftEdge) _androidTouching = 'left';
+ if (isRightEdge) _androidTouching = 'right';
+ }).bind(this));
+
+ document.addEventListener('touchend', (function(event) {
+ // break if we did not start a touch
+ if (!_androidTouching || touchStart === null) return;
+
+ // break if the menu did not even start opening
+ if (!this._menu.classList.contains('open')) {
+ // reset
+ touchStart = null;
+ _androidTouching = '';
+ return;
+ }
+
+ // last known position of the finger
+ var position;
+ if (event) {
+ position = event.changedTouches[0].clientX;
+ }
+ else {
+ position = touchStart.x;
+ }
+
+ // clean up touch styles
+ this._menu.classList.add('androidMenuTouchEnd');
+ this._menu.style.removeProperty('transform');
+ backdrop.style.removeProperty(appearsAt);
+ this._menu.addEventListener('transitionend', (function() {
+ this._menu.classList.remove('androidMenuTouchEnd');
+ }).bind(this), { once: true });
+
+ // check whether the user moved the finger far enough
+ if (appearsAt === 'left') {
+ if (_androidTouching === 'left' && position < (touchStart.x + 100)) this.close();
+ if (_androidTouching === 'right' && position < (touchStart.x - 100)) this.close();
+ }
+ else if (appearsAt === 'right') {
+ if (_androidTouching === 'left' && position > (touchStart.x + 100)) this.close();
+ if (_androidTouching === 'right' && position > (touchStart.x - 100)) this.close();
+ }
+
+ // reset
+ touchStart = null;
+ _androidTouching = '';
+ }).bind(this));
+
+ document.addEventListener('touchmove', (function(event) {
+ // break if we did not start a touch
+ if (!_androidTouching || touchStart === null) return;
+
+ var touches = event.touches;
+
+ // check whether the user started moving in the correct direction
+ // this avoids false positives, in case the user just wanted to tap
+ var movedFromEdge = false, movedVertically = false;
+ if (_androidTouching === 'left') movedFromEdge = touches[0].clientX > (touchStart.x + MOVED_HORIZONTALLY);
+ if (_androidTouching === 'right') movedFromEdge = touches[0].clientX < (touchStart.x - MOVED_HORIZONTALLY);
+ movedVertically = Math.abs(touches[0].clientY - touchStart.y) > MOVED_VERTICALLY;
+
+ var isOpen = this._menu.classList.contains('open');
+
+ if (!isOpen && movedFromEdge && !movedVertically) {
+ // the menu is not yet open, but the user moved into the right direction
+ this.open();
+ isOpen = true;
+ }
+
+ if (isOpen) {
+ // update CSS to the new finger position
+ var position = touches[0].clientX;
+ if (appearsAt === 'right') position = document.body.clientWidth - position;
+ if (position > this._menu.offsetWidth) position = this._menu.offsetWidth;
+ if (position < 0) position = 0;
+ this._menu.style.setProperty('transform', 'translateX(' + (appearsAt === 'left' ? 1 : -1) * (position - this._menu.offsetWidth) + 'px)');
+ backdrop.style.setProperty(appearsAt, Math.min(this._menu.offsetWidth, position) + 'px');
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Initializes all menu items.
+ *
+ * @protected
+ */
+ _initItems: function() {
+ elBySelAll('.menuOverlayItemLink', this._menu, this._initItem.bind(this));
+ },
+
+ /**
+ * Initializes a single menu item.
+ *
+ * @param {Element} item menu item
+ * @protected
+ */
+ _initItem: function(item) {
+ // check if it should contain a 'more' link w/ an external callback
+ var parent = item.parentNode;
+ var more = elData(parent, 'more');
+ if (more) {
+ item.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ EventHandler.fire(this._eventIdentifier, 'more', {
+ handler: this,
+ identifier: more,
+ item: item,
+ parent: parent
+ });
+ }).bind(this));
+
+ return;
+ }
+
+ var itemList = item.nextElementSibling, wrapper;
+ if (itemList === null) {
+ return;
+ }
+
+ // handle static items with an icon-type button next to it (acp menu)
+ if (itemList.nodeName !== 'OL' && itemList.classList.contains('menuOverlayItemLinkIcon')) {
+ // add wrapper
+ wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+ parent.insertBefore(wrapper, item);
+ wrapper.appendChild(item);
+
+ while (wrapper.nextElementSibling) {
+ wrapper.appendChild(wrapper.nextElementSibling);
+ }
+
+ return;
+ }
+
+ var isLink = (elAttr(item, 'href') !== '#');
+ var parentItemList = parent.parentNode;
+ var itemTitle = elData(itemList, 'title');
+
+ this._items.set(item, {
+ itemList: itemList,
+ parentItemList: parentItemList
+ });
+
+ if (itemTitle === '') {
+ itemTitle = DomTraverse.childByClass(item, 'menuOverlayItemTitle').textContent;
+ elData(itemList, 'title', itemTitle);
+ }
+
+ var callbackLink = this._showItemList.bind(this, item);
+ if (isLink) {
+ wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+ parent.insertBefore(wrapper, item);
+ wrapper.appendChild(item);
+
+ var moreLink = elCreate('a');
+ elAttr(moreLink, 'href', '#');
+ moreLink.className = 'menuOverlayItemLinkIcon' + (item.classList.contains('active') ? ' active' : '');
+ moreLink.innerHTML = '<span class="icon icon24 fa-angle-right"></span>';
+ moreLink.addEventListener(WCF_CLICK_EVENT, callbackLink);
+ wrapper.appendChild(moreLink);
+ }
+ else {
+ item.classList.add('menuOverlayItemLinkMore');
+ item.addEventListener(WCF_CLICK_EVENT, callbackLink);
+ }
+
+ var backLinkItem = elCreate('li');
+ backLinkItem.className = 'menuOverlayHeader';
+
+ wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+
+ var backLink = elCreate('a');
+ elAttr(backLink, 'href', '#');
+ backLink.className = 'menuOverlayItemLink menuOverlayBackLink';
+ backLink.textContent = elData(parentItemList, 'title');
+ backLink.addEventListener(WCF_CLICK_EVENT, this._hideItemList.bind(this, item));
+
+ var closeLink = elCreate('a');
+ elAttr(closeLink, 'href', '#');
+ closeLink.className = 'menuOverlayItemLinkIcon';
+ closeLink.innerHTML = '<span class="icon icon24 fa-times"></span>';
+ closeLink.addEventListener(WCF_CLICK_EVENT, this.close.bind(this));
+
+ wrapper.appendChild(backLink);
+ wrapper.appendChild(closeLink);
+ backLinkItem.appendChild(wrapper);
+
+ itemList.insertBefore(backLinkItem, itemList.firstElementChild);
+
+ if (!backLinkItem.nextElementSibling.classList.contains('menuOverlayTitle')) {
+ var titleItem = elCreate('li');
+ titleItem.className = 'menuOverlayTitle';
+ var title = elCreate('span');
+ title.textContent = itemTitle;
+ titleItem.appendChild(title);
+
+ itemList.insertBefore(titleItem, backLinkItem.nextElementSibling);
+ }
+ },
+
+ /**
+ * Renders the menu item list header.
+ *
+ * @protected
+ */
+ _initHeader: function() {
+ var listItem = elCreate('li');
+ listItem.className = 'menuOverlayHeader';
+
+ var wrapper = elCreate('span');
+ wrapper.className = 'menuOverlayItemWrapper';
+ listItem.appendChild(wrapper);
+
+ var logoWrapper = elCreate('span');
+ logoWrapper.className = 'menuOverlayLogoWrapper';
+ wrapper.appendChild(logoWrapper);
+
+ var logo = elCreate('span');
+ logo.className = 'menuOverlayLogo';
+ logo.style.setProperty('background-image', 'url("' + elData(this._menu, 'page-logo') + '")', '');
+ logoWrapper.appendChild(logo);
+
+ var closeLink = elCreate('a');
+ elAttr(closeLink, 'href', '#');
+ closeLink.className = 'menuOverlayItemLinkIcon';
+ closeLink.innerHTML = '<span class="icon icon24 fa-times"></span>';
+ closeLink.addEventListener(WCF_CLICK_EVENT, this.close.bind(this));
+ wrapper.appendChild(closeLink);
+
+ var list = DomTraverse.childByClass(this._menu, 'menuOverlayItemList');
+ list.insertBefore(listItem, list.firstElementChild);
+ },
+
+ /**
+ * Hides an item list, return to the parent item list.
+ *
+ * @param {Element} item menu item
+ * @param {Event} event event object
+ * @protected
+ */
+ _hideItemList: function(item, event) {
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ this._menu.classList.remove('allowScroll');
+ this._removeActiveList = true;
+
+ var data = this._items.get(item);
+ data.parentItemList.classList.remove('hidden');
+
+ this._updateDepth(false);
+ },
+
+ /**
+ * Shows the child item list.
+ *
+ * @param {Element} item menu item
+ * @param event
+ * @private
+ */
+ _showItemList: function(item, event) {
+ if (event instanceof Event) {
+ event.preventDefault();
+ }
+
+ var data = this._items.get(item);
+
+ var load = elData(data.itemList, 'load');
+ if (load) {
+ if (!elDataBool(item, 'loaded')) {
+ var icon = event.currentTarget.firstElementChild;
+ if (icon.classList.contains('fa-angle-right')) {
+ icon.classList.remove('fa-angle-right');
+ icon.classList.add('fa-spinner');
+ }
+
+ EventHandler.fire(this._eventIdentifier, 'load_' + load);
+
+ return;
+ }
+ }
+
+ this._menu.classList.remove('allowScroll');
+
+ data.itemList.classList.add('activeList');
+ data.parentItemList.classList.add('hidden');
+
+ this._activeList.push(data.itemList);
+
+ this._updateDepth(true);
+ },
+
+ _updateDepth: function(increase) {
+ this._depth += (increase) ? 1 : -1;
+
+ var offset = this._depth * -100;
+ if (Language.get('wcf.global.pageDirection') === 'rtl') {
+ // reverse logic for RTL
+ offset *= -1;
+ }
+
+ this._menu.children[0].style.setProperty('transform', 'translateX(' + offset + '%)', '');
+ },
+
+ _updateButtonState: function() {
+ var hasNewContent = false;
+ elBySelAll('.badgeUpdate', this._menu, function (badge) {
+ if (~~badge.textContent > 0) {
+ hasNewContent = true;
+ }
+ });
+
+ this._button.classList[(hasNewContent ? 'add' : 'remove')]('pageMenuMobileButtonHasContent');
+ }
+ };
+
+ return UiPageMenuAbstract;
+});
+
+/**
+ * Provides the touch-friendly fullscreen main menu.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/Main
+ */
+define('WoltLabSuite/Core/Ui/Page/Menu/Main',['Core', 'Language', 'Dom/Traverse', './Abstract'], function(Core, Language, DomTraverse, UiPageMenuAbstract) {
+ "use strict";
+
+ var _optionsTitle = null, _hasItems = null, _list = null, _navigationList = null, _callbackClose = null;
+
+ /**
+ * @constructor
+ */
+ function UiPageMenuMain() { this.init(); }
+ Core.inherit(UiPageMenuMain, UiPageMenuAbstract, {
+ /**
+ * Initializes the touch-friendly fullscreen main menu.
+ */
+ init: function() {
+ UiPageMenuMain._super.prototype.init.call(
+ this,
+ 'com.woltlab.wcf.MainMenuMobile',
+ 'pageMainMenuMobile',
+ '#pageHeader .mainMenu'
+ );
+
+ _optionsTitle = elById('pageMainMenuMobilePageOptionsTitle');
+ if (_optionsTitle !== null) {
+ _list = DomTraverse.childByClass(_optionsTitle, 'menuOverlayItemList');
+ _navigationList = elBySel('.jsPageNavigationIcons');
+
+ _callbackClose = (function(event) {
+ this.close();
+ event.stopPropagation();
+ }).bind(this);
+ }
+
+ elAttr(this._button, 'aria-label', Language.get('wcf.menu.page'));
+ elAttr(this._button, 'role', 'button');
+ },
+
+ open: function (event) {
+ if (!UiPageMenuMain._super.prototype.open.call(this, event)) {
+ return false;
+ }
+
+ if (_optionsTitle === null) {
+ return true;
+ }
+
+ _hasItems = _navigationList && _navigationList.childElementCount > 0;
+
+ if (_hasItems) {
+ var item, link;
+ while (_navigationList.childElementCount) {
+ item = _navigationList.children[0];
+
+ item.classList.add('menuOverlayItem');
+ item.classList.add('menuOverlayItemOption');
+ item.addEventListener(WCF_CLICK_EVENT, _callbackClose);
+
+ link = item.children[0];
+ link.classList.add('menuOverlayItemLink');
+ link.classList.add('box24');
+
+ link.children[1].classList.remove('invisible');
+ link.children[1].classList.add('menuOverlayItemTitle');
+
+ _optionsTitle.parentNode.insertBefore(item, _optionsTitle.nextSibling);
+ }
+
+ elShow(_optionsTitle);
+ }
+ else {
+ elHide(_optionsTitle);
+ }
+
+ return true;
+ },
+
+ close: function(event) {
+ if (!UiPageMenuMain._super.prototype.close.call(this, event)) {
+ return false;
+ }
+
+ if (_hasItems) {
+ elHide(_optionsTitle);
+
+ var item = _optionsTitle.nextElementSibling;
+ var link;
+ while (item && item.classList.contains('menuOverlayItemOption')) {
+ item.classList.remove('menuOverlayItem');
+ item.classList.remove('menuOverlayItemOption');
+ item.removeEventListener(WCF_CLICK_EVENT, _callbackClose);
+
+ link = item.children[0];
+ link.classList.remove('menuOverlayItemLink');
+ link.classList.remove('box24');
+
+ link.children[1].classList.add('invisible');
+ link.children[1].classList.remove('menuOverlayItemTitle');
+
+ _navigationList.appendChild(item);
+
+ item = item.nextElementSibling;
+ }
+ }
+
+ return true;
+ }
+ });
+
+ return UiPageMenuMain;
+});
+
+/**
+ * Provides the touch-friendly fullscreen user menu.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/User
+ */
+define('WoltLabSuite/Core/Ui/Page/Menu/User',['Core', 'EventHandler', 'Language', './Abstract'], function(Core, EventHandler, Language, UiPageMenuAbstract) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiPageMenuUser() { this.init(); }
+ Core.inherit(UiPageMenuUser, UiPageMenuAbstract, {
+ /**
+ * Initializes the touch-friendly fullscreen user menu.
+ */
+ init: function() {
+ // check if user menu is actually empty
+ var menu = elBySel('#pageUserMenuMobile > .menuOverlayItemList');
+ if (menu.childElementCount === 1 && menu.children[0].classList.contains('menuOverlayTitle')) {
+ elBySel('#pageHeader .userPanel').classList.add('hideUserPanel');
+ return;
+ }
+
+ UiPageMenuUser._super.prototype.init.call(
+ this,
+ 'com.woltlab.wcf.UserMenuMobile',
+ 'pageUserMenuMobile',
+ '#pageHeader .userPanel'
+ );
+
+ EventHandler.add('com.woltlab.wcf.userMenu', 'updateBadge', (function (data) {
+ elBySelAll('.menuOverlayItemBadge', this._menu, (function (item) {
+ if (elData(item, 'badge-identifier') === data.identifier) {
+ var badge = elBySel('.badge', item);
+ if (data.count) {
+ if (badge === null) {
+ badge = elCreate('span');
+ badge.className = 'badge badgeUpdate';
+ item.appendChild(badge);
+ }
+
+ badge.textContent = data.count;
+ }
+ else if (badge !== null) {
+ elRemove(badge);
+ }
+
+ this._updateButtonState();
+ }
+ }).bind(this));
+ }).bind(this));
+
+ elAttr(this._button, 'aria-label', Language.get('wcf.menu.user'));
+ elAttr(this._button, 'role', 'button');
+ },
+
+ close: function (event) {
+ var dropdown = WCF.Dropdown.Interactive.Handler.getOpenDropdown();
+ if (dropdown) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ dropdown.close();
+ }
+ else {
+ UiPageMenuUser._super.prototype.close.call(this, event);
+ }
+ }
+ });
+
+ return UiPageMenuUser;
+});
+
+/**
+ * Simple interface to work with reusable dropdowns that are not bound to a specific item.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dropdown/Reusable
+ */
+define('WoltLabSuite/Core/Ui/Dropdown/Reusable',['Dictionary', 'Ui/SimpleDropdown'], function(Dictionary, UiSimpleDropdown) {
+ "use strict";
+
+ var _dropdowns = new Dictionary();
+ var _ghostElementId = 0;
+
+ /**
+ * Returns dropdown name by internal identifier.
+ *
+ * @param {string} identifier internal identifier
+ * @returns {string} dropdown name
+ */
+ function _getDropdownName(identifier) {
+ if (!_dropdowns.has(identifier)) {
+ throw new Error("Unknown dropdown identifier '" + identifier + "'");
+ }
+
+ return _dropdowns.get(identifier);
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dropdown/Reusable
+ */
+ return {
+ /**
+ * Initializes a new reusable dropdown.
+ *
+ * @param {string} identifier internal identifier
+ * @param {Element} menu dropdown menu element
+ */
+ init: function(identifier, menu) {
+ if (_dropdowns.has(identifier)) {
+ return;
+ }
+
+ var ghostElement = elCreate('div');
+ ghostElement.id = 'reusableDropdownGhost' + _ghostElementId++;
+
+ UiSimpleDropdown.initFragment(ghostElement, menu);
+
+ _dropdowns.set(identifier, ghostElement.id);
+ },
+
+ /**
+ * Returns the dropdown menu element.
+ *
+ * @param {string} identifier internal identifier
+ * @returns {Element} dropdown menu element
+ */
+ getDropdownMenu: function(identifier) {
+ return UiSimpleDropdown.getDropdownMenu(_getDropdownName(identifier));
+ },
+
+ /**
+ * Registers a callback invoked upon open and close.
+ *
+ * @param {string} identifier internal identifier
+ * @param {function} callback callback function
+ */
+ registerCallback: function(identifier, callback) {
+ UiSimpleDropdown.registerCallback(_getDropdownName(identifier), callback);
+ },
+
+ /**
+ * Toggles a dropdown.
+ *
+ * @param {string} identifier internal identifier
+ * @param {Element} referenceElement reference element used for alignment
+ */
+ toggleDropdown: function(identifier, referenceElement) {
+ UiSimpleDropdown.toggleDropdown(_getDropdownName(identifier), referenceElement);
+ }
+ };
+});
+
+/**
+ * Modifies the interface to provide a better usability for mobile devices.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Mobile
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Mobile',[ 'Core', 'Environment', 'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse', 'Ui/Alignment', 'Ui/CloseOverlay', 'Ui/Screen', './Page/Menu/Main', './Page/Menu/User', 'WoltLabSuite/Core/Ui/Dropdown/Reusable'],
+ function(Core, Environment, EventHandler, Language, List, DomChangeListener, DomTraverse, UiAlignment, UiCloseOverlay, UiScreen, UiPageMenuMain, UiPageMenuUser, UiDropdownReusable)
+{
+ "use strict";
+
+ var _buttonGroupNavigations = elByClass('buttonGroupNavigation');
+ var _callbackCloseDropdown = null;
+ var _dropdownMenu = null;
+ var _dropdownMenuMessage = null;
+ var _enabled = false;
+ var _knownMessages = new List();
+ var _main = null;
+ var _messages = elByClass('message');
+ var _options = {};
+ var _pageMenuMain = null;
+ var _pageMenuUser = null;
+ var _messageGroups = null;
+ var _sidebars = [];
+ var _sidebarXsEnabled = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Mobile
+ */
+ return {
+ /**
+ * Initializes the mobile UI.
+ *
+ * @param {Object=} options initialization options
+ */
+ setup: function(options) {
+ _options = Core.extend({
+ enableMobileMenu: true
+ }, options);
+
+ _main = elById('main');
+
+ elBySelAll('.sidebar', undefined, function (sidebar) {
+ _sidebars.push(sidebar);
+ });
+
+ if (Environment.touch()) {
+ document.documentElement.classList.add('touch');
+ }
+
+ if (Environment.platform() !== 'desktop') {
+ document.documentElement.classList.add('mobile');
+ }
+
+ var messageGroupList = elBySel('.messageGroupList');
+ if (messageGroupList) _messageGroups = elByClass('messageGroup', messageGroupList);
+
+ UiScreen.on('screen-md-down', {
+ match: this.enable.bind(this),
+ unmatch: this.disable.bind(this),
+ setup: this._init.bind(this)
+ });
+
+ UiScreen.on('screen-sm-down', {
+ match: this.enableShadow.bind(this),
+ unmatch: this.disableShadow.bind(this),
+ setup: this.enableShadow.bind(this)
+ });
+
+ UiScreen.on('screen-xs', {
+ match: this._enableSidebarXS.bind(this),
+ unmatch: this._disableSidebarXS.bind(this),
+ setup: this._setupSidebarXS.bind(this)
+ });
+ },
+
+ /**
+ * Enables the mobile UI.
+ */
+ enable: function() {
+ _enabled = true;
+
+ if (_options.enableMobileMenu) {
+ _pageMenuMain.enable();
+ _pageMenuUser.enable();
+ }
+ },
+
+ /**
+ * Enables shadow links for larger click areas on messages.
+ */
+ enableShadow: function () {
+ if (_messageGroups) this.rebuildShadow(_messageGroups, '.messageGroupLink');
+ },
+
+ /**
+ * Disables the mobile UI.
+ */
+ disable: function() {
+ _enabled = false;
+
+ if (_options.enableMobileMenu) {
+ _pageMenuMain.disable();
+ _pageMenuUser.disable();
+ }
+ },
+
+ /**
+ * Disables shadow links.
+ */
+ disableShadow: function () {
+ if (_messageGroups) this.removeShadow(_messageGroups);
+
+ if (_dropdownMenu) _callbackCloseDropdown();
+ },
+
+ _init: function() {
+ _enabled = true;
+
+ this._initSearchBar();
+ this._initButtonGroupNavigation();
+ this._initMessages();
+ this._initMobileMenu();
+
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Mobile', this._closeAllMenus.bind(this));
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Mobile', (function() {
+ this._initButtonGroupNavigation();
+ this._initMessages();
+ }).bind(this));
+ },
+
+ _initSearchBar: function() {
+ var _searchBar = elById('pageHeaderSearch');
+ var _searchInput = elById('pageHeaderSearchInput');
+
+ var scrollTop = null;
+
+ EventHandler.add('com.woltlab.wcf.MainMenuMobile', 'more', function(data) {
+ if (data.identifier === 'com.woltlab.wcf.search') {
+ data.handler.close(true);
+
+ if (Environment.platform() === 'ios') {
+ scrollTop = document.body.scrollTop;
+ UiScreen.scrollDisable();
+ }
+
+ _searchBar.style.setProperty('top', elById('pageHeader').offsetHeight + 'px', '');
+ _searchBar.classList.add('open');
+ _searchInput.focus();
+
+ if (Environment.platform() === 'ios') {
+ document.body.scrollTop = 0;
+ }
+ }
+ });
+
+ _main.addEventListener(WCF_CLICK_EVENT, function() {
+ if (_searchBar) _searchBar.classList.remove('open');
+
+ if (Environment.platform() === 'ios' && scrollTop !== null) {
+ UiScreen.scrollEnable();
+ document.body.scrollTop = scrollTop;
+
+ scrollTop = null;
+ }
+ });
+ },
+
+ _initButtonGroupNavigation: function() {
+ for (var i = 0, length = _buttonGroupNavigations.length; i < length; i++) {
+ var navigation = _buttonGroupNavigations[i];
+
+ if (navigation.classList.contains('jsMobileButtonGroupNavigation')) continue;
+ else navigation.classList.add('jsMobileButtonGroupNavigation');
+
+ var list = elBySel('.buttonList', navigation);
+ if (list.childElementCount === 0) {
+ // ignore objects without options
+ continue;
+ }
+
+ navigation.parentNode.classList.add('hasMobileNavigation');
+
+ var button = elCreate('a');
+ button.className = 'dropdownLabel';
+
+ var span = elCreate('span');
+ span.className = 'icon icon24 fa-ellipsis-v';
+ button.appendChild(span);
+
+ (function(navigation, button, list) {
+ button.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ navigation.classList.toggle('open');
+ });
+
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.stopPropagation();
+
+ navigation.classList.remove('open');
+ });
+ })(navigation, button, list);
+
+ navigation.insertBefore(button, navigation.firstChild);
+ }
+ },
+
+ _initMessages: function() {
+ Array.prototype.forEach.call(_messages, (function(message) {
+ if (_knownMessages.has(message)) {
+ return;
+ }
+
+ var navigation = elBySel('.jsMobileNavigation', message);
+ if (navigation) {
+ navigation.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.stopPropagation();
+
+ // mimic dropdown behavior
+ window.setTimeout(function () {
+ navigation.classList.remove('open');
+ }, 10);
+ });
+
+ var quickOptions = elBySel('.messageQuickOptions', message);
+ if (quickOptions && navigation.childElementCount) {
+ quickOptions.classList.add('active');
+ quickOptions.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ if (_enabled && event.target.nodeName !== 'LABEL' && event.target.nodeName !== 'INPUT') {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this._toggleMobileNavigation(message, quickOptions, navigation);
+ }
+ }).bind(this));
+ }
+ }
+
+ _knownMessages.add(message);
+ }).bind(this));
+ },
+
+ _initMobileMenu: function() {
+ if (_options.enableMobileMenu) {
+ _pageMenuMain = new UiPageMenuMain();
+ _pageMenuUser = new UiPageMenuUser();
+ }
+ },
+
+ _closeAllMenus: function() {
+ elBySelAll('.jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open', null, function (menu) {
+ menu.classList.remove('open');
+ });
+
+ if (_enabled && _dropdownMenu) _callbackCloseDropdown();
+ },
+
+ rebuildShadow: function(elements, linkSelector) {
+ var element, parent, shadow;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ parent = element.parentNode;
+
+ shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
+ if (shadow === null) {
+ if (elBySel(linkSelector, element).href) {
+ shadow = elCreate('a');
+ shadow.className = 'mobileLinkShadow';
+ shadow.href = elBySel(linkSelector, element).href;
+
+ parent.appendChild(shadow);
+ parent.classList.add('mobileLinkShadowContainer');
+ }
+ }
+ }
+ },
+
+ removeShadow: function(elements) {
+ var element, parent, shadow;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ parent = element.parentNode;
+
+ if (parent.classList.contains('mobileLinkShadowContainer')) {
+ shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
+ if (shadow !== null) {
+ elRemove(shadow);
+ }
+
+ parent.classList.remove('mobileLinkShadowContainer');
+ }
+ }
+ },
+
+ _enableSidebarXS: function() {
+ _sidebarXsEnabled = true;
+ },
+
+ _disableSidebarXS: function() {
+ _sidebarXsEnabled = false;
+
+ _sidebars.forEach(function (sidebar) {
+ sidebar.classList.remove('open');
+ });
+ },
+
+ _setupSidebarXS: function() {
+ _sidebars.forEach(function (sidebar) {
+ sidebar.addEventListener('mousedown', function(event) {
+ if (_sidebarXsEnabled && event.target === sidebar) {
+ event.preventDefault();
+
+ sidebar.classList.toggle('open');
+ }
+ });
+ });
+
+ _sidebarXsEnabled = true;
+ },
+
+ _toggleMobileNavigation: function (message, quickOptions, navigation) {
+ if (_dropdownMenu === null) {
+ _dropdownMenu = elCreate('ul');
+ _dropdownMenu.className = 'dropdownMenu';
+
+ UiDropdownReusable.init('com.woltlab.wcf.jsMobileNavigation', _dropdownMenu);
+
+ _callbackCloseDropdown = function () {
+ _dropdownMenu.classList.remove('dropdownOpen');
+ }
+ }
+ else if (_dropdownMenu.classList.contains('dropdownOpen')) {
+ _callbackCloseDropdown();
+
+ if (_dropdownMenuMessage === message) {
+ // toggle behavior
+ return;
+ }
+ }
+
+ _dropdownMenu.innerHTML = '';
+ UiCloseOverlay.execute();
+
+ this._rebuildMobileNavigation(navigation);
+
+ var previousNavigation = navigation.previousElementSibling;
+ if (previousNavigation && previousNavigation.classList.contains('messageFooterButtonsExtra')) {
+ var divider = elCreate('li');
+ divider.className = 'dropdownDivider';
+ _dropdownMenu.appendChild(divider);
+
+ this._rebuildMobileNavigation(previousNavigation);
+ }
+
+ UiAlignment.set(_dropdownMenu, quickOptions, {
+ horizontal: 'right',
+ allowFlip: 'vertical'
+ });
+ _dropdownMenu.classList.add('dropdownOpen');
+
+ _dropdownMenuMessage = message;
+ },
+
+ _rebuildMobileNavigation: function (navigation) {
+ elBySelAll('.button:not(.ignoreMobileNavigation)', navigation, function (button) {
+ var item = elCreate('li');
+ if (button.classList.contains('active')) item.className = 'active';
+ item.innerHTML = '<a href="#">' + elBySel('span:not(.icon)', button).textContent + '</a>';
+ item.children[0].addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (button.nodeName === 'A') button.click();
+ else Core.triggerEvent(button, WCF_CLICK_EVENT);
+
+ _callbackCloseDropdown();
+ });
+
+ _dropdownMenu.appendChild(item);
+ });
+ }
+ };
+});
+
+/**
+ * Simple tab menu implementation with a straight-forward logic.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/TabMenu/Simple
+ */
+define('WoltLabSuite/Core/Ui/TabMenu/Simple',['Dictionary', 'EventHandler', 'Dom/Traverse', 'Dom/Util'], function(Dictionary, EventHandler, DomTraverse, DomUtil) {
+ "use strict";
+
+ /**
+ * @param {Element} container container element
+ * @constructor
+ */
+ function TabMenuSimple(container) {
+ this._container = container;
+ this._containers = new Dictionary();
+ this._isLegacy = null;
+ this._store = null;
+ this._tabs = new Dictionary();
+ }
+
+ TabMenuSimple.prototype = {
+ /**
+ * Validates the properties and DOM structure of this container.
+ *
+ * Expected DOM:
+ * <div class="tabMenuContainer">
+ * <nav>
+ * <ul>
+ * <li data-name="foo"><a>bar</a></li>
+ * </ul>
+ * </nav>
+ *
+ * <div id="foo">baz</div>
+ * </div>
+ *
+ * @return {boolean} false if any properties are invalid or the DOM does not match the expectations
+ */
+ validate: function() {
+ if (!this._container.classList.contains('tabMenuContainer')) {
+ return false;
+ }
+
+ var nav = DomTraverse.childByTag(this._container, 'NAV');
+ if (nav === null) {
+ return false;
+ }
+
+ // get children
+ var tabs = elByTag('li', nav);
+ if (tabs.length === 0) {
+ return false;
+ }
+
+ var container, containers = DomTraverse.childrenByTag(this._container, 'DIV'), name, i, length;
+ for (i = 0, length = containers.length; i < length; i++) {
+ container = containers[i];
+ name = elData(container, 'name');
+
+ if (!name) {
+ name = DomUtil.identify(container);
+ }
+
+ elData(container, 'name', name);
+ this._containers.set(name, container);
+ }
+
+ var containerId = this._container.id, tab;
+ for (i = 0, length = tabs.length; i < length; i++) {
+ tab = tabs[i];
+ name = this._getTabName(tab);
+
+ if (!name) {
+ continue;
+ }
+
+ if (this._tabs.has(name)) {
+ throw new Error("Tab names must be unique, li[data-name='" + name + "'] (tab menu id: '" + containerId + "') exists more than once.");
+ }
+
+ container = this._containers.get(name);
+ if (container === undefined) {
+ throw new Error("Expected content element for li[data-name='" + name + "'] (tab menu id: '" + containerId + "').");
+ }
+ else if (container.parentNode !== this._container) {
+ throw new Error("Expected content element '" + name + "' (tab menu id: '" + containerId + "') to be a direct children.");
+ }
+
+ // check if tab holds exactly one children which is an anchor element
+ if (tab.childElementCount !== 1 || tab.children[0].nodeName !== 'A') {
+ throw new Error("Expected exactly one <a> as children for li[data-name='" + name + "'] (tab menu id: '" + containerId + "').");
+ }
+
+ this._tabs.set(name, tab);
+ }
+
+ if (!this._tabs.size) {
+ throw new Error("Expected at least one tab (tab menu id: '" + containerId + "').");
+ }
+
+ if (this._isLegacy) {
+ elData(this._container, 'is-legacy', true);
+
+ this._tabs.forEach(function(tab, name) {
+ elAttr(tab, 'aria-controls', name);
+ });
+ }
+
+ return true;
+ },
+
+ /**
+ * Initializes this tab menu.
+ *
+ * @param {Dictionary=} oldTabs previous list of tabs
+ * @return {?Element} parent tab for selection or null
+ */
+ init: function(oldTabs) {
+ oldTabs = oldTabs || null;
+
+ // bind listeners
+ this._tabs.forEach((function(tab) {
+ if (!oldTabs || oldTabs.get(elData(tab, 'name')) !== tab) {
+ tab.children[0].addEventListener(WCF_CLICK_EVENT, this._onClick.bind(this));
+ }
+ }).bind(this));
+
+ var returnValue = null;
+ if (!oldTabs) {
+ var hash = TabMenuSimple.getIdentifierFromHash();
+ var selectTab = null;
+ if (hash !== '') {
+ selectTab = this._tabs.get(hash);
+
+ // check for parent tab menu
+ if (selectTab && this._container.parentNode.classList.contains('tabMenuContainer')) {
+ returnValue = this._container;
+ }
+ }
+
+ if (!selectTab) {
+ var preselect = elData(this._container, 'preselect') || elData(this._container, 'active');
+ if (preselect === "true" || !preselect) preselect = true;
+
+ if (preselect === true) {
+ this._tabs.forEach(function(tab) {
+ if (!selectTab && !elIsHidden(tab) && (!tab.previousElementSibling || elIsHidden(tab.previousElementSibling))) {
+ selectTab = tab;
+ }
+ });
+ }
+ else if (preselect !== "false") {
+ selectTab = this._tabs.get(preselect);
+ }
+ }
+
+ if (selectTab) {
+ this._containers.forEach(function(container) {
+ container.classList.add('hidden');
+ });
+
+ this.select(null, selectTab, true);
+ }
+
+ var store = elData(this._container, 'store');
+ if (store) {
+ var input = elCreate('input');
+ input.type = 'hidden';
+ input.name = store;
+ input.value = elData(this.getActiveTab(), 'name');
+
+ this._container.appendChild(input);
+
+ this._store = input;
+ }
+ }
+
+ return returnValue;
+ },
+
+ /**
+ * Selects a tab.
+ *
+ * @param {?(string|int)} name tab name or sequence no
+ * @param {Element=} tab tab element
+ * @param {boolean=} disableEvent suppress event handling
+ */
+ select: function(name, tab, disableEvent) {
+ tab = tab || this._tabs.get(name);
+
+ if (!tab) {
+ // check if name is an integer
+ if (~~name == name) {
+ name = ~~name;
+
+ var i = 0;
+ this._tabs.forEach(function(item) {
+ if (i === name) {
+ tab = item;
+ }
+
+ i++;
+ });
+ }
+
+ if (!tab) {
+ throw new Error("Expected a valid tab name, '" + name + "' given (tab menu id: '" + this._container.id + "').");
+ }
+ }
+
+ name = name || elData(tab, 'name');
+
+ // unmark active tab
+ var oldTab = this.getActiveTab();
+ var oldContent = null;
+ if (oldTab) {
+ var oldTabName = elData(oldTab, 'name');
+ if (oldTabName === name) {
+ // same tab
+ return;
+ }
+
+ if (!disableEvent) {
+ EventHandler.fire('com.woltlab.wcf.simpleTabMenu_' + this._container.id, 'beforeSelect', {
+ tab: oldTab,
+ tabName: oldTabName
+ });
+ }
+
+ oldTab.classList.remove('active');
+ oldContent = this._containers.get(elData(oldTab, 'name'));
+ oldContent.classList.remove('active');
+ oldContent.classList.add('hidden');
+
+ if (this._isLegacy) {
+ oldTab.classList.remove('ui-state-active');
+ oldContent.classList.remove('ui-state-active');
+ }
+ }
+
+ tab.classList.add('active');
+ var newContent = this._containers.get(name);
+ newContent.classList.add('active');
+ newContent.classList.remove('hidden');
+
+ if (this._isLegacy) {
+ tab.classList.add('ui-state-active');
+ newContent.classList.add('ui-state-active');
+ }
+
+ if (this._store) {
+ this._store.value = name;
+ }
+
+ if (!disableEvent) {
+ EventHandler.fire('com.woltlab.wcf.simpleTabMenu_' + this._container.id, 'select', {
+ active: tab,
+ activeName: name,
+ previous: oldTab,
+ previousName: oldTab ? elData(oldTab, 'name') : null
+ });
+
+ var jQuery = (this._isLegacy && typeof window.jQuery === 'function') ? window.jQuery : null;
+ if (jQuery) {
+ // simulate jQuery UI Tabs event
+ jQuery(this._container).trigger('wcftabsbeforeactivate', {
+ newTab: jQuery(tab),
+ oldTab: jQuery(oldTab),
+ newPanel: jQuery(newContent),
+ oldPanel: jQuery(oldContent)
+ });
+ }
+
+ var location = window.location.href.replace(/#+[^#]*$/, '');
+ if (TabMenuSimple.getIdentifierFromHash() === name) {
+ location += window.location.hash;
+ }
+ else {
+ location += '#' + name;
+ }
+
+ // update history
+ //noinspection JSCheckFunctionSignatures
+ window.history.replaceState(
+ undefined,
+ undefined,
+ location
+ );
+ }
+
+ require(['WoltLabSuite/Core/Ui/TabMenu'], function (UiTabMenu) {
+ //noinspection JSUnresolvedFunction
+ UiTabMenu.scrollToTab(tab);
+ });
+ },
+
+ /**
+ * Selects the first visible tab of the tab menu and return `true`. If there is no
+ * visible tab, `false` is returned.
+ *
+ * The visibility of a tab is determined by calling `elIsHidden` with the tab menu
+ * item as the parameter.
+ *
+ * @return {boolean}
+ */
+ selectFirstVisible: function() {
+ var selectTab;
+ this._tabs.forEach(function(tab) {
+ if (!selectTab && !elIsHidden(tab)) {
+ selectTab = tab;
+ }
+ }.bind(this));
+
+ if (selectTab) {
+ this.select(undefined, selectTab, false);
+ }
+
+ return !!selectTab;
+ },
+
+ /**
+ * Rebuilds all tabs, must be invoked after adding or removing of tabs.
+ *
+ * Warning: Do not remove tabs if you plan to add these later again or at least clone the nodes
+ * to prevent issues with already bound event listeners. Consider hiding them via CSS.
+ */
+ rebuild: function() {
+ var oldTabs = new Dictionary();
+ oldTabs.merge(this._tabs);
+
+ this.validate();
+ this.init(oldTabs);
+ },
+
+ /**
+ * Returns true if this tab menu has a tab with provided name.
+ *
+ * @param {string} name tab name
+ * @return {boolean} true if tab name matches
+ */
+ hasTab: function (name) {
+ return this._tabs.has(name);
+ },
+
+ /**
+ * Handles clicks on a tab.
+ *
+ * @param {object} event event object
+ */
+ _onClick: function(event) {
+ event.preventDefault();
+
+ this.select(null, event.currentTarget.parentNode);
+ },
+
+ /**
+ * Returns the tab name.
+ *
+ * @param {Element} tab tab element
+ * @return {string} tab name
+ */
+ _getTabName: function(tab) {
+ var name = elData(tab, 'name');
+
+ // handle legacy tab menus
+ if (!name) {
+ if (tab.childElementCount === 1 && tab.children[0].nodeName === 'A') {
+ if (tab.children[0].href.match(/#([^#]+)$/)) {
+ name = RegExp.$1;
+
+ if (elById(name) === null) {
+ name = null;
+ }
+ else {
+ this._isLegacy = true;
+ elData(tab, 'name', name);
+ }
+ }
+ }
+ }
+
+ return name;
+ },
+
+ /**
+ * Returns the currently active tab.
+ *
+ * @return {Element} active tab
+ */
+ getActiveTab: function() {
+ return elBySel('#' + this._container.id + ' > nav > ul > li.active');
+ },
+
+ /**
+ * Returns the list of registered content containers.
+ *
+ * @returns {Dictionary} content containers
+ */
+ getContainers: function() {
+ return this._containers;
+ },
+
+ /**
+ * Returns the list of registered tabs.
+ *
+ * @returns {Dictionary} tab items
+ */
+ getTabs: function() {
+ return this._tabs;
+ }
+ };
+
+ TabMenuSimple.getIdentifierFromHash = function () {
+ if (window.location.hash.match(/^#+([^\/]+)+(?:\/.+)?/)) {
+ return RegExp.$1;
+ }
+
+ return '';
+ };
+
+ return TabMenuSimple;
+});
+
+/**
+ * Common interface for tab menu access.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/TabMenu
+ */
+define('WoltLabSuite/Core/Ui/TabMenu',['Dictionary', 'EventHandler', 'Dom/ChangeListener', 'Dom/Util', 'Ui/CloseOverlay', 'Ui/Screen', './TabMenu/Simple'], function(Dictionary, EventHandler, DomChangeListener, DomUtil, UiCloseOverlay, UiScreen, SimpleTabMenu) {
+ "use strict";
+
+ var _activeList = null;
+ var _enableTabScroll = false;
+ var _tabMenus = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/TabMenu
+ */
+ return {
+ /**
+ * Sets up tab menus and binds listeners.
+ */
+ setup: function() {
+ this._init();
+ this._selectErroneousTabs();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/TabMenu', this._init.bind(this));
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/TabMenu', function() {
+ if (_activeList) {
+ _activeList.classList.remove('active');
+
+ _activeList = null;
+ }
+ });
+
+ //noinspection JSUnresolvedVariable
+ UiScreen.on('screen-sm-down', {
+ enable: this._scrollEnable.bind(this, false),
+ disable: this._scrollDisable.bind(this),
+ setup: this._scrollEnable.bind(this, true)
+ });
+
+ window.addEventListener('hashchange', function () {
+ var hash = SimpleTabMenu.getIdentifierFromHash();
+ var element = (hash) ? elById(hash) : null;
+ if (element !== null && element.classList.contains('tabMenuContent')) {
+ _tabMenus.forEach(function (tabMenu) {
+ if (tabMenu.hasTab(hash)) {
+ tabMenu.select(hash);
+ }
+ });
+ }
+ });
+
+ var hash = SimpleTabMenu.getIdentifierFromHash();
+ if (hash) {
+ window.setTimeout(function () {
+ // check if page was initially scrolled using a tab id
+ var tabMenuContent = elById(hash);
+ if (tabMenuContent && tabMenuContent.classList.contains('tabMenuContent')) {
+ var scrollY = (window.scrollY || window.pageYOffset);
+ if (scrollY > 0) {
+ var parent = tabMenuContent.parentNode;
+ var offsetTop = parent.offsetTop - 50;
+ if (offsetTop < 0) offsetTop = 0;
+
+ if (scrollY > offsetTop) {
+ var y = DomUtil.offset(parent).top;
+
+ if (y <= 50) {
+ y = 0;
+ }
+ else {
+ y -= 50;
+ }
+
+ window.scrollTo(0, y);
+ }
+ }
+ }
+ }, 100);
+ }
+ },
+
+ /**
+ * Initializes available tab menus.
+ */
+ _init: function() {
+ var container, containerId, list, returnValue, tabMenu, tabMenus = elBySelAll('.tabMenuContainer:not(.staticTabMenuContainer)');
+ for (var i = 0, length = tabMenus.length; i < length; i++) {
+ container = tabMenus[i];
+ containerId = DomUtil.identify(container);
+
+ if (_tabMenus.has(containerId)) {
+ continue;
+ }
+
+ tabMenu = new SimpleTabMenu(container);
+ if (tabMenu.validate()) {
+ returnValue = tabMenu.init();
+
+ _tabMenus.set(containerId, tabMenu);
+
+ if (returnValue instanceof Element) {
+ tabMenu = this.getTabMenu(returnValue.parentNode.id);
+ tabMenu.select(returnValue.id, null, true);
+ }
+
+ list = elBySel('#' + containerId + ' > nav > ul');
+ (function(list) {
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (event.target === list) {
+ list.classList.add('active');
+
+ _activeList = list;
+ }
+ else {
+ list.classList.remove('active');
+
+ _activeList = null;
+ }
+ });
+ })(list);
+
+ // bind scroll listener
+ elBySelAll('.tabMenu, .menu', container, (function(menu) {
+ var callback = this._rebuildMenuOverflow.bind(this, menu);
+
+ var timeout = null;
+ elBySel('ul', menu).addEventListener('scroll', function () {
+ if (timeout !== null) {
+ window.clearTimeout(timeout);
+ }
+
+ // slight delay to avoid calling this function too often
+ timeout = window.setTimeout(callback, 10);
+ });
+ }).bind(this));
+ }
+ }
+ },
+
+ /**
+ * Selects the first tab containing an element with class `formError`.
+ */
+ _selectErroneousTabs: function() {
+ _tabMenus.forEach(function(tabMenu) {
+ var foundError = false;
+ tabMenu.getContainers().forEach(function(container) {
+ if (!foundError && elByClass('formError', container).length) {
+ foundError = true;
+
+ tabMenu.select(container.id);
+ }
+ });
+ });
+ },
+
+ /**
+ * Returns a SimpleTabMenu instance for given container id.
+ *
+ * @param {string} containerId tab menu container id
+ * @return {(SimpleTabMenu|undefined)} tab menu object
+ */
+ getTabMenu: function(containerId) {
+ return _tabMenus.get(containerId);
+ },
+
+ _scrollEnable: function (isSetup) {
+ _enableTabScroll = true;
+
+ _tabMenus.forEach((function (tabMenu) {
+ var activeTab = tabMenu.getActiveTab();
+ if (isSetup) {
+ this._rebuildMenuOverflow(activeTab.closest('.menu, .tabMenu'));
+ }
+ else {
+ this.scrollToTab(activeTab);
+ }
+ }).bind(this));
+ },
+
+ _scrollDisable: function () {
+ _enableTabScroll = false;
+ },
+
+ scrollToTab: function (tab) {
+ if (!_enableTabScroll) {
+ return;
+ }
+
+ var list = tab.closest('ul');
+ var width = list.clientWidth;
+ var scrollLeft = list.scrollLeft;
+ var scrollWidth = list.scrollWidth;
+ if (width === scrollWidth) {
+ // no overflow, ignore
+ return;
+ }
+
+ // check if tab is currently visible
+ var left = tab.offsetLeft;
+ var shouldScroll = false;
+ if (left < scrollLeft) {
+ shouldScroll = true;
+ }
+
+ var paddingRight = false;
+ if (!shouldScroll) {
+ var visibleWidth = width - (left - scrollLeft);
+ var virtualWidth = tab.clientWidth;
+ if (tab.nextElementSibling !== null) {
+ paddingRight = true;
+ virtualWidth += 20;
+ }
+
+ if (visibleWidth < virtualWidth) {
+ shouldScroll = true;
+ }
+ }
+
+ if (shouldScroll) {
+ this._scrollMenu(list, left, scrollLeft, scrollWidth, width, paddingRight);
+ }
+ },
+
+ _scrollMenu: function (list, left, scrollLeft, scrollWidth, width, paddingRight) {
+ // allow some padding to indicate overflow
+ if (paddingRight) {
+ left -= 15;
+ }
+ else if (left > 0) {
+ left -= 15;
+ }
+
+ if (left < 0) {
+ left = 0;
+ }
+ else {
+ // ensure that our left value is always within the boundaries
+ left = Math.min(left, scrollWidth - width);
+ }
+
+ if (scrollLeft === left) {
+ return;
+ }
+
+ list.classList.add('enableAnimation');
+
+ // new value is larger, we're scrolling towards the end
+ if (scrollLeft < left) {
+ list.firstElementChild.style.setProperty('margin-left', (scrollLeft - left) + 'px', '');
+ }
+ else {
+ // new value is smaller, we're scrolling towards the start
+ list.style.setProperty('padding-left', (scrollLeft - left) + 'px', '');
+ }
+
+ setTimeout(function () {
+ list.classList.remove('enableAnimation');
+
+ list.firstElementChild.style.removeProperty('margin-left');
+ list.style.removeProperty('padding-left');
+
+ list.scrollLeft = left;
+ }, 300);
+ },
+
+ _rebuildMenuOverflow: function (menu) {
+ if (!_enableTabScroll) {
+ return;
+ }
+
+ var width = menu.clientWidth;
+ var list = elBySel('ul', menu);
+ var scrollLeft = list.scrollLeft;
+ var scrollWidth = list.scrollWidth;
+
+ var overflowLeft = (scrollLeft > 0);
+ var overlayLeft = elBySel('.tabMenuOverlayLeft', menu);
+ if (overflowLeft) {
+ if (overlayLeft === null) {
+ overlayLeft = elCreate('span');
+ overlayLeft.className = 'tabMenuOverlayLeft icon icon24 fa-angle-left';
+ overlayLeft.addEventListener(WCF_CLICK_EVENT, (function () {
+ var listWidth = list.clientWidth;
+
+ this._scrollMenu(
+ list,
+ list.scrollLeft - ~~(listWidth / 2),
+ list.scrollLeft,
+ list.scrollWidth,
+ listWidth,
+ 0
+ );
+ }).bind(this));
+
+ menu.insertBefore(overlayLeft, menu.firstChild);
+ }
+
+ overlayLeft.classList.add('active');
+ }
+ else if (overlayLeft !== null) {
+ overlayLeft.classList.remove('active');
+ }
+
+ var overflowRight = (width + scrollLeft < scrollWidth);
+ var overlayRight = elBySel('.tabMenuOverlayRight', menu);
+ if (overflowRight) {
+ if (overlayRight === null) {
+ overlayRight = elCreate('span');
+ overlayRight.className = 'tabMenuOverlayRight icon icon24 fa-angle-right';
+ overlayRight.addEventListener(WCF_CLICK_EVENT, (function () {
+ var listWidth = list.clientWidth;
+
+ this._scrollMenu(
+ list,
+ list.scrollLeft + ~~(listWidth / 2),
+ list.scrollLeft,
+ list.scrollWidth,
+ listWidth,
+ 0
+ );
+ }).bind(this));
+
+ menu.appendChild(overlayRight);
+ }
+
+ overlayRight.classList.add('active');
+ }
+ else if (overlayRight !== null) {
+ overlayRight.classList.remove('active');
+ }
+ }
+ };
+});
+
+/**
+ * Dynamically transforms menu-like structures to handle items exceeding the available width
+ * by moving them into a separate dropdown.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/FlexibleMenu
+ */
+define('WoltLabSuite/Core/Ui/FlexibleMenu',['Core', 'Dictionary', 'Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'Ui/SimpleDropdown'], function(Core, Dictionary, DomChangeListener, DomTraverse, DomUtil, SimpleDropdown) {
+ "use strict";
+
+ var _containers = new Dictionary();
+ var _dropdowns = new Dictionary();
+ var _dropdownMenus = new Dictionary();
+ var _itemLists = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/FlexibleMenu
+ */
+ var UiFlexibleMenu = {
+ /**
+ * Register default menus and set up event listeners.
+ */
+ setup: function() {
+ if (elById('mainMenu') !== null) this.register('mainMenu');
+ var navigationHeader = elBySel('.navigationHeader');
+ if (navigationHeader !== null) this.register(DomUtil.identify(navigationHeader));
+
+ window.addEventListener('resize', this.rebuildAll.bind(this));
+ DomChangeListener.add('WoltLabSuite/Core/Ui/FlexibleMenu', this.registerTabMenus.bind(this));
+ },
+
+ /**
+ * Registers a menu by element id.
+ *
+ * @param {string} containerId element id
+ */
+ register: function(containerId) {
+ var container = elById(containerId);
+ if (container === null) {
+ throw "Expected a valid element id, '" + containerId + "' does not exist.";
+ }
+
+ if (_containers.has(containerId)) {
+ return;
+ }
+
+ var list = DomTraverse.childByTag(container, 'UL');
+ if (list === null) {
+ throw "Expected an <ul> element as child of container '" + containerId + "'.";
+ }
+
+ _containers.set(containerId, container);
+ _itemLists.set(containerId, list);
+
+ this.rebuild(containerId);
+ },
+
+ /**
+ * Registers tab menus.
+ */
+ registerTabMenus: function() {
+ var tabMenus = elBySelAll('.tabMenuContainer:not(.jsFlexibleMenuEnabled), .messageTabMenu:not(.jsFlexibleMenuEnabled)');
+ for (var i = 0, length = tabMenus.length; i < length; i++) {
+ var tabMenu = tabMenus[i];
+ var nav = DomTraverse.childByTag(tabMenu, 'NAV');
+ if (nav !== null) {
+ tabMenu.classList.add('jsFlexibleMenuEnabled');
+ this.register(DomUtil.identify(nav));
+ }
+ }
+ },
+
+ /**
+ * Rebuilds all menus, e.g. on window resize.
+ */
+ rebuildAll: function() {
+ _containers.forEach((function(container, containerId) {
+ this.rebuild(containerId);
+ }).bind(this));
+ },
+
+ /**
+ * Rebuild the menu identified by given element id.
+ *
+ * @param {string} containerId element id
+ */
+ rebuild: function(containerId) {
+ var container = _containers.get(containerId);
+ if (container === undefined) {
+ throw "Expected a valid element id, '" + containerId + "' is unknown.";
+ }
+
+ var styles = window.getComputedStyle(container);
+
+ var availableWidth = container.parentNode.clientWidth;
+ availableWidth -= DomUtil.styleAsInt(styles, 'margin-left');
+ availableWidth -= DomUtil.styleAsInt(styles, 'margin-right');
+
+ var list = _itemLists.get(containerId);
+ var items = DomTraverse.childrenByTag(list, 'LI');
+ var dropdown = _dropdowns.get(containerId);
+ var dropdownWidth = 0;
+ if (dropdown !== undefined) {
+ // show all items for calculation
+ for (var i = 0, length = items.length; i < length; i++) {
+ var item = items[i];
+ if (item.classList.contains('dropdown')) {
+ continue;
+ }
+
+ elShow(item);
+ }
+
+ if (dropdown.parentNode !== null) {
+ dropdownWidth = DomUtil.outerWidth(dropdown);
+ }
+ }
+
+ var currentWidth = list.scrollWidth - dropdownWidth;
+ var hiddenItems = [];
+ if (currentWidth > availableWidth) {
+ // hide items starting with the last one
+ for (var i = items.length - 1; i >= 0; i--) {
+ var item = items[i];
+
+ // ignore dropdown and active item
+ if (item.classList.contains('dropdown') || item.classList.contains('active') || item.classList.contains('ui-state-active')) {
+ continue;
+ }
+
+ hiddenItems.push(item);
+ elHide(item);
+
+ if (list.scrollWidth < availableWidth) {
+ break;
+ }
+ }
+ }
+
+ if (hiddenItems.length) {
+ var dropdownMenu;
+ if (dropdown === undefined) {
+ dropdown = elCreate('li');
+ dropdown.className = 'dropdown jsFlexibleMenuDropdown';
+ var icon = elCreate('a');
+ icon.className = 'icon icon16 fa-list';
+ dropdown.appendChild(icon);
+
+ dropdownMenu = elCreate('ul');
+ dropdownMenu.classList.add('dropdownMenu');
+ dropdown.appendChild(dropdownMenu);
+
+ _dropdowns.set(containerId, dropdown);
+ _dropdownMenus.set(containerId, dropdownMenu);
+
+ SimpleDropdown.init(icon);
+ }
+ else {
+ dropdownMenu = _dropdownMenus.get(containerId);
+ }
+
+ if (dropdown.parentNode === null) {
+ list.appendChild(dropdown);
+ }
+
+ // build dropdown menu
+ var fragment = document.createDocumentFragment();
+
+ var self = this;
+ hiddenItems.forEach(function(hiddenItem) {
+ var item = elCreate('li');
+ item.innerHTML = hiddenItem.innerHTML;
+
+ item.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+
+ Core.triggerEvent(elBySel('a', hiddenItem), WCF_CLICK_EVENT);
+
+ // force a rebuild to guarantee the active item being visible
+ setTimeout(function() {
+ self.rebuild(containerId);
+ }, 59);
+ }).bind(this));
+
+ fragment.appendChild(item);
+ });
+
+ dropdownMenu.innerHTML = '';
+ dropdownMenu.appendChild(fragment);
+ }
+ else if (dropdown !== undefined && dropdown.parentNode !== null) {
+ elRemove(dropdown);
+ }
+ }
+ };
+
+ return UiFlexibleMenu;
+});
+
+/**
+ * Provides enhanced tooltips.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Tooltip
+ */
+define('WoltLabSuite/Core/Ui/Tooltip',['Environment', 'Dom/ChangeListener', 'Ui/Alignment'], function(Environment, DomChangeListener, UiAlignment) {
+ "use strict";
+
+ var _callbackMouseEnter = null;
+ var _callbackMouseLeave = null;
+ var _elements = null;
+ var _pointer = null;
+ var _text = null;
+ var _tooltip = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Tooltip
+ */
+ return {
+ /**
+ * Initializes the tooltip element and binds event listener.
+ */
+ setup: function() {
+ if (Environment.platform() !== 'desktop') return;
+
+ _tooltip = elCreate('div');
+ elAttr(_tooltip, 'id', 'balloonTooltip');
+ _tooltip.classList.add('balloonTooltip');
+ _tooltip.addEventListener('transitionend', function () {
+ if (!_tooltip.classList.contains('active')) {
+ // reset back to the upper left corner, prevent it from staying outside
+ // the viewport if the body overflow was previously hidden
+ ['bottom', 'left', 'right', 'top'].forEach(function(property) {
+ _tooltip.style.removeProperty(property);
+ });
+ }
+ });
+
+ _text = elCreate('span');
+ elAttr(_text, 'id', 'balloonTooltipText');
+ _tooltip.appendChild(_text);
+
+ _pointer = elCreate('span');
+ _pointer.classList.add('elementPointer');
+ _pointer.appendChild(elCreate('span'));
+ _tooltip.appendChild(_pointer);
+
+ document.body.appendChild(_tooltip);
+
+ _elements = elByClass('jsTooltip');
+
+ _callbackMouseEnter = this._mouseEnter.bind(this);
+ _callbackMouseLeave = this._mouseLeave.bind(this);
+
+ this.init();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Tooltip', this.init.bind(this));
+ window.addEventListener('scroll', this._mouseLeave.bind(this));
+ },
+
+ /**
+ * Initializes tooltip elements.
+ */
+ init: function() {
+ if (_elements.length === 0) {
+ return;
+ }
+
+ elBySelAll('.jsTooltip', undefined, function (element) {
+ element.classList.remove('jsTooltip');
+
+ var title = elAttr(element, 'title').trim();
+ if (title.length) {
+ elData(element, 'tooltip', title);
+ element.removeAttribute('title');
+ elAttr(element, 'aria-label', title);
+
+ element.addEventListener('mouseenter', _callbackMouseEnter);
+ element.addEventListener('mouseleave', _callbackMouseLeave);
+ element.addEventListener(WCF_CLICK_EVENT, _callbackMouseLeave);
+ }
+ });
+ },
+
+ /**
+ * Displays the tooltip on mouse enter.
+ *
+ * @param {Event} event event object
+ */
+ _mouseEnter: function(event) {
+ var element = event.currentTarget;
+ var title = elAttr(element, 'title');
+ title = (typeof title === 'string') ? title.trim() : '';
+
+ if (title !== '') {
+ elData(element, 'tooltip', title);
+ element.removeAttribute('title');
+ }
+
+ title = elData(element, 'tooltip');
+
+ // reset tooltip position
+ _tooltip.style.removeProperty('top');
+ _tooltip.style.removeProperty('left');
+
+ // ignore empty tooltip
+ if (!title.length) {
+ _tooltip.classList.remove('active');
+ return;
+ }
+ else {
+ _tooltip.classList.add('active');
+ }
+
+ _text.textContent = title;
+
+ UiAlignment.set(_tooltip, element, {
+ horizontal: 'center',
+ verticalOffset: 4,
+ pointer: true,
+ pointerClassNames: ['inverse'],
+ vertical: 'top'
+ });
+ },
+
+ /**
+ * Hides the tooltip once the mouse leaves the element.
+ */
+ _mouseLeave: function() {
+ _tooltip.classList.remove('active');
+ }
+ };
+});
+
+/**
+ * Date picker with time support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Date/Picker
+ */
+define('WoltLabSuite/Core/Date/Picker',['DateUtil', 'Dom/Traverse', 'Dom/Util', 'EventHandler', 'Language', 'ObjectMap', 'Dom/ChangeListener', 'Ui/Alignment', 'WoltLabSuite/Core/Ui/CloseOverlay'], function(DateUtil, DomTraverse, DomUtil, EventHandler, Language, ObjectMap, DomChangeListener, UiAlignment, UiCloseOverlay) {
+ "use strict";
+
+ var _didInit = false;
+ var _firstDayOfWeek = 0;
+ var _wasInsidePicker = false;
+
+ var _data = new ObjectMap();
+ var _input = null;
+ var _maxDate = 0;
+ var _minDate = 0;
+
+ var _dateCells = [];
+ var _dateGrid = null;
+ var _dateHour = null;
+ var _dateMinute = null;
+ var _dateMonth = null;
+ var _dateMonthNext = null;
+ var _dateMonthPrevious = null;
+ var _dateTime = null;
+ var _dateYear = null;
+ var _datePicker = null;
+
+ var _callbackOpen = null;
+ var _callbackFocus = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Date/Picker
+ */
+ var DatePicker = {
+ /**
+ * Initializes all date and datetime input fields.
+ */
+ init: function() {
+ this._setup();
+
+ var elements = elBySelAll('input[type="date"]:not(.inputDatePicker), input[type="datetime"]:not(.inputDatePicker)');
+ var now = new Date();
+ for (var i = 0, length = elements.length; i < length; i++) {
+ var element = elements[i];
+ element.classList.add('inputDatePicker');
+ element.readOnly = true;
+
+ var isDateTime = (elAttr(element, 'type') === 'datetime');
+ var isTimeOnly = (isDateTime && elDataBool(element, 'time-only'));
+ var disableClear = elDataBool(element, 'disable-clear');
+ var ignoreTimezone = isDateTime && elDataBool(element, 'ignore-timezone');
+ var isBirthday = element.classList.contains('birthday');
+
+ elData(element, 'is-date-time', isDateTime);
+ elData(element, 'is-time-only', isTimeOnly);
+
+ // convert value
+ var date = null, value = elAttr(element, 'value');
+
+ // ignore the timezone, if the value is only a date (YYYY-MM-DD)
+ var isDateOnly = /^\d+-\d+-\d+$/.test(value);
+
+ if (elAttr(element, 'value')) {
+ if (isTimeOnly) {
+ date = new Date();
+ var tmp = value.split(':');
+ date.setHours(tmp[0], tmp[1]);
+ }
+ else {
+ if (ignoreTimezone || isBirthday || isDateOnly) {
+ var timezoneOffset = new Date(value).getTimezoneOffset();
+ var timezone = (timezoneOffset > 0) ? '-' : '+'; // -120 equals GMT+0200
+ timezoneOffset = Math.abs(timezoneOffset);
+ var hours = (Math.floor(timezoneOffset / 60)).toString();
+ var minutes = (timezoneOffset % 60).toString();
+ timezone += (hours.length === 2) ? hours : '0' + hours;
+ timezone += ':';
+ timezone += (minutes.length === 2) ? minutes : '0' + minutes;
+
+ if (isBirthday || isDateOnly) {
+ value += 'T00:00:00' + timezone;
+ }
+ else {
+ value = value.replace(/[+-][0-9]{2}:[0-9]{2}$/, timezone);
+ }
+ }
+
+ date = new Date(value);
+ }
+
+ var time = date.getTime();
+
+ // check for invalid dates
+ if (isNaN(time)) {
+ value = '';
+ }
+ else {
+ elData(element, 'value', time);
+ var format = (isTimeOnly) ? 'formatTime' : ('formatDate' + (isDateTime ? 'Time' : ''));
+ value = DateUtil[format](date);
+ }
+ }
+
+ var isEmpty = (value.length === 0);
+
+ // handle birthday input
+ if (isBirthday) {
+ elData(element, 'min-date', '120');
+
+ // do not use 'now' here, all though it makes sense, it causes bad UX
+ elData(element, 'max-date', new Date().getFullYear() + '-12-31');
+ }
+ else {
+ if (element.min) elData(element, 'min-date', element.min);
+ if (element.max) elData(element, 'max-date', element.max);
+ }
+
+ this._initDateRange(element, now, true);
+ this._initDateRange(element, now, false);
+
+ if (elData(element, 'min-date') === elData(element, 'max-date')) {
+ throw new Error("Minimum and maximum date cannot be the same (element id '" + element.id + "').");
+ }
+
+ // change type to prevent browser's datepicker to trigger
+ element.type = 'text';
+ element.value = value;
+ elData(element, 'empty', isEmpty);
+
+ if (elData(element, 'placeholder')) {
+ elAttr(element, 'placeholder', elData(element, 'placeholder'));
+ }
+
+ // add a hidden element to hold the actual date
+ var shadowElement = elCreate('input');
+ shadowElement.id = element.id + 'DatePicker';
+ shadowElement.name = element.name;
+ shadowElement.type = 'hidden';
+
+ if (date !== null) {
+ if (isTimeOnly) {
+ shadowElement.value = DateUtil.format(date, 'H:i');
+ }
+ else if (ignoreTimezone) {
+ shadowElement.value = DateUtil.format(date, 'Y-m-dTH:i:s');
+ }
+ else {
+ shadowElement.value = DateUtil.format(date, (isDateTime) ? 'c' : 'Y-m-d');
+ }
+ }
+
+ element.parentNode.insertBefore(shadowElement, element);
+ element.removeAttribute('name');
+
+ element.addEventListener(WCF_CLICK_EVENT, _callbackOpen);
+
+ if (!element.disabled) {
+ // create input addon
+ var container = elCreate('div');
+ container.className = 'inputAddon';
+
+ var button = elCreate('a');
+
+ button.className = 'inputSuffix button jsTooltip';
+ button.href = '#';
+ elAttr(button, 'role', 'button');
+ elAttr(button, 'tabindex', '0');
+ elAttr(button, 'title', Language.get('wcf.date.datePicker'));
+ elAttr(button, 'aria-label', Language.get('wcf.date.datePicker'));
+ elAttr(button, 'aria-haspopup', true);
+ elAttr(button, 'aria-expanded', false);
+ button.addEventListener(WCF_CLICK_EVENT, _callbackOpen);
+ container.appendChild(button);
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-calendar';
+ button.appendChild(icon);
+
+ element.parentNode.insertBefore(container, element);
+ container.insertBefore(element, button);
+
+ if (!disableClear) {
+ button = elCreate('a');
+ button.className = 'inputSuffix button';
+ button.addEventListener(WCF_CLICK_EVENT, this.clear.bind(this, element));
+ if (isEmpty) button.style.setProperty('visibility', 'hidden', '');
+
+ container.appendChild(button);
+
+ icon = elCreate('span');
+ icon.className = 'icon icon16 fa-times';
+ button.appendChild(icon);
+ }
+ }
+
+ // check if the date input has one of the following classes set otherwise default to 'short'
+ var hasClass = false, knownClasses = ['tiny', 'short', 'medium', 'long'];
+ for (var j = 0; j < 4; j++) {
+ if (element.classList.contains(knownClasses[j])) {
+ hasClass = true;
+ }
+ }
+
+ if (!hasClass) {
+ element.classList.add('short');
+ }
+
+ _data.set(element, {
+ clearButton: button,
+ shadow: shadowElement,
+
+ disableClear: disableClear,
+ isDateTime: isDateTime,
+ isEmpty: isEmpty,
+ isTimeOnly: isTimeOnly,
+ ignoreTimezone: ignoreTimezone,
+
+ onClose: null
+ });
+ }
+ },
+
+ /**
+ * Initializes the minimum/maximum date range.
+ *
+ * @param {Element} element input element
+ * @param {Date} now current date
+ * @param {boolean} isMinDate true for the minimum date
+ */
+ _initDateRange: function(element, now, isMinDate) {
+ var attribute = 'data-' + (isMinDate ? 'min' : 'max') + '-date';
+ var value = (element.hasAttribute(attribute)) ? elAttr(element, attribute).trim() : '';
+
+ if (value.match(/^(\d{4})-(\d{2})-(\d{2})$/)) {
+ // YYYY-mm-dd
+ value = new Date(value).getTime();
+ }
+ else if (value === 'now') {
+ value = now.getTime();
+ }
+ else if (value.match(/^\d{1,3}$/)) {
+ // relative time span in years
+ var date = new Date(now.getTime());
+ date.setFullYear(date.getFullYear() + ~~value * (isMinDate ? -1 : 1));
+
+ value = date.getTime();
+ }
+ else if (value.match(/^datePicker-(.+)$/)) {
+ // element id, e.g. `datePicker-someOtherElement`
+ value = RegExp.$1;
+
+ if (elById(value) === null) {
+ throw new Error("Reference date picker identified by '" + value + "' does not exists (element id: '" + element.id + "').");
+ }
+ }
+ else if (/^\d{4}\-\d{2}\-\d{2}T/.test(value)) {
+ value = new Date(value).getTime();
+ }
+ else {
+ value = new Date((isMinDate ? 1902 : 2038), 0, 1).getTime();
+ }
+
+ elAttr(element, attribute, value);
+ },
+
+ /**
+ * Sets up callbacks and event listeners.
+ */
+ _setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _firstDayOfWeek = ~~Language.get('wcf.date.firstDayOfTheWeek');
+ _callbackOpen = this._open.bind(this);
+
+ DomChangeListener.add('WoltLabSuite/Core/Date/Picker', this.init.bind(this));
+ UiCloseOverlay.add('WoltLabSuite/Core/Date/Picker', this._close.bind(this));
+ },
+
+ /**
+ * Opens the date picker.
+ *
+ * @param {object} event event object
+ */
+ _open: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this._createPicker();
+
+ if (_callbackFocus === null) {
+ _callbackFocus = this._maintainFocus.bind(this);
+ document.body.addEventListener('focus', _callbackFocus, { capture: true });
+ }
+
+ var input = (event.currentTarget.nodeName === 'INPUT') ? event.currentTarget : event.currentTarget.previousElementSibling;
+ if (input === _input) {
+ this._close();
+ return;
+ }
+
+ var dialogContent = DomTraverse.parentByClass(input, 'dialogContent');
+ if (dialogContent !== null) {
+ if (!elDataBool(dialogContent, 'has-datepicker-scroll-listener')) {
+ dialogContent.addEventListener('scroll', this._onDialogScroll.bind(this));
+ elData(dialogContent, 'has-datepicker-scroll-listener', 1);
+ }
+ }
+
+ _input = input;
+ var data = _data.get(_input), date, value = elData(_input, 'value');
+ if (value) {
+ date = new Date(+value);
+
+ if (date.toString() === 'Invalid Date') {
+ date = new Date();
+ }
+ }
+ else {
+ date = new Date();
+ }
+
+ // set min/max date
+ _minDate = elData(_input, 'min-date');
+ if (_minDate.match(/^datePicker-(.+)$/)) _minDate = elData(elById(RegExp.$1), 'value');
+ _minDate = new Date(+_minDate);
+
+ _maxDate = elData(_input, 'max-date');
+ if (_maxDate.match(/^datePicker-(.+)$/)) _maxDate = elData(elById(RegExp.$1), 'value');
+ _maxDate = new Date(+_maxDate);
+
+ if (data.isDateTime) {
+ _dateHour.value = date.getHours();
+ _dateMinute.value = date.getMinutes();
+
+ _datePicker.classList.add('datePickerTime');
+ }
+ else {
+ _datePicker.classList.remove('datePickerTime');
+ }
+
+ _datePicker.classList[(data.isTimeOnly) ? 'add' : 'remove']('datePickerTimeOnly');
+
+ this._renderPicker(date.getDate(), date.getMonth(), date.getFullYear());
+
+ UiAlignment.set(_datePicker, _input);
+
+ elAttr(_input.nextElementSibling, 'aria-expanded', true);
+
+ _wasInsidePicker = false;
+ },
+
+ /**
+ * Closes the date picker.
+ */
+ _close: function() {
+ if (_datePicker !== null && _datePicker.classList.contains('active')) {
+ _datePicker.classList.remove('active');
+
+ var data = _data.get(_input);
+ if (typeof data.onClose === 'function') {
+ data.onClose();
+ }
+
+ EventHandler.fire('WoltLabSuite/Core/Date/Picker', 'close', {element: _input});
+
+ elAttr(_input.nextElementSibling, 'aria-expanded', false);
+ _input = null;
+ _minDate = 0;
+ _maxDate = 0;
+ }
+ },
+
+ /**
+ * Updates the position of the date picker in a dialog if the dialog content
+ * is scrolled.
+ *
+ * @param {Event} event scroll event
+ */
+ _onDialogScroll: function(event) {
+ if (_input === null) {
+ return;
+ }
+
+ var dialogContent = event.currentTarget;
+
+ var offset = DomUtil.offset(_input);
+ var dialogOffset = DomUtil.offset(dialogContent);
+
+ // check if date picker input field is still (partially) visible
+ if (offset.top + _input.clientHeight <= dialogOffset.top) {
+ // top check
+ this._close();
+ }
+ else if (offset.top >= dialogOffset.top + dialogContent.offsetHeight) {
+ // bottom check
+ this._close();
+ }
+ else if (offset.left <= dialogOffset.left) {
+ // left check
+ this._close();
+ }
+ else if (offset.left >= dialogOffset.left + dialogContent.offsetWidth) {
+ // right check
+ this._close();
+ }
+ else {
+ UiAlignment.set(_datePicker, _input);
+ }
+ },
+
+ /**
+ * Renders the full picker on init.
+ *
+ * @param {int} day
+ * @param {int} month
+ * @param {int} year
+ */
+ _renderPicker: function(day, month, year) {
+ this._renderGrid(day, month, year);
+
+ // create options for month and year
+ var years = '';
+ for (var i = _minDate.getFullYear(), last = _maxDate.getFullYear(); i <= last; i++) {
+ years += '<option value="' + i + '">' + i + '</option>';
+ }
+ _dateYear.innerHTML = years;
+ _dateYear.value = year;
+
+ _dateMonth.value = month;
+
+ _datePicker.classList.add('active');
+ },
+
+ /**
+ * Updates the date grid.
+ *
+ * @param {int} day
+ * @param {int} month
+ * @param {int} year
+ */
+ _renderGrid: function(day, month, year) {
+ var cell, hasDay = (day !== undefined), hasMonth = (month !== undefined), i;
+
+ day = ~~day || ~~elData(_dateGrid, 'day');
+ month = ~~month;
+ year = ~~year;
+
+ // rebuild cells
+ if (hasMonth || year) {
+ var rebuildMonths = (year !== 0);
+
+ // rebuild grid
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild(_dateGrid);
+
+ if (!hasMonth) month = ~~elData(_dateGrid, 'month');
+ year = year || ~~elData(_dateGrid, 'year');
+
+ // check if current selection exceeds min/max date
+ var date = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-' + ('0' + day.toString()).slice(-2));
+ if (date < _minDate) {
+ year = _minDate.getFullYear();
+ month = _minDate.getMonth();
+ day = _minDate.getDate();
+
+ _dateMonth.value = month;
+ _dateYear.value = year;
+
+ rebuildMonths = true;
+ }
+ else if (date > _maxDate) {
+ year = _maxDate.getFullYear();
+ month = _maxDate.getMonth();
+ day = _maxDate.getDate();
+
+ _dateMonth.value = month;
+ _dateYear.value = year;
+
+ rebuildMonths = true;
+ }
+
+ date = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-01');
+
+ // shift until first displayed day equals first day of week
+ while (date.getDay() !== _firstDayOfWeek) {
+ date.setDate(date.getDate() - 1);
+ }
+
+ // show the last row
+ elShow(_dateCells[35].parentNode);
+
+ var selectable;
+ var comparableMinDate = new Date(_minDate.getFullYear(), _minDate.getMonth(), _minDate.getDate());
+ for (i = 0; i < 42; i++) {
+ if (i === 35 && date.getMonth() !== month) {
+ // skip the last row if it only contains the next month
+ elHide(_dateCells[35].parentNode);
+
+ break;
+ }
+
+ cell = _dateCells[i];
+
+ cell.textContent = date.getDate();
+ selectable = (date.getMonth() === month);
+ if (selectable) {
+ if (date < comparableMinDate) selectable = false;
+ else if (date > _maxDate) selectable = false;
+ }
+
+ cell.classList[selectable ? 'remove' : 'add']('otherMonth');
+ if (selectable) {
+ cell.href = '#';
+ elAttr(cell, 'role', 'button');
+ elAttr(cell, 'tabindex', '0');
+ elAttr(cell, 'title', DateUtil.formatDate(date));
+ elAttr(cell, 'aria-label', DateUtil.formatDate(date));
+ }
+
+ date.setDate(date.getDate() + 1);
+ }
+
+ elData(_dateGrid, 'month', month);
+ elData(_dateGrid, 'year', year);
+
+ _datePicker.insertBefore(fragment, _dateTime);
+
+ if (!hasDay) {
+ // check if date is valid
+ date = new Date(year, month, day);
+ if (date.getDate() !== day) {
+ while (date.getMonth() !== month) {
+ date.setDate(date.getDate() - 1);
+ }
+
+ day = date.getDate();
+ }
+ }
+
+ if (rebuildMonths) {
+ for (i = 0; i < 12; i++) {
+ var currentMonth = _dateMonth.children[i];
+
+ currentMonth.disabled = (year === _minDate.getFullYear() && currentMonth.value < _minDate.getMonth()) || (year === _maxDate.getFullYear() && currentMonth.value > _maxDate.getMonth());
+ }
+
+ var nextMonth = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-01');
+ nextMonth.setMonth(nextMonth.getMonth() + 1);
+
+ _dateMonthNext.classList[(nextMonth < _maxDate) ? 'add' : 'remove']('active');
+
+ var previousMonth = new Date(year + '-' + ('0' + (month + 1).toString()).slice(-2) + '-01');
+ previousMonth.setDate(previousMonth.getDate() - 1);
+
+ _dateMonthPrevious.classList[(previousMonth > _minDate) ? 'add' : 'remove']('active');
+ }
+ }
+
+ // update active day
+ if (day) {
+ for (i = 0; i < 35; i++) {
+ cell = _dateCells[i];
+
+ cell.classList[(!cell.classList.contains('otherMonth') && ~~cell.textContent === day) ? 'add' : 'remove']('active');
+ }
+
+ elData(_dateGrid, 'day', day);
+ }
+
+ this._formatValue();
+ },
+
+ /**
+ * Sets the visible and shadow value
+ */
+ _formatValue: function() {
+ var data = _data.get(_input), date;
+
+ if (elData(_input, 'empty') === 'true') {
+ return;
+ }
+
+ if (data.isDateTime) {
+ date = new Date(
+ elData(_dateGrid, 'year'),
+ elData(_dateGrid, 'month'),
+ elData(_dateGrid, 'day'),
+ _dateHour.value,
+ _dateMinute.value
+ );
+ }
+ else {
+ date = new Date(
+ elData(_dateGrid, 'year'),
+ elData(_dateGrid, 'month'),
+ elData(_dateGrid, 'day')
+ );
+ }
+
+ this.setDate(_input, date);
+ },
+
+ /**
+ * Creates the date picker DOM.
+ */
+ _createPicker: function() {
+ if (_datePicker !== null) {
+ return;
+ }
+
+ _datePicker = elCreate('div');
+ _datePicker.className = 'datePicker';
+ _datePicker.addEventListener(WCF_CLICK_EVENT, function(event) { event.stopPropagation(); });
+
+ var header = elCreate('header');
+ _datePicker.appendChild(header);
+
+ _dateMonthPrevious = elCreate('a');
+ _dateMonthPrevious.className = 'previous jsTooltip';
+ _dateMonthPrevious.href = '#';
+ elAttr(_dateMonthPrevious, 'role', 'button');
+ elAttr(_dateMonthPrevious, 'tabindex', '0');
+ elAttr(_dateMonthPrevious, 'title', Language.get('wcf.date.datePicker.previousMonth'));
+ elAttr(_dateMonthPrevious, 'aria-label', Language.get('wcf.date.datePicker.previousMonth'));
+ _dateMonthPrevious.innerHTML = '<span class="icon icon16 fa-arrow-left"></span>';
+ _dateMonthPrevious.addEventListener(WCF_CLICK_EVENT, this.previousMonth.bind(this));
+ header.appendChild(_dateMonthPrevious);
+
+ var monthYearContainer = elCreate('span');
+ header.appendChild(monthYearContainer);
+
+ _dateMonth = elCreate('select');
+ _dateMonth.className = 'month jsTooltip';
+ elAttr(_dateMonth, 'title', Language.get('wcf.date.datePicker.month'));
+ elAttr(_dateMonth, 'aria-label', Language.get('wcf.date.datePicker.month'));
+ _dateMonth.addEventListener('change', this._changeMonth.bind(this));
+ monthYearContainer.appendChild(_dateMonth);
+
+ var i, months = '', monthNames = Language.get('__monthsShort');
+ for (i = 0; i < 12; i++) {
+ months += '<option value="' + i + '">' + monthNames[i] + '</option>';
+ }
+ _dateMonth.innerHTML = months;
+
+ _dateYear = elCreate('select');
+ _dateYear.className = 'year jsTooltip';
+ elAttr(_dateYear, 'title', Language.get('wcf.date.datePicker.year'));
+ elAttr(_dateYear, 'aria-label', Language.get('wcf.date.datePicker.year'));
+ _dateYear.addEventListener('change', this._changeYear.bind(this));
+ monthYearContainer.appendChild(_dateYear);
+
+ _dateMonthNext = elCreate('a');
+ _dateMonthNext.className = 'next jsTooltip';
+ _dateMonthNext.href = '#';
+ elAttr(_dateMonthNext, 'role', 'button');
+ elAttr(_dateMonthNext, 'tabindex', '0');
+ elAttr(_dateMonthNext, 'title', Language.get('wcf.date.datePicker.nextMonth'));
+ elAttr(_dateMonthNext, 'aria-label', Language.get('wcf.date.datePicker.nextMonth'));
+ _dateMonthNext.innerHTML = '<span class="icon icon16 fa-arrow-right"></span>';
+ _dateMonthNext.addEventListener(WCF_CLICK_EVENT, this.nextMonth.bind(this));
+ header.appendChild(_dateMonthNext);
+
+ _dateGrid = elCreate('ul');
+ _datePicker.appendChild(_dateGrid);
+
+ var item = elCreate('li');
+ item.className = 'weekdays';
+ _dateGrid.appendChild(item);
+
+ var span, weekdays = Language.get('__daysShort');
+ for (i = 0; i < 7; i++) {
+ var day = i + _firstDayOfWeek;
+ if (day > 6) day -= 7;
+
+ span = elCreate('span');
+ span.textContent = weekdays[day];
+ item.appendChild(span);
+ }
+
+ // create date grid
+ var callbackClick = this._click.bind(this), cell, row;
+ for (i = 0; i < 6; i++) {
+ row = elCreate('li');
+ _dateGrid.appendChild(row);
+
+ for (var j = 0; j < 7; j++) {
+ cell = elCreate('a');
+ cell.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ _dateCells.push(cell);
+
+ row.appendChild(cell);
+ }
+ }
+
+ _dateTime = elCreate('footer');
+ _datePicker.appendChild(_dateTime);
+
+ _dateHour = elCreate('select');
+ _dateHour.className = 'hour';
+ elAttr(_dateHour, 'title', Language.get('wcf.date.datePicker.hour'));
+ elAttr(_dateHour, 'aria-label', Language.get('wcf.date.datePicker.hour'));
+ _dateHour.addEventListener('change', this._formatValue.bind(this));
+
+ var tmp = '';
+ var date = new Date(2000, 0, 1);
+ var timeFormat = Language.get('wcf.date.timeFormat').replace(/:/, '').replace(/[isu]/g, '');
+ for (i = 0; i < 24; i++) {
+ date.setHours(i);
+ tmp += '<option value="' + i + '">' + DateUtil.format(date, timeFormat) + "</option>";
+ }
+ _dateHour.innerHTML = tmp;
+
+ _dateTime.appendChild(_dateHour);
+
+ _dateTime.appendChild(document.createTextNode('\u00A0:\u00A0'));
+
+ _dateMinute = elCreate('select');
+ _dateMinute.className = 'minute';
+ elAttr(_dateMinute, 'title', Language.get('wcf.date.datePicker.minute'));
+ elAttr(_dateMinute, 'aria-label', Language.get('wcf.date.datePicker.minute'));
+ _dateMinute.addEventListener('change', this._formatValue.bind(this));
+
+ tmp = '';
+ for (i = 0; i < 60; i++) {
+ tmp += '<option value="' + i + '">' + (i < 10 ? '0' + i.toString() : i) + '</option>';
+ }
+ _dateMinute.innerHTML = tmp;
+
+ _dateTime.appendChild(_dateMinute);
+
+ document.body.appendChild(_datePicker);
+ },
+
+ /**
+ * Shows the previous month.
+ */
+ previousMonth: function(event) {
+ event.preventDefault();
+
+ if (_dateMonth.value === '0') {
+ _dateMonth.value = 11;
+ _dateYear.value = ~~_dateYear.value - 1;
+ }
+ else {
+ _dateMonth.value = ~~_dateMonth.value - 1;
+ }
+
+ this._renderGrid(undefined, _dateMonth.value, _dateYear.value);
+ },
+
+ /**
+ * Shows the next month.
+ */
+ nextMonth: function(event) {
+ event.preventDefault();
+
+ if (_dateMonth.value === '11') {
+ _dateMonth.value = 0;
+ _dateYear.value = ~~_dateYear.value + 1;
+ }
+ else {
+ _dateMonth.value = ~~_dateMonth.value + 1;
+ }
+
+ this._renderGrid(undefined, _dateMonth.value, _dateYear.value);
+ },
+
+ /**
+ * Handles changes to the month select element.
+ *
+ * @param {object} event event object
+ */
+ _changeMonth: function(event) {
+ this._renderGrid(undefined, event.currentTarget.value);
+ },
+
+ /**
+ * Handles changes to the year select element.
+ *
+ * @param {object} event event object
+ */
+ _changeYear: function(event) {
+ this._renderGrid(undefined, undefined, event.currentTarget.value);
+ },
+
+ /**
+ * Handles clicks on an individual day.
+ *
+ * @param {object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ if (event.currentTarget.classList.contains('otherMonth')) {
+ return;
+ }
+
+ elData(_input, 'empty', false);
+
+ this._renderGrid(event.currentTarget.textContent);
+
+ var data = _data.get(_input);
+ if (!data.isDateTime) {
+ this._close();
+ }
+ },
+
+ /**
+ * Returns the current Date object or null.
+ *
+ * @param {(Element|string)} element input element or id
+ * @return {?Date} Date object or null
+ */
+ getDate: function(element) {
+ element = this._getElement(element);
+
+ if (element.hasAttribute('data-value')) {
+ return new Date(+elData(element, 'value'));
+ }
+
+ return null;
+ },
+
+ /**
+ * Sets the date of given element.
+ *
+ * @param {(HTMLInputElement|string)} element input element or id
+ * @param {Date} date Date object
+ */
+ setDate: function(element, date) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ elData(element, 'value', date.getTime());
+
+ var format = '', value;
+ if (data.isDateTime) {
+ if (data.isTimeOnly) {
+ value = DateUtil.formatTime(date);
+ format = 'H:i';
+ }
+ else if (data.ignoreTimezone) {
+ value = DateUtil.formatDateTime(date);
+ format = 'Y-m-dTH:i:s';
+ }
+ else {
+ value = DateUtil.formatDateTime(date);
+ format = 'c';
+ }
+ }
+ else {
+ value = DateUtil.formatDate(date);
+ format = 'Y-m-d';
+ }
+
+ element.value = value;
+ data.shadow.value = DateUtil.format(date, format);
+
+ // show clear button
+ if (!data.disableClear) {
+ data.clearButton.style.removeProperty('visibility');
+ }
+ },
+
+ /**
+ * Returns the current value.
+ *
+ * @param {(Element|string)} element input element or id
+ * @return {string} current date value
+ */
+ getValue: function (element) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ if (data) {
+ return data.shadow.value;
+ }
+
+ return '';
+ },
+
+ /**
+ * Clears the date value of given element.
+ *
+ * @param {(HTMLInputElement|string)} element input element or id
+ */
+ clear: function(element) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ element.removeAttribute('data-value');
+ element.value = '';
+
+ if (!data.disableClear) data.clearButton.style.setProperty('visibility', 'hidden', '');
+ data.isEmpty = true;
+ data.shadow.value = '';
+ },
+
+ /**
+ * Reverts the date picker into a normal input field.
+ *
+ * @param {(HTMLInputElement|string)} element input element or id
+ */
+ destroy: function(element) {
+ element = this._getElement(element);
+ var data = _data.get(element);
+
+ var container = element.parentNode;
+ container.parentNode.insertBefore(element, container);
+ elRemove(container);
+
+ elAttr(element, 'type', 'date' + (data.isDateTime ? 'time' : ''));
+ element.name = data.shadow.name;
+ element.value = data.shadow.value;
+
+ element.removeAttribute('data-value');
+ element.removeEventListener(WCF_CLICK_EVENT, _callbackOpen);
+ elRemove(data.shadow);
+
+ element.classList.remove('inputDatePicker');
+ element.readOnly = false;
+ _data['delete'](element);
+ },
+
+ /**
+ * Sets the callback invoked on picker close.
+ *
+ * @param {(Element|string)} element input element or id
+ * @param {function} callback callback function
+ */
+ setCloseCallback: function(element, callback) {
+ element = this._getElement(element);
+ _data.get(element).onClose = callback;
+ },
+
+ /**
+ * Validates given element or id if it represents an active date picker.
+ *
+ * @param {(Element|string)} element input element or id
+ * @return {Element} input element
+ */
+ _getElement: function(element) {
+ if (typeof element === 'string') element = elById(element);
+
+ if (!(element instanceof Element) || !element.classList.contains('inputDatePicker') || !_data.has(element)) {
+ throw new Error("Expected a valid date picker input element or id.");
+ }
+
+ return element;
+ },
+
+ /**
+ * @param {Event} event
+ */
+ _maintainFocus: function(event) {
+ if (_datePicker !== null && _datePicker.classList.contains('active')) {
+ if (!_datePicker.contains(event.target)) {
+ if (_wasInsidePicker) {
+ _input.nextElementSibling.focus();
+ _wasInsidePicker = false;
+ }
+ else {
+ elBySel('.previous', _datePicker).focus();
+ }
+ }
+ else {
+ _wasInsidePicker = true;
+ }
+ }
+ }
+ };
+
+ // backward-compatibility for `$.ui.datepicker` shim
+ window.__wcf_bc_datePicker = DatePicker;
+
+ return DatePicker;
+});
+
+/**
+ * Provides page actions such as "jump to top" and clipboard actions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Action
+ */
+define('WoltLabSuite/Core/Ui/Page/Action',['Dictionary', 'Dom/Util'], function(Dictionary, DomUtil) {
+ "use strict";
+
+ var _buttons = new Dictionary();
+ var _container = null;
+ var _didInit = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Action
+ */
+ return {
+ /**
+ * Initializes the page action container.
+ */
+ setup: function() {
+ _didInit = true;
+
+ _container = elCreate('ul');
+ _container.className = 'pageAction';
+ document.body.appendChild(_container);
+ },
+
+ /**
+ * Adds a button to the page action list. You can optionally provide a button name to
+ * insert the button right before it. Unmatched button names or empty value will cause
+ * the button to be prepended to the list.
+ *
+ * @param {string} buttonName unique identifier
+ * @param {Element} button button element, must not be wrapped in a <li>
+ * @param {string=} insertBeforeButton insert button before element identified by provided button name
+ */
+ add: function(buttonName, button, insertBeforeButton) {
+ if (_didInit === false) this.setup();
+
+ var listItem = elCreate('li');
+ button.classList.add('button');
+ button.classList.add('buttonPrimary');
+ listItem.appendChild(button);
+ elAttr(listItem, 'aria-hidden', (buttonName === 'toTop' ? 'true' : 'false'));
+ elData(listItem, 'name', buttonName);
+
+ // force 'to top' button to be always at the most outer position
+ if (buttonName === 'toTop') {
+ listItem.className = 'toTop initiallyHidden';
+ _container.appendChild(listItem);
+ }
+ else {
+ var insertBefore = null;
+ if (insertBeforeButton) {
+ insertBefore = _buttons.get(insertBeforeButton);
+ if (insertBefore !== undefined) {
+ insertBefore = insertBefore.parentNode;
+ }
+ }
+
+ if (insertBefore === null && _container.childElementCount) {
+ insertBefore = _container.children[0];
+ }
+
+ if (insertBefore === null) {
+ DomUtil.prepend(listItem, _container);
+ }
+ else {
+ _container.insertBefore(listItem, insertBefore);
+ }
+ }
+
+ _buttons.set(buttonName, button);
+ this._renderContainer();
+ },
+
+ /**
+ * Returns true if there is a registered button with the provided name.
+ *
+ * @param {string} buttonName unique identifier
+ * @return {boolean} true if there is a registered button with this name
+ */
+ has: function (buttonName) {
+ return _buttons.has(buttonName);
+ },
+
+ /**
+ * Returns the stored button by name or undefined.
+ *
+ * @param {string} buttonName unique identifier
+ * @return {Element} button element or undefined
+ */
+ get: function(buttonName) {
+ return _buttons.get(buttonName);
+ },
+
+ /**
+ * Removes a button by its button name.
+ *
+ * @param {string} buttonName unique identifier
+ */
+ remove: function(buttonName) {
+ var button = _buttons.get(buttonName);
+ if (button !== undefined) {
+ var listItem = button.parentNode;
+ listItem.addEventListener('animationend', function () {
+ try {
+ _container.removeChild(listItem);
+ _buttons.delete(buttonName);
+ }
+ catch (e) {
+ // ignore errors if the element has already been removed
+ }
+ });
+
+ this.hide(buttonName);
+ }
+ },
+
+ /**
+ * Hides a button by its button name.
+ *
+ * @param {string} buttonName unique identifier
+ */
+ hide: function(buttonName) {
+ var button = _buttons.get(buttonName);
+ if (button) {
+ elAttr(button.parentNode, 'aria-hidden', 'true');
+ this._renderContainer();
+ }
+ },
+
+ /**
+ * Shows a button by its button name.
+ *
+ * @param {string} buttonName unique identifier
+ */
+ show: function(buttonName) {
+ var button = _buttons.get(buttonName);
+ if (button) {
+ if (button.parentNode.classList.contains('initiallyHidden')) {
+ button.parentNode.classList.remove('initiallyHidden');
+ }
+
+ elAttr(button.parentNode, 'aria-hidden', 'false');
+ this._renderContainer();
+ }
+ },
+
+ /**
+ * Toggles the container's visibility.
+ *
+ * @protected
+ */
+ _renderContainer: function() {
+ var hasVisibleItems = false;
+ if (_container.childElementCount) {
+ for (var i = 0, length = _container.childElementCount; i < length; i++) {
+ if (elAttr(_container.children[i], 'aria-hidden') === 'false') {
+ hasVisibleItems = true;
+ break;
+ }
+ }
+ }
+
+ _container.classList[(hasVisibleItems ? 'add' : 'remove')]('active');
+ }
+ };
+});
+
+/**
+ * Provides a link to scroll to top once the page is scrolled by at least 50% the height of the window.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/JumpToTop
+ */
+define('WoltLabSuite/Core/Ui/Page/JumpToTop',['Environment', 'Language', './Action'], function(Environment, Language, PageAction) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function JumpToTop() { this.init(); }
+ JumpToTop.prototype = {
+ /**
+ * Initializes the top link for desktop browsers only.
+ */
+ init: function() {
+ // top link is not available on smartphones and tablets (they have a built-in function to accomplish this)
+ if (Environment.platform() !== 'desktop') {
+ return;
+ }
+
+ this._callbackScrollEnd = this._afterScroll.bind(this);
+ this._timeoutScroll = null;
+
+ var button = elCreate('a');
+ button.className = 'jsTooltip';
+ button.href = '#';
+ elAttr(button, 'title', Language.get('wcf.global.scrollUp'));
+ elAttr(button, 'role', 'button');
+ button.innerHTML = '<span class="icon icon32 fa-angle-up"></span>';
+
+ button.addEventListener(WCF_CLICK_EVENT, this._jump.bind(this));
+
+ PageAction.add('toTop', button);
+
+ window.addEventListener('scroll', this._scroll.bind(this));
+
+ // invoke callback on page load
+ this._afterScroll();
+ },
+
+ /**
+ * Handles clicks on the top link.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _jump: function(event) {
+ event.preventDefault();
+
+ elById('top').scrollIntoView({ behavior: 'smooth' });
+ },
+
+ /**
+ * Callback executed whenever the window is being scrolled.
+ *
+ * @protected
+ */
+ _scroll: function() {
+ if (this._timeoutScroll !== null) {
+ window.clearTimeout(this._timeoutScroll);
+ }
+
+ this._timeoutScroll = window.setTimeout(this._callbackScrollEnd, 100);
+ },
+
+ /**
+ * Delayed callback executed once the page has not been scrolled for a certain amount of time.
+ *
+ * @protected
+ */
+ _afterScroll: function() {
+ this._timeoutScroll = null;
+
+ PageAction[(window.pageYOffset >= 300) ? 'show' : 'hide']('toTop');
+ }
+ };
+
+ return JumpToTop;
+});
+
+/**
+ * Bootstraps WCF's JavaScript.
+ * It defines globals needed for backwards compatibility
+ * and runs modules that are needed on page load.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Bootstrap
+ */
+define(
+ 'WoltLabSuite/Core/Bootstrap',[
+ 'favico', 'enquire', 'perfect-scrollbar', 'WoltLabSuite/Core/Date/Time/Relative',
+ 'Ui/SimpleDropdown', 'WoltLabSuite/Core/Ui/Mobile', 'WoltLabSuite/Core/Ui/TabMenu', 'WoltLabSuite/Core/Ui/FlexibleMenu',
+ 'Ui/Dialog', 'WoltLabSuite/Core/Ui/Tooltip', 'WoltLabSuite/Core/Language', 'WoltLabSuite/Core/Environment',
+ 'WoltLabSuite/Core/Date/Picker', 'EventHandler', 'Core', 'WoltLabSuite/Core/Ui/Page/JumpToTop',
+ 'Devtools', 'Dom/ChangeListener'
+ ],
+ function(
+ favico, enquire, perfectScrollbar, DateTimeRelative,
+ UiSimpleDropdown, UiMobile, UiTabMenu, UiFlexibleMenu,
+ UiDialog, UiTooltip, Language, Environment,
+ DatePicker, EventHandler, Core, UiPageJumpToTop,
+ Devtools, DomChangeListener
+ )
+{
+ "use strict";
+
+ // perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js
+ window.Favico = favico;
+ window.enquire = enquire;
+ // non strict equals by intent
+ if (window.WCF == null) window.WCF = { };
+ if (window.WCF.Language == null) window.WCF.Language = { };
+ window.WCF.Language.get = Language.get;
+ window.WCF.Language.add = Language.add;
+ window.WCF.Language.addObject = Language.addObject;
+
+ // WCF.System.Event compatibility
+ window.__wcf_bc_eventHandler = EventHandler;
+
+ /**
+ * @exports WoltLabSuite/Core/Bootstrap
+ */
+ return {
+ /**
+ * Initializes the core UI modifications and unblocks jQuery's ready event.
+ *
+ * @param {Object=} options initialization options
+ */
+ setup: function(options) {
+ options = Core.extend({
+ enableMobileMenu: true
+ }, options);
+
+ //noinspection JSUnresolvedVariable
+ if (window.ENABLE_DEVELOPER_TOOLS) Devtools._internal_.enable();
+
+ Environment.setup();
+
+ DateTimeRelative.setup();
+ DatePicker.init();
+
+ UiSimpleDropdown.setup();
+ UiMobile.setup({
+ enableMobileMenu: options.enableMobileMenu
+ });
+ UiTabMenu.setup();
+ //UiFlexibleMenu.setup();
+ UiDialog.setup();
+ UiTooltip.setup();
+
+ // convert method=get into method=post
+ var forms = elBySelAll('form[method=get]');
+ for (var i = 0, length = forms.length; i < length; i++) {
+ forms[i].setAttribute('method', 'post');
+ }
+
+ if (Environment.browser() === 'microsoft') {
+ window.onbeforeunload = function() {
+ /* Prevent "Back navigation caching" (http://msdn.microsoft.com/en-us/library/ie/dn265017%28v=vs.85%29.aspx) */
+ };
+ }
+
+ var interval = 0;
+ interval = window.setInterval(function() {
+ if (typeof window.jQuery === 'function') {
+ window.clearInterval(interval);
+
+ // the 'jump to top' button triggers style recalculation/layout,
+ // putting it at the end of the jQuery queue avoids trashing the
+ // layout too early and thus delaying the page initialization
+ window.jQuery(function() {
+ new UiPageJumpToTop();
+ });
+
+ window.jQuery.holdReady(false);
+ }
+ }, 20);
+
+ this._initA11y();
+ DomChangeListener.add('WoltLabSuite/Core/Bootstrap', this._initA11y.bind(this));
+ },
+
+ _initA11y: function() {
+ elBySelAll('nav:not([aria-label]):not([aria-labelledby]):not([role])', undefined, function(element) {
+ elAttr(element, 'role', 'presentation');
+ });
+
+ elBySelAll('article:not([aria-label]):not([aria-labelledby]):not([role])', undefined, function(element) {
+ elAttr(element, 'role', 'presentation');
+ });
+ }
+ };
+});
+
+/**
+ * Dialog based style changer.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Style/Changer
+ */
+define('WoltLabSuite/Core/Controller/Style/Changer',['Ajax', 'Language', 'Ui/Dialog'], function(Ajax, Language, UiDialog) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Style/Changer
+ */
+ return {
+ /**
+ * Adds the style changer to the bottom navigation.
+ */
+ setup: function() {
+ var link = elBySel('.jsButtonStyleChanger');
+ if (link) {
+ link.addEventListener(WCF_CLICK_EVENT, this.showDialog.bind(this));
+ }
+ },
+
+ /**
+ * Loads and displays the style change dialog.
+ *
+ * @param {object} event event object
+ */
+ showDialog: function(event) {
+ event.preventDefault();
+
+ UiDialog.open(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'styleChanger',
+ options: {
+ disableContentPadding: true,
+ title: Language.get('wcf.style.changeStyle')
+ },
+ source: {
+ data: {
+ actionName: 'getStyleChooser',
+ className: 'wcf\\data\\style\\StyleAction'
+ },
+ after: (function(content) {
+ var styles = elBySelAll('.styleList > li', content);
+ for (var i = 0, length = styles.length; i < length; i++) {
+ var style = styles[i];
+
+ style.classList.add('pointer');
+ style.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }
+ }).bind(this)
+ }
+ };
+ },
+
+ /**
+ * Changes the style and reloads current page.
+ *
+ * @param {object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ Ajax.apiOnce({
+ data: {
+ actionName: 'changeStyle',
+ className: 'wcf\\data\\style\\StyleAction',
+ objectIDs: [ elData(event.currentTarget, 'style-id') ]
+ },
+ success: function() { window.location.reload(); }
+ });
+ }
+ };
+});
+
+/**
+ * Versatile popover manager.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Popover
+ */
+define('WoltLabSuite/Core/Controller/Popover',['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', 'Ui/Alignment'], function(Ajax, Dictionary, Environment, DomChangeListener, DomUtil, UiAlignment) {
+ "use strict";
+
+ var _activeId = null;
+ var _cache = new Dictionary();
+ var _elements = new Dictionary();
+ var _handlers = new Dictionary();
+ var _hoverId = null;
+ var _suspended = false;
+ var _timeoutEnter = null;
+ var _timeoutLeave = null;
+
+ var _popover = null;
+ var _popoverContent = null;
+
+ var _callbackClick = null;
+ var _callbackHide = null;
+ var _callbackMouseEnter = null;
+ var _callbackMouseLeave = null;
+
+ /** @const */ var STATE_NONE = 0;
+ /** @const */ var STATE_LOADING = 1;
+ /** @const */ var STATE_READY = 2;
+
+ /** @const */ var DELAY_HIDE = 500;
+ /** @const */ var DELAY_SHOW = 800;
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Popover
+ */
+ return {
+ /**
+ * Builds popover DOM elements and binds event listeners.
+ */
+ _setup: function() {
+ if (_popover !== null) {
+ return;
+ }
+
+ _popover = elCreate('div');
+ _popover.className = 'popover forceHide';
+
+ _popoverContent = elCreate('div');
+ _popoverContent.className = 'popoverContent';
+ _popover.appendChild(_popoverContent);
+
+ var pointer = elCreate('span');
+ pointer.className = 'elementPointer';
+ pointer.appendChild(elCreate('span'));
+ _popover.appendChild(pointer);
+
+ document.body.appendChild(_popover);
+
+ // static binding for callbacks (they don't change anyway and binding each time is expensive)
+ _callbackClick = this._hide.bind(this);
+ _callbackMouseEnter = this._mouseEnter.bind(this);
+ _callbackMouseLeave = this._mouseLeave.bind(this);
+
+ // event listener
+ _popover.addEventListener('mouseenter', this._popoverMouseEnter.bind(this));
+ _popover.addEventListener('mouseleave', _callbackMouseLeave);
+
+ _popover.addEventListener('animationend', this._clearContent.bind(this));
+
+ window.addEventListener('beforeunload', (function() {
+ _suspended = true;
+
+ if (_timeoutEnter !== null) {
+ window.clearTimeout(_timeoutEnter);
+ }
+
+ this._hide(true);
+ }).bind(this));
+
+ DomChangeListener.add('WoltLabSuite/Core/Controller/Popover', this._init.bind(this));
+ },
+
+ /**
+ * Initializes a popover handler.
+ *
+ * Usage:
+ *
+ * ControllerPopover.init({
+ * attributeName: 'data-object-id',
+ * className: 'fooLink',
+ * identifier: 'com.example.bar.foo',
+ * loadCallback: function(objectId, popover) {
+ * // request data for object id (e.g. via WoltLabSuite/Core/Ajax)
+ *
+ * // then call this to set the content
+ * popover.setContent('com.example.bar.foo', objectId, htmlTemplateString);
+ * }
+ * });
+ *
+ * @param {Object} options handler options
+ */
+ init: function(options) {
+ if (Environment.platform() !== 'desktop') {
+ return;
+ }
+
+ options.attributeName = options.attributeName || 'data-object-id';
+ options.legacy = (options.legacy === true);
+
+ this._setup();
+
+ if (_handlers.has(options.identifier)) {
+ return;
+ }
+
+ _handlers.set(options.identifier, {
+ attributeName: options.attributeName,
+ elements: options.legacy ? options.className : elByClass(options.className),
+ legacy: options.legacy,
+ loadCallback: options.loadCallback
+ });
+
+ this._init(options.identifier);
+ },
+
+ /**
+ * Initializes a popover handler.
+ *
+ * @param {string} identifier handler identifier
+ */
+ _init: function(identifier) {
+ if (typeof identifier === 'string' && identifier.length) {
+ this._initElements(_handlers.get(identifier), identifier);
+ }
+ else {
+ _handlers.forEach(this._initElements.bind(this));
+ }
+ },
+
+ /**
+ * Binds event listeners for popover-enabled elements.
+ *
+ * @param {Object} options handler options
+ * @param {string} identifier handler identifier
+ */
+ _initElements: function(options, identifier) {
+ var elements = options.legacy ? elBySelAll(options.elements) : options.elements;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ var element = elements[i];
+
+ var id = DomUtil.identify(element);
+ if (_cache.has(id)) {
+ return;
+ }
+ // skip if element is in a popover
+ if (element.closest('.popover') !== null) {
+ _cache.set(id, {
+ content: null,
+ state: STATE_NONE
+ });
+ return;
+ }
+
+ var objectId = (options.legacy) ? id : ~~element.getAttribute(options.attributeName);
+ if (objectId === 0) {
+ continue;
+ }
+
+ element.addEventListener('mouseenter', _callbackMouseEnter);
+ element.addEventListener('mouseleave', _callbackMouseLeave);
+
+ if (element.nodeName === 'A' && elAttr(element, 'href')) {
+ element.addEventListener(WCF_CLICK_EVENT, _callbackClick);
+ }
+
+ var cacheId = identifier + "-" + objectId;
+ elData(element, 'cache-id', cacheId);
+
+ _elements.set(id, {
+ element: element,
+ identifier: identifier,
+ objectId: objectId
+ });
+
+ if (!_cache.has(cacheId)) {
+ _cache.set(identifier + "-" + objectId, {
+ content: null,
+ state: STATE_NONE
+ });
+ }
+ }
+ },
+
+ /**
+ * Sets the content for given identifier and object id.
+ *
+ * @param {string} identifier handler identifier
+ * @param {int} objectId object id
+ * @param {string} content HTML string
+ */
+ setContent: function(identifier, objectId, content) {
+ var cacheId = identifier + "-" + objectId;
+ var data = _cache.get(cacheId);
+ if (data === undefined) {
+ throw new Error("Unable to find element for object id '" + objectId + "' (identifier: '" + identifier + "').");
+ }
+
+ var fragment = DomUtil.createFragmentFromHtml(content);
+ if (!fragment.childElementCount) fragment = DomUtil.createFragmentFromHtml('<p>' + content + '</p>');
+ data.content = fragment;
+ data.state = STATE_READY;
+
+ if (_activeId) {
+ var activeElement = _elements.get(_activeId).element;
+
+ if (elData(activeElement, 'cache-id') === cacheId) {
+ this._show();
+ }
+ }
+ },
+
+ /**
+ * Handles the mouse start hovering the popover-enabled element.
+ *
+ * @param {object} event event object
+ */
+ _mouseEnter: function(event) {
+ if (_suspended) {
+ return;
+ }
+
+ if (_timeoutEnter !== null) {
+ window.clearTimeout(_timeoutEnter);
+ _timeoutEnter = null;
+ }
+
+ var id = DomUtil.identify(event.currentTarget);
+ if (_activeId === id && _timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+
+ _hoverId = id;
+
+ _timeoutEnter = window.setTimeout((function() {
+ _timeoutEnter = null;
+
+ if (_hoverId === id) {
+ this._show();
+ }
+ }).bind(this), DELAY_SHOW);
+ },
+
+ /**
+ * Handles the mouse leaving the popover-enabled element or the popover itself.
+ */
+ _mouseLeave: function() {
+ _hoverId = null;
+
+ if (_timeoutLeave !== null) {
+ return;
+ }
+
+ if (_callbackHide === null) {
+ _callbackHide = this._hide.bind(this);
+ }
+
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ }
+
+ _timeoutLeave = window.setTimeout(_callbackHide, DELAY_HIDE);
+ },
+
+ /**
+ * Handles the mouse start hovering the popover element.
+ */
+ _popoverMouseEnter: function() {
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+ },
+
+ /**
+ * Shows the popover and loads content on-the-fly.
+ */
+ _show: function() {
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+
+ var forceHide = false;
+ if (_popover.classList.contains('active')) {
+ if (_activeId !== _hoverId) {
+ this._hide();
+
+ forceHide = true;
+ }
+ }
+ else if (_popoverContent.childElementCount) {
+ forceHide = true;
+ }
+
+ if (forceHide) {
+ _popover.classList.add('forceHide');
+
+ // force layout
+ //noinspection BadExpressionStatementJS
+ _popover.offsetTop;
+
+ this._clearContent();
+
+ _popover.classList.remove('forceHide');
+ }
+
+ _activeId = _hoverId;
+
+ var elementData = _elements.get(_activeId);
+ // check if source element is already gone
+ if (elementData === undefined) {
+ return;
+ }
+
+ var data = _cache.get(elData(elementData.element, 'cache-id'));
+
+ if (data.state === STATE_READY) {
+ _popoverContent.appendChild(data.content);
+
+ this._rebuild(_activeId);
+ }
+ else if (data.state === STATE_NONE) {
+ data.state = STATE_LOADING;
+
+ _handlers.get(elementData.identifier).loadCallback(elementData.objectId, this);
+ }
+ },
+
+ /**
+ * Hides the popover element.
+ */
+ _hide: function() {
+ if (_timeoutLeave !== null) {
+ window.clearTimeout(_timeoutLeave);
+ _timeoutLeave = null;
+ }
+
+ _popover.classList.remove('active');
+ },
+
+ /**
+ * Clears popover content by moving it back into the cache.
+ */
+ _clearContent: function() {
+ if (_activeId && _popoverContent.childElementCount && !_popover.classList.contains('active')) {
+ var activeElData = _cache.get(elData(_elements.get(_activeId).element, 'cache-id'));
+ while (_popoverContent.childNodes.length) {
+ activeElData.content.appendChild(_popoverContent.childNodes[0]);
+ }
+ }
+ },
+
+ /**
+ * Rebuilds the popover.
+ */
+ _rebuild: function() {
+ if (_popover.classList.contains('active')) {
+ return;
+ }
+
+ _popover.classList.remove('forceHide');
+ _popover.classList.add('active');
+
+ UiAlignment.set(_popover, _elements.get(_activeId).element, {
+ pointer: true,
+ vertical: 'top'
+ });
+ },
+
+ _ajaxSetup: function() {
+ return {
+ silent: true
+ };
+ },
+
+ /**
+ * Sends an AJAX requests to the server, simple wrapper to reuse the request object.
+ *
+ * @param {Object} data request data
+ * @param {function} success success callback
+ * @param {function=} failure error callback
+ */
+ ajaxApi: function(data, success, failure) {
+ if (typeof success !== 'function') {
+ throw new TypeError("Expected a valid callback for parameter 'success'.");
+ }
+
+ Ajax.api(this, data, success, failure);
+ }
+ };
+});
+
+/**
+ * Provides global helper methods to interact with ignored content.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Ignore
+ */
+define('WoltLabSuite/Core/Ui/User/Ignore',['List', 'Dom/ChangeListener'], function(List, DomChangeListener) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _rebuild: function() {},
+ _removeClass: function() {}
+ };
+ return Fake;
+ }
+
+ var _availableMessages = elByClass('ignoredUserMessage');
+ var _callback = null;
+ var _knownMessages = new List();
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/User/Ignore
+ */
+ return {
+ /**
+ * Initializes the click handler for each ignored message and listens for
+ * newly inserted messages.
+ */
+ init: function () {
+ _callback = this._removeClass.bind(this);
+
+ this._rebuild();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/User/Ignore', this._rebuild.bind(this));
+ },
+
+ /**
+ * Adds ignored messages to the collection.
+ *
+ * @protected
+ */
+ _rebuild: function() {
+ var message;
+ for (var i = 0, length = _availableMessages.length; i < length; i++) {
+ message = _availableMessages[i];
+
+ if (!_knownMessages.has(message)) {
+ message.addEventListener(WCF_CLICK_EVENT, _callback);
+
+ _knownMessages.add(message);
+ }
+ }
+ },
+
+ /**
+ * Reveals a message on click/tap and disables the listener.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _removeClass: function(event) {
+ event.preventDefault();
+
+ var message = event.currentTarget;
+ message.classList.remove('ignoredUserMessage');
+ message.removeEventListener(WCF_CLICK_EVENT, _callback);
+ _knownMessages.delete(message);
+
+ // Firefox selects the entire message on click for no reason
+ window.getSelection().removeAllRanges();
+ }
+ };
+});
+
+/**
+ * Handles main menu overflow and a11y.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Header/Menu
+ */
+define('WoltLabSuite/Core/Ui/Page/Header/Menu',['Environment', 'Language', 'Ui/Screen'], function(Environment, Language, UiScreen) {
+ "use strict";
+
+ var _enabled = false;
+
+ // elements
+ var _buttonShowNext, _buttonShowPrevious, _firstElement, _menu;
+
+ // internal states
+ var _marginLeft = 0, _invisibleLeft = [], _invisibleRight = [];
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Header/Menu
+ */
+ return {
+ /**
+ * Initializes the main menu overflow handling.
+ */
+ init: function () {
+ _menu = elBySel('.mainMenu .boxMenu');
+ _firstElement = (_menu && _menu.childElementCount) ? _menu.children[0] : null;
+ if (_firstElement === null) {
+ throw new Error("Unable to find the menu.");
+ }
+
+ UiScreen.on('screen-lg', {
+ enable: this._enable.bind(this),
+ disable: this._disable.bind(this),
+ setup: this._setup.bind(this)
+ });
+ },
+
+ /**
+ * Enables the overflow handler.
+ *
+ * @protected
+ */
+ _enable: function () {
+ _enabled = true;
+
+ // Safari waits three seconds for a font to be loaded which causes the header menu items
+ // to be extremely wide while waiting for the font to be loaded. The extremely wide menu
+ // items in turn can cause the overflow controls to be shown even if the width of the header
+ // menu, after the font has been loaded successfully, does not require them. This width
+ // issue results in the next button being shown for a short time. To circumvent this issue,
+ // we wait a second before showing the obverflow controls in Safari.
+ // see https://webkit.org/blog/6643/improved-font-loading/
+ if (Environment.browser() === 'safari') {
+ window.setTimeout(this._rebuildVisibility.bind(this), 1000);
+ }
+ else {
+ this._rebuildVisibility();
+
+ // IE11 sometimes suffers from a timing issue
+ window.setTimeout(this._rebuildVisibility.bind(this), 1000);
+ }
+ },
+
+ /**
+ * Disables the overflow handler.
+ *
+ * @protected
+ */
+ _disable: function () {
+ _enabled = false;
+ },
+
+ /**
+ * Displays the next three menu items.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _showNext: function(event) {
+ event.preventDefault();
+
+ if (_invisibleRight.length) {
+ var showItem = _invisibleRight.slice(0, 3).pop();
+ this._setMarginLeft(_menu.clientWidth - (showItem.offsetLeft + showItem.clientWidth));
+
+ if (_menu.lastElementChild === showItem) {
+ _buttonShowNext.classList.remove('active');
+ }
+
+ _buttonShowPrevious.classList.add('active');
+ }
+ },
+
+ /**
+ * Displays the previous three menu items.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _showPrevious: function (event) {
+ event.preventDefault();
+
+ if (_invisibleLeft.length) {
+ var showItem = _invisibleLeft.slice(-3)[0];
+ this._setMarginLeft(showItem.offsetLeft * -1);
+
+ if (_menu.firstElementChild === showItem) {
+ _buttonShowPrevious.classList.remove('active');
+ }
+
+ _buttonShowNext.classList.add('active');
+ }
+ },
+
+ /**
+ * Sets the first item's margin-left value that is
+ * used to move the menu contents around.
+ *
+ * @param {int} offset changes to the margin-left value in pixel
+ * @protected
+ */
+ _setMarginLeft: function (offset) {
+ _marginLeft = Math.min(_marginLeft + offset, 0);
+
+ _firstElement.style.setProperty('margin-left', _marginLeft + 'px', '');
+ },
+
+ /**
+ * Toggles button overlays and rebuilds the list
+ * of invisible items from left to right.
+ *
+ * @protected
+ */
+ _rebuildVisibility: function () {
+ if (!_enabled) return;
+
+ _invisibleLeft = [];
+ _invisibleRight = [];
+
+ var menuWidth = _menu.clientWidth;
+ if (_menu.scrollWidth > menuWidth || _marginLeft < 0) {
+ var child;
+ for (var i = 0, length = _menu.childElementCount; i < length; i++) {
+ child = _menu.children[i];
+
+ var offsetLeft = child.offsetLeft;
+ if (offsetLeft < 0) {
+ _invisibleLeft.push(child);
+ }
+ else if (offsetLeft + child.clientWidth > menuWidth) {
+ _invisibleRight.push(child);
+ }
+ }
+ }
+
+ _buttonShowPrevious.classList[(_invisibleLeft.length ? 'add' : 'remove')]('active');
+ _buttonShowNext.classList[(_invisibleRight.length ? 'add' : 'remove')]('active');
+ },
+
+ /**
+ * Builds the UI and binds the event listeners.
+ *
+ * @protected
+ */
+ _setup: function () {
+ this._setupOverflow();
+ this._setupA11y();
+ },
+
+ /**
+ * Setups overflow handling.
+ *
+ * @protected
+ */
+ _setupOverflow: function () {
+ _buttonShowNext = elCreate('a');
+ _buttonShowNext.className = 'mainMenuShowNext';
+ _buttonShowNext.href = '#';
+ _buttonShowNext.innerHTML = '<span class="icon icon32 fa-angle-right"></span>';
+ _buttonShowNext.addEventListener(WCF_CLICK_EVENT, this._showNext.bind(this));
+
+ _menu.parentNode.appendChild(_buttonShowNext);
+
+ _buttonShowPrevious = elCreate('a');
+ _buttonShowPrevious.className = 'mainMenuShowPrevious';
+ _buttonShowPrevious.href = '#';
+ _buttonShowPrevious.innerHTML = '<span class="icon icon32 fa-angle-left"></span>';
+ _buttonShowPrevious.addEventListener(WCF_CLICK_EVENT, this._showPrevious.bind(this));
+
+ _menu.parentNode.insertBefore(_buttonShowPrevious, _menu.parentNode.firstChild);
+
+ var rebuildVisibility = this._rebuildVisibility.bind(this);
+ _firstElement.addEventListener('transitionend', rebuildVisibility);
+
+ window.addEventListener('resize', function () {
+ _firstElement.style.setProperty('margin-left', '0px', '');
+ _marginLeft = 0;
+
+ rebuildVisibility();
+ });
+
+ this._enable();
+ },
+
+ /**
+ * Setups a11y improvements.
+ *
+ * @protected
+ */
+ _setupA11y: function() {
+ elBySelAll('.boxMenuHasChildren', _menu, (function(element) {
+ var showMenu = false;
+ var link = elBySel('.boxMenuLink', element);
+ if (link) {
+ elAttr(link, 'aria-haspopup', true);
+ elAttr(link, 'aria-expanded', showMenu);
+ }
+
+ var showMenuButton = elCreate('button');
+ showMenuButton.className = 'visuallyHidden';
+ showMenuButton.tabindex = 0;
+ elAttr(showMenuButton, 'role', 'button');
+ elAttr(showMenuButton, 'aria-label', Language.get('wcf.global.button.showMenu'));
+ element.insertBefore(showMenuButton, link.nextSibling);
+
+ showMenuButton.addEventListener(WCF_CLICK_EVENT, function() {
+ showMenu = !showMenu;
+ elAttr(link, 'aria-expanded', showMenu);
+ elAttr(showMenuButton, 'aria-label', (showMenu ? Language.get('wcf.global.button.hideMenu') : Language.get('wcf.global.button.showMenu')));
+ });
+ }).bind(this));
+ }
+ };
+});
+
+/**
+ * Bootstraps WCF's JavaScript with additions for the frontend usage.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/BootstrapFrontend
+ */
+define(
+ 'WoltLabSuite/Core/BootstrapFrontend',[
+ 'WoltLabSuite/Core/BackgroundQueue', 'WoltLabSuite/Core/Bootstrap', 'WoltLabSuite/Core/Controller/Style/Changer',
+ 'WoltLabSuite/Core/Controller/Popover', 'WoltLabSuite/Core/Ui/User/Ignore', 'WoltLabSuite/Core/Ui/Page/Header/Menu'
+ ],
+ function(
+ BackgroundQueue, Bootstrap, ControllerStyleChanger,
+ ControllerPopover, UiUserIgnore, UiPageHeaderMenu
+ )
+{
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/BootstrapFrontend
+ */
+ return {
+ /**
+ * Bootstraps general modules and frontend exclusive ones.
+ *
+ * @param {object<string, *>} options bootstrap options
+ */
+ setup: function(options) {
+ // fix the background queue URL to always run against the current domain (avoiding CORS)
+ options.backgroundQueue.url = WSC_API_URL + options.backgroundQueue.url.substr(WCF_PATH.length);
+
+ Bootstrap.setup();
+
+ UiPageHeaderMenu.init();
+
+ if (options.styleChanger) {
+ ControllerStyleChanger.setup();
+ }
+
+ if (options.enableUserPopover) {
+ this._initUserPopover();
+ }
+
+ BackgroundQueue.setUrl(options.backgroundQueue.url);
+ if (Math.random() < 0.1 || options.backgroundQueue.force) {
+ // invoke the queue roughly every 10th request or on demand
+ BackgroundQueue.invoke();
+ }
+
+ if (COMPILER_TARGET_DEFAULT) {
+ UiUserIgnore.init();
+ }
+ },
+
+ /**
+ * Initializes user profile popover.
+ */
+ _initUserPopover: function() {
+ ControllerPopover.init({
+ attributeName: 'data-user-id',
+ className: 'userLink',
+ identifier: 'com.woltlab.wcf.user',
+ loadCallback: function(objectId, popover) {
+ var callback = function(data) {
+ popover.setContent('com.woltlab.wcf.user', objectId, data.returnValues.template);
+ };
+
+ popover.ajaxApi({
+ actionName: 'getUserProfile',
+ className: 'wcf\\data\\user\\UserProfileAction',
+ objectIDs: [ objectId ]
+ }, callback, callback);
+ }
+ });
+ }
+ };
+});
+
+/**
+ * Wrapper around the web browser's various clipboard APIs.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Clipboard
+ */
+define('WoltLabSuite/Core/Clipboard',[], function() {
+ "use strict";
+
+ return {
+ copyTextToClipboard: function (text) {
+ if (navigator.clipboard) {
+ return navigator.clipboard.writeText(text);
+ }
+ else if (window.getSelection) {
+ var textarea = elCreate('textarea');
+ textarea.contentEditable = true;
+ textarea.readOnly = false;
+ textarea.style.cssText = 'position: absolute; left: -9999px; top: -9999px; width: 0; height: 0;';
+ document.body.appendChild(textarea);
+ try {
+ // see: https://stackoverflow.com/a/34046084/782822
+ textarea.value = text;
+ var range = document.createRange();
+ range.selectNodeContents(textarea);
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ textarea.setSelectionRange(0, 999999);
+ if (!document.execCommand('copy')) {
+ return Promise.reject(new Error("execCommand('copy') failed"));
+ }
+ return Promise.resolve();
+ }
+ finally {
+ elRemove(textarea);
+ }
+ }
+
+ return Promise.reject(new Error('Neither navigator.clipboard, nor window.getSelection is supported.'));
+ },
+
+ copyElementTextToClipboard: function (element) {
+ return this.copyTextToClipboard(element.textContent);
+ }
+ };
+});
+
+/**
+ * Helper functions to convert between different color formats.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/ColorUtil
+ */
+define('WoltLabSuite/Core/ColorUtil',[], function () {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/ColorUtil
+ */
+ var ColorUtil = {
+ /**
+ * Converts a HSV color into RGB.
+ *
+ * @see https://secure.wikimedia.org/wikipedia/de/wiki/HSV-Farbraum#Transformation_von_RGB_und_HSV
+ *
+ * @param {int} h
+ * @param {int} s
+ * @param {int} v
+ * @return {Object}
+ */
+ hsvToRgb: function(h, s, v) {
+ var rgb = { r: 0, g: 0, b: 0 };
+ var h2, f, p, q, t;
+
+ h2 = Math.floor(h / 60);
+ f = h / 60 - h2;
+
+ s /= 100;
+ v /= 100;
+
+ p = v * (1 - s);
+ q = v * (1 - s * f);
+ t = v * (1 - s * (1 - f));
+
+ if (s == 0) {
+ rgb.r = rgb.g = rgb.b = v;
+ }
+ else {
+ switch (h2) {
+ case 1:
+ rgb.r = q;
+ rgb.g = v;
+ rgb.b = p;
+ break;
+
+ case 2:
+ rgb.r = p;
+ rgb.g = v;
+ rgb.b = t;
+ break;
+
+ case 3:
+ rgb.r = p;
+ rgb.g = q;
+ rgb.b = v;
+ break;
+
+ case 4:
+ rgb.r = t;
+ rgb.g = p;
+ rgb.b = v;
+ break;
+
+ case 5:
+ rgb.r = v;
+ rgb.g = p;
+ rgb.b = q;
+ break;
+
+ case 0:
+ case 6:
+ rgb.r = v;
+ rgb.g = t;
+ rgb.b = p;
+ break;
+ }
+ }
+
+ return {
+ r: Math.round(rgb.r * 255),
+ g: Math.round(rgb.g * 255),
+ b: Math.round(rgb.b * 255)
+ };
+ },
+
+ /**
+ * Converts a RGB color into HSV.
+ *
+ * @see https://secure.wikimedia.org/wikipedia/de/wiki/HSV-Farbraum#Transformation_von_RGB_und_HSV
+ *
+ * @param {int} r
+ * @param {int} g
+ * @param {int} b
+ * @return {Object}
+ */
+ rgbToHsv: function(r, g, b) {
+ var h, s, v;
+ var max, min, diff;
+
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ max = Math.max(Math.max(r, g), b);
+ min = Math.min(Math.min(r, g), b);
+ diff = max - min;
+
+ h = 0;
+ if (max !== min) {
+ switch (max) {
+ case r:
+ h = 60 * ((g - b) / diff);
+ break;
+
+ case g:
+ h = 60 * (2 + (b - r) / diff);
+ break;
+
+ case b:
+ h = 60 * (4 + (r - g) / diff);
+ break;
+ }
+
+ if (h < 0) {
+ h += 360;
+ }
+ }
+
+ if (max === 0) {
+ s = 0;
+ }
+ else {
+ s = diff / max;
+ }
+
+ v = max;
+
+ return {
+ h: Math.round(h),
+ s: Math.round(s * 100),
+ v: Math.round(v * 100)
+ };
+ },
+
+ /**
+ * Converts HEX into RGB.
+ *
+ * @param {string} hex
+ * @return {Object}
+ */
+ hexToRgb: function(hex) {
+ if (/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex)) {
+ // only convert #abc and #abcdef
+ var parts = hex.split('');
+
+ // drop the hashtag
+ if (parts[0] === '#') {
+ parts.shift();
+ }
+
+ // parse shorthand #xyz
+ if (parts.length === 3) {
+ return {
+ r: parseInt(parts[0] + '' + parts[0], 16),
+ g: parseInt(parts[1] + '' + parts[1], 16),
+ b: parseInt(parts[2] + '' + parts[2], 16)
+ };
+ }
+ else {
+ return {
+ r: parseInt(parts[0] + '' + parts[1], 16),
+ g: parseInt(parts[2] + '' + parts[3], 16),
+ b: parseInt(parts[4] + '' + parts[5], 16)
+ };
+ }
+ }
+
+ return Number.NaN;
+ },
+
+ /**
+ * Converts a RGB into HEX.
+ *
+ * @see http://www.linuxtopia.org/online_books/javascript_guides/javascript_faq/rgbtohex.htm
+ *
+ * @param {int} r
+ * @param {int} g
+ * @param {int} b
+ * @return {string}
+ */
+ rgbToHex: function(r, g, b) {
+ var charList = "0123456789ABCDEF";
+
+ if (g === undefined) {
+ if (r.toString().match(/^rgba?\((\d+), ?(\d+), ?(\d+)(?:, ?[0-9.]+)?\)$/)) {
+ r = RegExp.$1;
+ g = RegExp.$2;
+ b = RegExp.$3;
+ }
+ }
+
+ return (charList.charAt((r - r % 16) / 16) + '' + charList.charAt(r % 16)) + '' + (charList.charAt((g - g % 16) / 16) + '' + charList.charAt(g % 16)) + '' + (charList.charAt((b - b % 16) / 16) + '' + charList.charAt(b % 16));
+ }
+ };
+
+ // WCF.ColorPicker compatibility (color format conversion)
+ window.__wcf_bc_colorUtil = ColorUtil;
+
+ return ColorUtil;
+});
+
+/**
+ * Provides helper functions for file handling.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/FileUtil
+ */
+define('WoltLabSuite/Core/FileUtil',['Dictionary', 'StringUtil'], function(Dictionary, StringUtil) {
+ "use strict";
+
+ var _fileExtensionIconMapping = Dictionary.fromObject({
+ // archive
+ zip: 'archive',
+ rar: 'archive',
+ tar: 'archive',
+ gz: 'archive',
+
+ // audio
+ mp3: 'audio',
+ ogg: 'audio',
+ wav: 'audio',
+
+ // code
+ php: 'code',
+ html: 'code',
+ htm: 'code',
+ tpl: 'code',
+ js: 'code',
+
+ // excel
+ xls: 'excel',
+ ods: 'excel',
+ xlsx: 'excel',
+
+ // image
+ gif: 'image',
+ jpg: 'image',
+ jpeg: 'image',
+ png: 'image',
+ bmp: 'image',
+ webp: 'image',
+
+ // video
+ avi: 'video',
+ wmv: 'video',
+ mov: 'video',
+ mp4: 'video',
+ mpg: 'video',
+ mpeg: 'video',
+ flv: 'video',
+
+ // pdf
+ pdf: 'pdf',
+
+ // powerpoint
+ ppt: 'powerpoint',
+ pptx: 'powerpoint',
+
+ // text
+ txt: 'text',
+
+ // word
+ doc: 'word',
+ docx: 'word',
+ odt: 'word'
+ });
+
+ var _mimeTypeExtensionMapping = Dictionary.fromObject({
+ // archive
+ 'application/zip': 'zip',
+ 'application/x-zip-compressed': 'zip',
+ 'application/rar': 'rar',
+ 'application/vnd.rar': 'rar',
+ 'application/x-rar-compressed': 'rar',
+ 'application/x-tar': 'tar',
+ 'application/x-gzip': 'gz',
+ 'application/gzip': 'gz',
+
+ // audio
+ 'audio/mpeg': 'mp3',
+ 'audio/mp3': 'mp3',
+ 'audio/ogg': 'ogg',
+ 'audio/x-wav': 'wav',
+
+ // code
+ 'application/x-php': 'php',
+ 'text/html': 'html',
+ 'application/javascript': 'js',
+
+ // excel
+ 'application/vnd.ms-excel': 'xls',
+ 'application/vnd.oasis.opendocument.spreadsheet': 'ods',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
+
+ // image
+ 'image/gif': 'gif',
+ 'image/jpeg': 'jpg',
+ 'image/png': 'png',
+ 'image/x-ms-bmp': 'bmp',
+ 'image/bmp': 'bmp',
+ 'image/webp': 'webp',
+
+ // video
+ 'video/x-msvideo': 'avi',
+ 'video/x-ms-wmv': 'wmv',
+ 'video/quicktime': 'mov',
+ 'video/mp4': 'mp4',
+ 'video/mpeg': 'mpg',
+ 'video/x-flv': 'flv',
+
+ // pdf
+ 'application/pdf': 'pdf',
+
+ // powerpoint
+ 'application/vnd.ms-powerpoint': 'ppt',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
+
+ // text
+ 'text/plain': 'txt',
+
+ // word
+ 'application/msword': 'doc',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
+ 'application/vnd.oasis.opendocument.text': 'odt'
+ });
+
+ return {
+ /**
+ * Formats the given filesize.
+ *
+ * @param {integer} byte number of bytes
+ * @param {integer} precision number of decimals
+ * @return {string} formatted filesize
+ */
+ formatFilesize: function(byte, precision) {
+ if (precision === undefined) {
+ precision = 2;
+ }
+
+ var symbol = 'Byte';
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'kB';
+ }
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'MB';
+ }
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'GB';
+ }
+ if (byte >= 1000) {
+ byte /= 1000;
+ symbol = 'TB';
+ }
+
+ return StringUtil.formatNumeric(byte, -precision) + ' ' + symbol;
+ },
+
+ /**
+ * Returns the icon name for given filename.
+ *
+ * Note: For any file icon name like `fa-file-word`, only `word`
+ * will be returned by this method.
+ *
+ * @parsm {string} filename name of file for which icon name will be returned
+ * @return {string} FontAwesome icon name
+ */
+ getIconNameByFilename: function(filename) {
+ var lastDotPosition = filename.lastIndexOf('.');
+ if (lastDotPosition !== false) {
+ var extension = filename.substr(lastDotPosition + 1);
+
+ if (_fileExtensionIconMapping.has(extension)) {
+ return _fileExtensionIconMapping.get(extension);
+ }
+ }
+
+ return '';
+ },
+
+ /**
+ * Returns a known file extension including a leading dot or an empty string.
+ *
+ * @param mimetype the mimetype to get the common file extension for
+ * @returns {string} the file dot prefixed extension or an empty string
+ */
+ getExtensionByMimeType: function (mimetype) {
+ if (_mimeTypeExtensionMapping.has(mimetype)) {
+ return '.' + _mimeTypeExtensionMapping.get(mimetype);
+ }
+
+ return '';
+ },
+
+ /**
+ * Constructs a File object from a Blob
+ *
+ * @param blob the blob to convert
+ * @param filename the filename
+ * @returns {File} the File object
+ */
+ blobToFile: function (blob, filename) {
+ var ext = this.getExtensionByMimeType(blob.type);
+ var File = window.File;
+
+ try {
+ // IE11 does not support the file constructor
+ new File([], 'ie11-check');
+ }
+ catch (error) {
+ // Create a good enough File object based on the Blob prototype
+ File = function File(chunks, filename, options) {
+ var self = Blob.call(this, chunks, options);
+
+ self.name = filename;
+ self.lastModifiedDate = new Date();
+
+ return self;
+ };
+
+ File.prototype = Object.create(window.File.prototype);
+ }
+
+ return new File([blob], filename + ext, {type: blob.type});
+ },
+ };
+});
+
+/**
+ * Manages user permissions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Permission
+ */
+define('WoltLabSuite/Core/Permission',['Dictionary'], function(Dictionary) {
+ "use strict";
+
+ var _permissions = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Permission
+ */
+ return {
+ /**
+ * Adds a single permission to the store.
+ *
+ * @param {string} permission permission name
+ * @param {boolean} value permission value
+ */
+ add: function(permission, value) {
+ if (typeof value !== "boolean") {
+ throw new TypeError("Permission value has to be boolean.");
+ }
+
+ _permissions.set(permission, value);
+ },
+
+ /**
+ * Adds all the permissions in the given object to the store.
+ *
+ * @param {Object.<string, boolean>} object permission list
+ */
+ addObject: function(object) {
+ for (var key in object) {
+ if (objOwns(object, key)) {
+ this.add(key, object[key]);
+ }
+ }
+ },
+
+ /**
+ * Returns the value of a permission.
+ *
+ * If the permission is unknown, false is returned.
+ *
+ * @param {string} permission permission name
+ * @return {boolean} permission value
+ */
+ get: function(permission) {
+ if (_permissions.has(permission)) {
+ return _permissions.get(permission);
+ }
+
+ return false;
+ }
+ };
+});
+
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism */
+var _self = (typeof window !== 'undefined')
+ ? window // if in browser
+ : (
+ (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
+ ? self // if in worker
+ : {} // if in node js
+ );
+
+/**
+ * Prism: Lightweight, robust, elegant syntax highlighting
+ * MIT license http://www.opensource.org/licenses/mit-license.php/
+ * @author Lea Verou http://lea.verou.me
+ */
+
+var Prism = (function(){
+
+// Private helper vars
+var lang = /\blang(?:uage)?-([\w-]+)\b/i;
+var uniqueId = 0;
+
+var _ = _self.Prism = {
+ manual: _self.Prism && _self.Prism.manual,
+ disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
+ util: {
+ encode: function (tokens) {
+ if (tokens instanceof Token) {
+ return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
+ } else if (_.util.type(tokens) === 'Array') {
+ return tokens.map(_.util.encode);
+ } else {
+ return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' ');
+ }
+ },
+
+ type: function (o) {
+ return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
+ },
+
+ objId: function (obj) {
+ if (!obj['__id']) {
+ Object.defineProperty(obj, '__id', { value: ++uniqueId });
+ }
+ return obj['__id'];
+ },
+
+ // Deep clone a language definition (e.g. to extend it)
+ clone: function (o, visited) {
+ var type = _.util.type(o);
+ visited = visited || {};
+
+ switch (type) {
+ case 'Object':
+ if (visited[_.util.objId(o)]) {
+ return visited[_.util.objId(o)];
+ }
+ var clone = {};
+ visited[_.util.objId(o)] = clone;
+
+ for (var key in o) {
+ if (o.hasOwnProperty(key)) {
+ clone[key] = _.util.clone(o[key], visited);
+ }
+ }
+
+ return clone;
+
+ case 'Array':
+ if (visited[_.util.objId(o)]) {
+ return visited[_.util.objId(o)];
+ }
+ var clone = [];
+ visited[_.util.objId(o)] = clone;
+
+ o.forEach(function (v, i) {
+ clone[i] = _.util.clone(v, visited);
+ });
+
+ return clone;
+ }
+
+ return o;
+ }
+ },
+
+ languages: {
+ extend: function (id, redef) {
+ var lang = _.util.clone(_.languages[id]);
+
+ for (var key in redef) {
+ lang[key] = redef[key];
+ }
+
+ return lang;
+ },
+
+ /**
+ * Insert a token before another token in a language literal
+ * As this needs to recreate the object (we cannot actually insert before keys in object literals),
+ * we cannot just provide an object, we need anobject and a key.
+ * @param inside The key (or language id) of the parent
+ * @param before The key to insert before. If not provided, the function appends instead.
+ * @param insert Object with the key/value pairs to insert
+ * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
+ */
+ insertBefore: function (inside, before, insert, root) {
+ root = root || _.languages;
+ var grammar = root[inside];
+
+ if (arguments.length == 2) {
+ insert = arguments[1];
+
+ for (var newToken in insert) {
+ if (insert.hasOwnProperty(newToken)) {
+ grammar[newToken] = insert[newToken];
+ }
+ }
+
+ return grammar;
+ }
+
+ var ret = {};
+
+ for (var token in grammar) {
+
+ if (grammar.hasOwnProperty(token)) {
+
+ if (token == before) {
+
+ for (var newToken in insert) {
+
+ if (insert.hasOwnProperty(newToken)) {
+ ret[newToken] = insert[newToken];
+ }
+ }
+ }
+
+ ret[token] = grammar[token];
+ }
+ }
+
+ var old = root[inside];
+ root[inside] = ret;
+
+ // Update references in other language definitions
+ _.languages.DFS(_.languages, function(key, value) {
+ if (value === old && key != inside) {
+ this[key] = ret;
+ }
+ });
+
+ return ret;
+ },
+
+ // Traverse a language definition with Depth First Search
+ DFS: function(o, callback, type, visited) {
+ visited = visited || {};
+ for (var i in o) {
+ if (o.hasOwnProperty(i)) {
+ callback.call(o, i, o[i], type || i);
+
+ if (_.util.type(o[i]) === 'Object' && !visited[_.util.objId(o[i])]) {
+ visited[_.util.objId(o[i])] = true;
+ _.languages.DFS(o[i], callback, null, visited);
+ }
+ else if (_.util.type(o[i]) === 'Array' && !visited[_.util.objId(o[i])]) {
+ visited[_.util.objId(o[i])] = true;
+ _.languages.DFS(o[i], callback, i, visited);
+ }
+ }
+ }
+ }
+ },
+ plugins: {},
+
+ highlightAll: function(async, callback) {
+ _.highlightAllUnder(document, async, callback);
+ },
+
+ highlightAllUnder: function(container, async, callback) {
+ var env = {
+ callback: callback,
+ selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
+ };
+
+ _.hooks.run("before-highlightall", env);
+
+ var elements = env.elements || container.querySelectorAll(env.selector);
+
+ for (var i=0, element; element = elements[i++];) {
+ _.highlightElement(element, async === true, env.callback);
+ }
+ },
+
+ highlightElement: function(element, async, callback) {
+ // Find language
+ var language, grammar, parent = element;
+
+ while (parent && !lang.test(parent.className)) {
+ parent = parent.parentNode;
+ }
+
+ if (parent) {
+ language = (parent.className.match(lang) || [,''])[1].toLowerCase();
+ grammar = _.languages[language];
+ }
+
+ // Set language on the element, if not present
+ element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
+
+ if (element.parentNode) {
+ // Set language on the parent, for styling
+ parent = element.parentNode;
+
+ if (/pre/i.test(parent.nodeName)) {
+ parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
+ }
+ }
+
+ var code = element.textContent;
+
+ var env = {
+ element: element,
+ language: language,
+ grammar: grammar,
+ code: code
+ };
+
+ _.hooks.run('before-sanity-check', env);
+
+ if (!env.code || !env.grammar) {
+ if (env.code) {
+ _.hooks.run('before-highlight', env);
+ env.element.textContent = env.code;
+ _.hooks.run('after-highlight', env);
+ }
+ _.hooks.run('complete', env);
+ return;
+ }
+
+ _.hooks.run('before-highlight', env);
+
+ if (async && _self.Worker) {
+ var worker = new Worker(_.filename);
+
+ worker.onmessage = function(evt) {
+ env.highlightedCode = evt.data;
+
+ _.hooks.run('before-insert', env);
+
+ env.element.innerHTML = env.highlightedCode;
+
+ callback && callback.call(env.element);
+ _.hooks.run('after-highlight', env);
+ _.hooks.run('complete', env);
+ };
+
+ worker.postMessage(JSON.stringify({
+ language: env.language,
+ code: env.code,
+ immediateClose: true
+ }));
+ }
+ else {
+ env.highlightedCode = _.highlight(env.code, env.grammar, env.language);
+
+ _.hooks.run('before-insert', env);
+
+ env.element.innerHTML = env.highlightedCode;
+
+ callback && callback.call(element);
+
+ _.hooks.run('after-highlight', env);
+ _.hooks.run('complete', env);
+ }
+ },
+
+ highlight: function (text, grammar, language) {
+ var env = {
+ code: text,
+ grammar: grammar,
+ language: language
+ };
+ _.hooks.run('before-tokenize', env);
+ env.tokens = _.tokenize(env.code, env.grammar);
+ _.hooks.run('after-tokenize', env);
+ return Token.stringify(_.util.encode(env.tokens), env.language);
+ },
+
+ matchGrammar: function (text, strarr, grammar, index, startPos, oneshot, target) {
+ var Token = _.Token;
+
+ for (var token in grammar) {
+ if(!grammar.hasOwnProperty(token) || !grammar[token]) {
+ continue;
+ }
+
+ if (token == target) {
+ return;
+ }
+
+ var patterns = grammar[token];
+ patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns];
+
+ for (var j = 0; j < patterns.length; ++j) {
+ var pattern = patterns[j],
+ inside = pattern.inside,
+ lookbehind = !!pattern.lookbehind,
+ greedy = !!pattern.greedy,
+ lookbehindLength = 0,
+ alias = pattern.alias;
+
+ if (greedy && !pattern.pattern.global) {
+ // Without the global flag, lastIndex won't work
+ var flags = pattern.pattern.toString().match(/[imuy]*$/)[0];
+ pattern.pattern = RegExp(pattern.pattern.source, flags + "g");
+ }
+
+ pattern = pattern.pattern || pattern;
+
+ // Don’t cache length as it changes during the loop
+ for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) {
+
+ var str = strarr[i];
+
+ if (strarr.length > text.length) {
+ // Something went terribly wrong, ABORT, ABORT!
+ return;
+ }
+
+ if (str instanceof Token) {
+ continue;
+ }
+
+ if (greedy && i != strarr.length - 1) {
+ pattern.lastIndex = pos;
+ var match = pattern.exec(text);
+ if (!match) {
+ break;
+ }
+
+ var from = match.index + (lookbehind ? match[1].length : 0),
+ to = match.index + match[0].length,
+ k = i,
+ p = pos;
+
+ for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) {
+ p += strarr[k].length;
+ // Move the index i to the element in strarr that is closest to from
+ if (from >= p) {
+ ++i;
+ pos = p;
+ }
+ }
+
+ // If strarr[i] is a Token, then the match starts inside another Token, which is invalid
+ if (strarr[i] instanceof Token) {
+ continue;
+ }
+
+ // Number of tokens to delete and replace with the new match
+ delNum = k - i;
+ str = text.slice(pos, p);
+ match.index -= pos;
+ } else {
+ pattern.lastIndex = 0;
+
+ var match = pattern.exec(str),
+ delNum = 1;
+ }
+
+ if (!match) {
+ if (oneshot) {
+ break;
+ }
+
+ continue;
+ }
+
+ if(lookbehind) {
+ lookbehindLength = match[1] ? match[1].length : 0;
+ }
+
+ var from = match.index + lookbehindLength,
+ match = match[0].slice(lookbehindLength),
+ to = from + match.length,
+ before = str.slice(0, from),
+ after = str.slice(to);
+
+ var args = [i, delNum];
+
+ if (before) {
+ ++i;
+ pos += before.length;
+ args.push(before);
+ }
+
+ var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);
+
+ args.push(wrapped);
+
+ if (after) {
+ args.push(after);
+ }
+
+ Array.prototype.splice.apply(strarr, args);
+
+ if (delNum != 1)
+ _.matchGrammar(text, strarr, grammar, i, pos, true, token);
+
+ if (oneshot)
+ break;
+ }
+ }
+ }
+ },
+
+ tokenize: function(text, grammar, language) {
+ var strarr = [text];
+
+ var rest = grammar.rest;
+
+ if (rest) {
+ for (var token in rest) {
+ grammar[token] = rest[token];
+ }
+
+ delete grammar.rest;
+ }
+
+ _.matchGrammar(text, strarr, grammar, 0, 0, false);
+
+ return strarr;
+ },
+
+ hooks: {
+ all: {},
+
+ add: function (name, callback) {
+ var hooks = _.hooks.all;
+
+ hooks[name] = hooks[name] || [];
+
+ hooks[name].push(callback);
+ },
+
+ run: function (name, env) {
+ var callbacks = _.hooks.all[name];
+
+ if (!callbacks || !callbacks.length) {
+ return;
+ }
+
+ for (var i=0, callback; callback = callbacks[i++];) {
+ callback(env);
+ }
+ }
+ }
+};
+
+var Token = _.Token = function(type, content, alias, matchedStr, greedy) {
+ this.type = type;
+ this.content = content;
+ this.alias = alias;
+ // Copy of the full string this token was created from
+ this.length = (matchedStr || "").length|0;
+ this.greedy = !!greedy;
+};
+
+Token.stringify = function(o, language, parent) {
+ if (typeof o == 'string') {
+ return o;
+ }
+
+ if (_.util.type(o) === 'Array') {
+ return o.map(function(element) {
+ return Token.stringify(element, language, o);
+ }).join('');
+ }
+
+ var env = {
+ type: o.type,
+ content: Token.stringify(o.content, language, parent),
+ tag: 'span',
+ classes: ['token', o.type],
+ attributes: {},
+ language: language,
+ parent: parent
+ };
+
+ if (o.alias) {
+ var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
+ Array.prototype.push.apply(env.classes, aliases);
+ }
+
+ _.hooks.run('wrap', env);
+
+ var attributes = Object.keys(env.attributes).map(function(name) {
+ return name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"';
+ }).join(' ');
+
+ return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '</' + env.tag + '>';
+
+};
+
+if (!_self.document) {
+ if (!_self.addEventListener) {
+ // in Node.js
+ return _self.Prism;
+ }
+
+ if (!_.disableWorkerMessageHandler) {
+ // In worker
+ _self.addEventListener('message', function (evt) {
+ var message = JSON.parse(evt.data),
+ lang = message.language,
+ code = message.code,
+ immediateClose = message.immediateClose;
+
+ _self.postMessage(_.highlight(code, _.languages[lang], lang));
+ if (immediateClose) {
+ _self.close();
+ }
+ }, false);
+ }
+
+ return _self.Prism;
+}
+
+//Get current script and highlight
+var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop();
+
+if (script) {
+ _.filename = script.src;
+
+ if (!_.manual && !script.hasAttribute('data-manual')) {
+ if(document.readyState !== "loading") {
+ if (window.requestAnimationFrame) {
+ window.requestAnimationFrame(_.highlightAll);
+ } else {
+ window.setTimeout(_.highlightAll, 16);
+ }
+ }
+ else {
+ document.addEventListener('DOMContentLoaded', _.highlightAll);
+ }
+ }
+}
+
+return _self.Prism;
+
+})();
+
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Prism;
+}
+
+// hack for components to work correctly in node.js
+if (typeof global !== 'undefined') {
+ global.Prism = Prism;
+}
+;
+
+define("prism/prism", function(){});
+
+/**
+ * Augments the Prism syntax highlighter with additional functions.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Prism
+ */
+
+window.Prism = window.Prism || {}
+window.Prism.manual = true
+
+define('WoltLabSuite/Core/Prism',['prism/prism'], function () {
+ Prism.wscSplitIntoLines = function (container) {
+ var frag = document.createDocumentFragment();
+ var lineNo = 1;
+ var it, node, line;
+
+ function newLine() {
+ var line = elCreate('span');
+ elData(line, 'number', lineNo++);
+ frag.appendChild(line);
+
+ return line;
+ }
+
+ // IE11 expects a fourth, non-standard, parameter (entityReferenceExpansion) and a valid function as third
+ it = document.createNodeIterator(container, NodeFilter.SHOW_TEXT, function () {
+ return NodeFilter.FILTER_ACCEPT;
+ }, false);
+
+ line = newLine(lineNo);
+ while (node = it.nextNode()) {
+ node.data.split(/\r?\n/).forEach(function (codeLine, index) {
+ var current, parent;
+
+ // We are behind a newline, insert \n and create new container.
+ if (index >= 1) {
+ line.appendChild(document.createTextNode("\n"));
+ line = newLine(lineNo);
+ }
+
+ current = document.createTextNode(codeLine);
+
+ // Copy hierarchy (to preserve CSS classes).
+ parent = node.parentNode
+ while (parent !== container) {
+ var clone = parent.cloneNode(false);
+ clone.appendChild(current);
+ current = clone;
+ parent = parent.parentNode;
+ }
+
+ line.appendChild(current);
+ });
+ }
+
+ return frag;
+ };
+
+ return Prism;
+});
+
+/**
+ * Uploads file via AJAX.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Upload
+ */
+define('WoltLabSuite/Core/Upload',['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util', 'Dom/Traverse'], function(AjaxRequest, Core, DomChangeListener, Language, DomUtil, DomTraverse) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _createButton: function() {},
+ _createFileElement: function() {},
+ _createFileElements: function() {},
+ _failure: function() {},
+ _getParameters: function() {},
+ _insertButton: function() {},
+ _progress: function() {},
+ _removeButton: function() {},
+ _success: function() {},
+ _upload: function() {},
+ _uploadFiles: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function Upload(buttonContainerId, targetId, options) {
+ options = options || {};
+
+ if (options.className === undefined) {
+ throw new Error("Missing class name.");
+ }
+
+ // set default options
+ this._options = Core.extend({
+ // name of the PHP action
+ action: 'upload',
+ // is true if multiple files can be uploaded at once
+ multiple: false,
+ // name if the upload field
+ name: '__files[]',
+ // is true if every file from a multi-file selection is uploaded in its own request
+ singleFileRequests: false,
+ // url for uploading file
+ url: 'index.php?ajax-upload/&t=' + SECURITY_TOKEN
+ }, options);
+
+ this._options.url = Core.convertLegacyUrl(this._options.url);
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
+ }
+
+ this._buttonContainer = elById(buttonContainerId);
+ if (this._buttonContainer === null) {
+ throw new Error("Element id '" + buttonContainerId + "' is unknown.");
+ }
+
+ this._target = elById(targetId);
+ if (targetId === null) {
+ throw new Error("Element id '" + targetId + "' is unknown.");
+ }
+ if (options.multiple && this._target.nodeName !== 'UL' && this._target.nodeName !== 'OL' && this._target.nodeName !== 'TBODY') {
+ throw new Error("Target element has to be list or table body if uploading multiple files is supported.");
+ }
+
+ this._fileElements = [];
+ this._internalFileId = 0;
+
+ // upload ids that belong to an upload of multiple files at once
+ this._multiFileUploadIds = [];
+
+ this._createButton();
+ }
+ Upload.prototype = {
+ /**
+ * Creates the upload button.
+ */
+ _createButton: function() {
+ this._fileUpload = elCreate('input');
+ elAttr(this._fileUpload, 'type', 'file');
+ elAttr(this._fileUpload, 'name', this._options.name);
+ if (this._options.multiple) {
+ elAttr(this._fileUpload, 'multiple', 'true');
+ }
+ this._fileUpload.addEventListener('change', this._upload.bind(this));
+
+ this._button = elCreate('p');
+ this._button.className = 'button uploadButton';
+ elAttr(this._button, 'role', 'button');
+ elAttr(this._button, 'tabindex', '0');
+
+ var span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.upload');
+ this._button.appendChild(span);
+
+ DomUtil.prepend(this._fileUpload, this._button);
+
+ this._insertButton();
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Creates the document element for an uploaded file.
+ *
+ * @param {File} file uploaded file
+ * @return {HTMLElement}
+ */
+ _createFileElement: function(file) {
+ var progress = elCreate('progress');
+ elAttr(progress, 'max', 100);
+
+ if (this._target.nodeName === 'OL' || this._target.nodeName === 'UL') {
+ var li = elCreate('li');
+ li.innerText = file.name;
+ li.appendChild(progress);
+
+ this._target.appendChild(li);
+
+ return li;
+ }
+ else if (this._target.nodeName === 'TBODY') {
+ return this._createFileTableRow(file);
+ }
+ else {
+ var p = elCreate('p');
+ p.appendChild(progress);
+
+ this._target.appendChild(p);
+
+ return p;
+ }
+ },
+
+ /**
+ * Creates the document elements for uploaded files.
+ *
+ * @param {(FileList|Array.<File>)} files uploaded files
+ */
+ _createFileElements: function(files) {
+ if (files.length) {
+ var uploadId = this._fileElements.length;
+ this._fileElements[uploadId] = [];
+
+ for (var i = 0, length = files.length; i < length; i++) {
+ var file = files[i];
+ var fileElement = this._createFileElement(file);
+
+ if (!fileElement.classList.contains('uploadFailed')) {
+ elData(fileElement, 'filename', file.name);
+ elData(fileElement, 'internal-file-id', this._internalFileId++);
+ this._fileElements[uploadId][i] = fileElement;
+ }
+ }
+
+ DomChangeListener.trigger();
+
+ return uploadId;
+ }
+
+ return null;
+ },
+
+ _createFileTableRow: function(file) {
+ throw new Error("Has to be implemented in subclass.");
+ },
+
+ /**
+ * Handles a failed file upload.
+ *
+ * @param {int} uploadId identifier of a file upload
+ * @param {object<string, *>} data response data
+ * @param {string} responseText response
+ * @param {XMLHttpRequest} xhr request object
+ * @param {object<string, *>} requestOptions options used to send AJAX request
+ * @return {boolean} true if the error message should be shown
+ */
+ _failure: function(uploadId, data, responseText, xhr, requestOptions) {
+ // does nothing
+ return true;
+ },
+
+ /**
+ * Return additional parameters for upload requests.
+ *
+ * @return {object<string, *>} additional parameters
+ */
+ _getParameters: function() {
+ return {};
+ },
+
+ /**
+ * Return additional form data for upload requests.
+ *
+ * @return {object<string, *>} additional form data
+ * @since 5.2
+ */
+ _getFormData: function() {
+ return {};
+ },
+
+ /**
+ * Inserts the created button to upload files into the button container.
+ */
+ _insertButton: function() {
+ DomUtil.prepend(this._button, this._buttonContainer);
+ },
+
+ /**
+ * Updates the progress of an upload.
+ *
+ * @param {int} uploadId internal upload identifier
+ * @param {XMLHttpRequestProgressEvent} event progress event object
+ */
+ _progress: function(uploadId, event) {
+ var percentComplete = Math.round(event.loaded / event.total * 100);
+
+ for (var i in this._fileElements[uploadId]) {
+ var progress = elByTag('PROGRESS', this._fileElements[uploadId][i]);
+ if (progress.length === 1) {
+ elAttr(progress[0], 'value', percentComplete);
+ }
+ }
+ },
+
+ /**
+ * Removes the button to upload files.
+ */
+ _removeButton: function() {
+ elRemove(this._button);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Handles a successful file upload.
+ *
+ * @param {int} uploadId identifier of a file upload
+ * @param {object<string, *>} data response data
+ * @param {string} responseText response
+ * @param {XMLHttpRequest} xhr request object
+ * @param {object<string, *>} requestOptions options used to send AJAX request
+ */
+ _success: function(uploadId, data, responseText, xhr, requestOptions) {
+ // does nothing
+ },
+
+ /**
+ * File input change callback to upload files.
+ *
+ * @param {Event} event input change event object
+ * @param {File} file uploaded file
+ * @param {Blob} blob file blob
+ * @return {(int|Array.<int>|null)} identifier(s) for the uploaded files
+ */
+ _upload: function(event, file, blob) {
+ // remove failed upload elements first
+ var failedUploads = DomTraverse.childrenByClass(this._target, 'uploadFailed');
+ for (var i = 0, length = failedUploads.length; i < length; i++) {
+ elRemove(failedUploads[i]);
+ }
+
+ var uploadId = null;
+
+ var files = [];
+ if (file) {
+ files.push(file);
+ }
+ else if (blob) {
+ var fileExtension = '';
+ switch (blob.type) {
+ case 'image/jpeg':
+ fileExtension = '.jpg';
+ break;
+
+ case 'image/gif':
+ fileExtension = '.gif';
+ break;
+
+ case 'image/png':
+ fileExtension = '.png';
+ break;
+ }
+
+ files.push({
+ name: 'pasted-from-clipboard' + fileExtension
+ });
+ }
+ else {
+ files = this._fileUpload.files;
+ }
+
+ if (files.length && this.validateUpload(files)) {
+ if (this._options.singleFileRequests) {
+ uploadId = [];
+ for (var i = 0, length = files.length; i < length; i++) {
+ var localUploadId = this._uploadFiles([ files[i] ], blob);
+
+ if (files.length !== 1) {
+ this._multiFileUploadIds.push(localUploadId)
+ }
+ uploadId.push(localUploadId);
+ }
+ }
+ else {
+ uploadId = this._uploadFiles(files, blob);
+ }
+ }
+
+ // re-create upload button to effectively reset the 'files'
+ // property of the input element
+ this._removeButton();
+ this._createButton();
+
+ return uploadId;
+ },
+
+ /**
+ * Validates the upload before uploading them.
+ *
+ * @param {(FileList|Array.<File>)} files uploaded files
+ * @return {boolean}
+ * @since 5.2
+ */
+ validateUpload: function(files) {
+ return true;
+ },
+
+ /**
+ * Sends the request to upload files.
+ *
+ * @param {(FileList|Array.<File>)} files uploaded files
+ * @param {Blob} blob file blob
+ * @return {(int|null)} identifier for the uploaded files
+ */
+ _uploadFiles: function(files, blob) {
+ var uploadId = this._createFileElements(files);
+
+ // no more files left, abort
+ if (!this._fileElements[uploadId].length) {
+ return null;
+ }
+
+ var formData = new FormData();
+ for (var i = 0, length = files.length; i < length; i++) {
+ if (this._fileElements[uploadId][i]) {
+ var internalFileId = elData(this._fileElements[uploadId][i], 'internal-file-id');
+
+ if (blob) {
+ formData.append('__files[' + internalFileId + ']', blob, files[i].name);
+ }
+ else {
+ formData.append('__files[' + internalFileId + ']', files[i]);
+ }
+ }
+ }
+
+ formData.append('actionName', this._options.action);
+ formData.append('className', this._options.className);
+ if (this._options.action === 'upload') {
+ formData.append('interfaceName', 'wcf\\data\\IUploadAction');
+ }
+
+ // recursively append additional parameters to form data
+ var appendFormData = function(parameters, prefix) {
+ prefix = prefix || '';
+
+ for (var name in parameters) {
+ if (typeof parameters[name] === 'object') {
+ var newPrefix = prefix.length === 0 ? name : prefix + '[' + name + ']';
+ appendFormData(parameters[name], newPrefix);
+ }
+ else {
+ var dataName = prefix.length === 0 ? name : prefix + '[' + name + ']';
+ formData.append(dataName, parameters[name]);
+ }
+ }
+ };
+
+ appendFormData(this._getParameters(), 'parameters');
+ appendFormData(this._getFormData());
+
+ var request = new AjaxRequest({
+ data: formData,
+ contentType: false,
+ failure: this._failure.bind(this, uploadId),
+ silent: true,
+ success: this._success.bind(this, uploadId),
+ uploadProgress: this._progress.bind(this, uploadId),
+ url: this._options.url,
+ withCredentials: true
+ });
+ request.sendRequest();
+
+ return uploadId;
+ },
+
+ /**
+ * Returns true if there are any pending uploads handled by this
+ * upload manager.
+ *
+ * @return {boolean}
+ * @since 5.2
+ */
+ hasPendingUploads: function() {
+ for (var uploadId in this._fileElements) {
+ for (var i in this._fileElements[uploadId]) {
+ var progress = elByTag('PROGRESS', this._fileElements[uploadId][i]);
+ if (progress.length === 1) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Uploads the given file blob.
+ *
+ * @param {Blob} blob file blob
+ * @return {int} identifier for the uploaded file
+ */
+ uploadBlob: function(blob) {
+ return this._upload(null, null, blob);
+ },
+
+ /**
+ * Uploads the given file.
+ *
+ * @param {File} file uploaded file
+ * @return {int} identifier(s) for the uploaded file
+ */
+ uploadFile: function(file) {
+ return this._upload(null, file);
+ }
+ };
+
+ return Upload;
+});
+
+/**
+ * Provides data of the active user.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/User
+ */
+define('WoltLabSuite/Core/User',[], function() {
+ "use strict";
+
+ var _didInit = false;
+ var _link;
+
+ /**
+ * @exports WoltLabSuite/Core/User
+ */
+ return {
+ /**
+ * Returns the link to the active user's profile or an empty string
+ * if the active user is a guest.
+ *
+ * @return {string}
+ */
+ getLink: function() {
+ return _link;
+ },
+
+ /**
+ * Initializes the user object.
+ *
+ * @param {int} userId id of the user, `0` for guests
+ * @param {string} username name of the user, empty for guests
+ * @param {string} userLink link to the user's profile, empty for guests
+ */
+ init: function(userId, username, userLink) {
+ if (_didInit) {
+ throw new Error('User has already been initialized.');
+ }
+
+ // define non-writeable properties for userId and username
+ Object.defineProperty(this, 'userId', {
+ value: userId,
+ writable: false
+ });
+ Object.defineProperty(this, 'username', {
+ value: username,
+ writable: false
+ });
+
+ _link = userLink;
+
+ _didInit = true;
+ }
+ };
+});
+
+/**
+ * Provides a utility class to issue JSONP requests.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ajax/Jsonp
+ */
+define('WoltLabSuite/Core/Ajax/Jsonp',['Core'], function(Core) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Ajax/Jsonp
+ */
+ return {
+ /**
+ * Issues a JSONP request.
+ *
+ * @param {string} url source URL, must not contain callback parameter
+ * @param {function} success success callback
+ * @param {function=} failure timeout callback
+ * @param {object<string, *>=} options request options
+ */
+ send: function(url, success, failure, options) {
+ url = (typeof url === 'string') ? url.trim() : '';
+ if (url.length === 0) {
+ throw new Error("Expected a non-empty string for parameter 'url'.");
+ }
+
+ if (typeof success !== 'function') {
+ throw new TypeError("Expected a valid callback function for parameter 'success'.");
+ }
+
+ options = Core.extend({
+ parameterName: 'callback',
+ timeout: 10
+ }, options || {});
+
+ var callbackName = 'wcf_jsonp_' + Core.getUuid().replace(/-/g, '').substr(0, 8);
+ var script;
+
+ var timeout = window.setTimeout(function() {
+ if (typeof failure === 'function') {
+ failure();
+ }
+
+ window[callbackName] = undefined;
+ elRemove(script);
+ }, (~~options.timeout || 10) * 1000);
+
+ window[callbackName] = function() {
+ window.clearTimeout(timeout);
+
+ success.apply(null, arguments);
+
+ window[callbackName] = undefined;
+ elRemove(script);
+ };
+
+ url += (url.indexOf('?') === -1) ? '?' : '&';
+ url += options.parameterName + '=' + callbackName;
+
+ script = elCreate('script');
+ script.async = true;
+ elAttr(script, 'src', url);
+
+ document.head.appendChild(script);
+ }
+ };
+});
+
+/**
+ * Simple notification overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Notification
+ */
+define('WoltLabSuite/Core/Ui/Notification',['Language'], function(Language) {
+ "use strict";
+
+ var _busy = false;
+ var _callback = null;
+ var _message = null;
+ var _notificationElement = null;
+ var _timeout = null;
+
+ var _callbackHide = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Notification
+ */
+ var UiNotification = {
+ /**
+ * Shows a notification.
+ *
+ * @param {string} message message
+ * @param {function=} callback callback function to be executed once notification is being hidden
+ * @param {string=} cssClassName alternate CSS class name, defaults to 'success'
+ */
+ show: function(message, callback, cssClassName) {
+ if (_busy) {
+ return;
+ }
+
+ this._init();
+
+ _callback = (typeof callback === 'function') ? callback : null;
+ _message.className = cssClassName || 'success';
+ _message.textContent = Language.get(message || 'wcf.global.success');
+
+ _busy = true;
+
+ _notificationElement.classList.add('active');
+
+ _timeout = setTimeout(_callbackHide, 2000);
+ },
+
+ /**
+ * Initializes the UI elements.
+ */
+ _init: function() {
+ if (_notificationElement === null) {
+ _callbackHide = this._hide.bind(this);
+
+ _notificationElement = elCreate('div');
+ _notificationElement.id = 'systemNotification';
+
+ _message = elCreate('p');
+ _message.addEventListener(WCF_CLICK_EVENT, _callbackHide);
+ _notificationElement.appendChild(_message);
+
+ document.body.appendChild(_notificationElement);
+ }
+ },
+
+ /**
+ * Hides the notification and invokes the callback if provided.
+ */
+ _hide: function() {
+ clearTimeout(_timeout);
+
+ _notificationElement.classList.remove('active');
+
+ if (_callback !== null) {
+ _callback();
+ }
+
+ _busy = false;
+ }
+ };
+
+ return UiNotification;
+});
+
+define('prism/prism-meta',[],function(){return /*START*/{"markup":{"title":"Markup","file":"markup"},"html":{"title":"HTML","file":"markup"},"xml":{"title":"XML","file":"markup"},"svg":{"title":"SVG","file":"markup"},"mathml":{"title":"MathML","file":"markup"},"css":{"title":"CSS","file":"css"},"clike":{"title":"C-like","file":"clike"},"javascript":{"title":"JavaScript","file":"javascript"},"abap":{"title":"ABAP","file":"abap"},"actionscript":{"title":"ActionScript","file":"actionscript"},"ada":{"title":"Ada","file":"ada"},"apacheconf":{"title":"Apache Configuration","file":"apacheconf"},"apl":{"title":"APL","file":"apl"},"applescript":{"title":"AppleScript","file":"applescript"},"arduino":{"title":"Arduino","file":"arduino"},"arff":{"title":"ARFF","file":"arff"},"asciidoc":{"title":"AsciiDoc","file":"asciidoc"},"asm6502":{"title":"6502 Assembly","file":"asm6502"},"aspnet":{"title":"ASP.NET (C#)","file":"aspnet"},"autohotkey":{"title":"AutoHotkey","file":"autohotkey"},"autoit":{"title":"AutoIt","file":"autoit"},"bash":{"title":"Bash","file":"bash"},"basic":{"title":"BASIC","file":"basic"},"batch":{"title":"Batch","file":"batch"},"bison":{"title":"Bison","file":"bison"},"brainfuck":{"title":"Brainfuck","file":"brainfuck"},"bro":{"title":"Bro","file":"bro"},"c":{"title":"C","file":"c"},"csharp":{"title":"C#","file":"csharp"},"cpp":{"title":"C++","file":"cpp"},"coffeescript":{"title":"CoffeeScript","file":"coffeescript"},"clojure":{"title":"Clojure","file":"clojure"},"crystal":{"title":"Crystal","file":"crystal"},"csp":{"title":"Content-Security-Policy","file":"csp"},"css-extras":{"title":"CSS Extras","file":"css-extras"},"d":{"title":"D","file":"d"},"dart":{"title":"Dart","file":"dart"},"diff":{"title":"Diff","file":"diff"},"django":{"title":"Django/Jinja2","file":"django"},"docker":{"title":"Docker","file":"docker"},"eiffel":{"title":"Eiffel","file":"eiffel"},"elixir":{"title":"Elixir","file":"elixir"},"elm":{"title":"Elm","file":"elm"},"erb":{"title":"ERB","file":"erb"},"erlang":{"title":"Erlang","file":"erlang"},"fsharp":{"title":"F#","file":"fsharp"},"flow":{"title":"Flow","file":"flow"},"fortran":{"title":"Fortran","file":"fortran"},"gedcom":{"title":"GEDCOM","file":"gedcom"},"gherkin":{"title":"Gherkin","file":"gherkin"},"git":{"title":"Git","file":"git"},"glsl":{"title":"GLSL","file":"glsl"},"gml":{"title":"GameMaker Language","file":"gml"},"go":{"title":"Go","file":"go"},"graphql":{"title":"GraphQL","file":"graphql"},"groovy":{"title":"Groovy","file":"groovy"},"haml":{"title":"Haml","file":"haml"},"handlebars":{"title":"Handlebars","file":"handlebars"},"haskell":{"title":"Haskell","file":"haskell"},"haxe":{"title":"Haxe","file":"haxe"},"http":{"title":"HTTP","file":"http"},"hpkp":{"title":"HTTP Public-Key-Pins","file":"hpkp"},"hsts":{"title":"HTTP Strict-Transport-Security","file":"hsts"},"ichigojam":{"title":"IchigoJam","file":"ichigojam"},"icon":{"title":"Icon","file":"icon"},"inform7":{"title":"Inform 7","file":"inform7"},"ini":{"title":"Ini","file":"ini"},"io":{"title":"Io","file":"io"},"j":{"title":"J","file":"j"},"java":{"title":"Java","file":"java"},"jolie":{"title":"Jolie","file":"jolie"},"json":{"title":"JSON","file":"json"},"julia":{"title":"Julia","file":"julia"},"keyman":{"title":"Keyman","file":"keyman"},"kotlin":{"title":"Kotlin","file":"kotlin"},"latex":{"title":"LaTeX","file":"latex"},"less":{"title":"Less","file":"less"},"liquid":{"title":"Liquid","file":"liquid"},"lisp":{"title":"Lisp","file":"lisp"},"livescript":{"title":"LiveScript","file":"livescript"},"lolcode":{"title":"LOLCODE","file":"lolcode"},"lua":{"title":"Lua","file":"lua"},"makefile":{"title":"Makefile","file":"makefile"},"markdown":{"title":"Markdown","file":"markdown"},"markup-templating":{"title":"Markup templating","file":"markup-templating"},"matlab":{"title":"MATLAB","file":"matlab"},"mel":{"title":"MEL","file":"mel"},"mizar":{"title":"Mizar","file":"mizar"},"monkey":{"title":"Monkey","file":"monkey"},"n4js":{"title":"N4JS","file":"n4js"},"nasm":{"title":"NASM","file":"nasm"},"nginx":{"title":"nginx","file":"nginx"},"nim":{"title":"Nim","file":"nim"},"nix":{"title":"Nix","file":"nix"},"nsis":{"title":"NSIS","file":"nsis"},"objectivec":{"title":"Objective-C","file":"objectivec"},"ocaml":{"title":"OCaml","file":"ocaml"},"opencl":{"title":"OpenCL","file":"opencl"},"oz":{"title":"Oz","file":"oz"},"parigp":{"title":"PARI/GP","file":"parigp"},"parser":{"title":"Parser","file":"parser"},"pascal":{"title":"Pascal","file":"pascal"},"perl":{"title":"Perl","file":"perl"},"php":{"title":"PHP","file":"php"},"php-extras":{"title":"PHP Extras","file":"php-extras"},"plsql":{"title":"PL/SQL","file":"plsql"},"powershell":{"title":"PowerShell","file":"powershell"},"processing":{"title":"Processing","file":"processing"},"prolog":{"title":"Prolog","file":"prolog"},"properties":{"title":".properties","file":"properties"},"protobuf":{"title":"Protocol Buffers","file":"protobuf"},"pug":{"title":"Pug","file":"pug"},"puppet":{"title":"Puppet","file":"puppet"},"pure":{"title":"Pure","file":"pure"},"python":{"title":"Python","file":"python"},"q":{"title":"Q (kdb+ database)","file":"q"},"qore":{"title":"Qore","file":"qore"},"r":{"title":"R","file":"r"},"jsx":{"title":"React JSX","file":"jsx"},"tsx":{"title":"React TSX","file":"tsx"},"renpy":{"title":"Ren'py","file":"renpy"},"reason":{"title":"Reason","file":"reason"},"rest":{"title":"reST (reStructuredText)","file":"rest"},"rip":{"title":"Rip","file":"rip"},"roboconf":{"title":"Roboconf","file":"roboconf"},"ruby":{"title":"Ruby","file":"ruby"},"rust":{"title":"Rust","file":"rust"},"sas":{"title":"SAS","file":"sas"},"sass":{"title":"Sass (Sass)","file":"sass"},"scss":{"title":"Sass (Scss)","file":"scss"},"scala":{"title":"Scala","file":"scala"},"scheme":{"title":"Scheme","file":"scheme"},"smalltalk":{"title":"Smalltalk","file":"smalltalk"},"smarty":{"title":"Smarty","file":"smarty"},"sql":{"title":"SQL","file":"sql"},"soy":{"title":"Soy (Closure Template)","file":"soy"},"stylus":{"title":"Stylus","file":"stylus"},"swift":{"title":"Swift","file":"swift"},"tap":{"title":"TAP","file":"tap"},"tcl":{"title":"Tcl","file":"tcl"},"textile":{"title":"Textile","file":"textile"},"tt2":{"title":"Template Toolkit 2","file":"tt2"},"twig":{"title":"Twig","file":"twig"},"typescript":{"title":"TypeScript","file":"typescript"},"vbnet":{"title":"VB.Net","file":"vbnet"},"velocity":{"title":"Velocity","file":"velocity"},"verilog":{"title":"Verilog","file":"verilog"},"vhdl":{"title":"VHDL","file":"vhdl"},"vim":{"title":"vim","file":"vim"},"visual-basic":{"title":"Visual Basic","file":"visual-basic"},"wasm":{"title":"WebAssembly","file":"wasm"},"wiki":{"title":"Wiki markup","file":"wiki"},"xeora":{"title":"Xeora","file":"xeora"},"xojo":{"title":"Xojo (REALbasic)","file":"xojo"},"xquery":{"title":"XQuery","file":"xquery"},"yaml":{"title":"YAML","file":"yaml"}}/*END*/;});
+/**
+ * Highlights code in the Code bbcode.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Bbcode/Code
+ */
+define('WoltLabSuite/Core/Bbcode/Code',[
+ 'Language', 'WoltLabSuite/Core/Ui/Notification', 'WoltLabSuite/Core/Clipboard', 'WoltLabSuite/Core/Prism', 'prism/prism-meta'
+ ],
+ function(
+ Language, UiNotification, Clipboard, Prism, PrismMeta
+ )
+{
+ "use strict";
+
+ /** @const */ var CHUNK_SIZE = 50;
+
+ // Define idleify() for piecewiese highlighting to not block the UI thread.
+ var idleify = function (callback) {
+ return function () {
+ var args = arguments;
+ return new Promise(function (resolve, reject) {
+ var body = function () {
+ try {
+ resolve(callback.apply(null, args));
+ }
+ catch (e) {
+ reject(e);
+ }
+ };
+
+ if (window.requestIdleCallback) {
+ window.requestIdleCallback(body, { timeout: 5000 });
+ }
+ else {
+ setTimeout(body, 0);
+ }
+ });
+ };
+ };
+
+ /**
+ * @constructor
+ */
+ function Code(container) {
+ var matches;
+
+ this.container = container;
+ this.codeContainer = elBySel('.codeBoxCode > code', this.container);
+ this.language = null;
+ for (var i = 0; i < this.codeContainer.classList.length; i++) {
+ if ((matches = this.codeContainer.classList[i].match(/language-(.*)/))) {
+ this.language = matches[1];
+ }
+ }
+ }
+ Code.processAll = function () {
+ elBySelAll('.codeBox:not([data-processed])', document, function (codeBox) {
+ elData(codeBox, 'processed', '1');
+
+ var handle = new Code(codeBox);
+ if (handle.language) handle.highlight();
+ handle.createCopyButton();
+ })
+ };
+ Code.prototype = {
+ createCopyButton: function () {
+ var header = elBySel('.codeBoxHeader', this.container);
+ var button = elCreate('span');
+ button.className = 'icon icon24 fa-files-o pointer jsTooltip';
+ button.setAttribute('title', Language.get('wcf.message.bbcode.code.copy'));
+ button.addEventListener('click', function () {
+ Clipboard.copyElementTextToClipboard(this.codeContainer).then(function () {
+ UiNotification.show(Language.get('wcf.message.bbcode.code.copy.success'));
+ });
+ }.bind(this));
+
+ header.appendChild(button);
+ },
+ highlight: function () {
+ if (!this.language) {
+ return Promise.reject(new Error('No language detected'));
+ }
+ if (!PrismMeta[this.language]) {
+ return Promise.reject(new Error('Unknown language ' + this.language));
+ }
+
+ this.container.classList.add('highlighting');
+
+ return require(['prism/components/prism-' + PrismMeta[this.language].file])
+ .then(idleify(function () {
+ var grammar = Prism.languages[this.language];
+ if (!grammar) {
+ throw new Error('Invalid language ' + language + ' given.');
+ }
+
+ var container = elCreate('div');
+ container.innerHTML = Prism.highlight(this.codeContainer.textContent, grammar, this.language);
+ return container;
+ }.bind(this)))
+ .then(idleify(function (container) {
+ var highlighted = Prism.wscSplitIntoLines(container);
+ var highlightedLines = elBySelAll('[data-number]', highlighted);
+ var originalLines = elBySelAll('.codeBoxLine > span', this.codeContainer);
+
+ if (highlightedLines.length !== originalLines.length) {
+ throw new Error('Unreachable');
+ }
+
+ var promises = [];
+ for (var chunkStart = 0, max = highlightedLines.length; chunkStart < max; chunkStart += CHUNK_SIZE) {
+ promises.push(idleify(function (chunkStart) {
+ var chunkEnd = Math.min(chunkStart + CHUNK_SIZE, max);
+
+ for (var offset = chunkStart; offset < chunkEnd; offset++) {
+ originalLines[offset].parentNode.replaceChild(highlightedLines[offset], originalLines[offset]);
+ }
+ })(chunkStart));
+ }
+ return Promise.all(promises);
+ }.bind(this)))
+ .then(function () {
+ this.container.classList.remove('highlighting');
+ this.container.classList.add('highlighted');
+ }.bind(this))
+ }
+ }
+
+ return Code;
+});
+
+/**
+ * Generic handler for collapsible bbcode boxes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Bbcode/Collapsible
+ */
+define('WoltLabSuite/Core/Bbcode/Collapsible',[], function() {
+ "use strict";
+
+ var _containers = elByClass('jsCollapsibleBbcode');
+
+ /**
+ * @exports WoltLabSuite/Core/Bbcode/Collapsible
+ */
+ return {
+ observe: function() {
+ var container, toggleButton;
+ while (_containers.length) {
+ container = _containers[0];
+
+ // find the matching toggle button
+ toggleButton = null;
+ elBySelAll('.toggleButton:not(.jsToggleButtonEnabled)', container, function (button) {
+ //noinspection JSReferencingMutableVariableFromClosure
+ if (button.closest('.jsCollapsibleBbcode') === container) {
+ toggleButton = button;
+ }
+ });
+
+ if (toggleButton) {
+ (function (container, toggleButton) {
+ var toggle = function (event) {
+ if (container.classList.toggle('collapsed')) {
+ toggleButton.textContent = elData(toggleButton, 'title-expand');
+
+ if (event instanceof Event) {
+ // negative top value means the upper boundary is not within the viewport
+ var top = container.getBoundingClientRect().top;
+ if (top < 0) {
+ var y = window.pageYOffset + (top - 100);
+ if (y < 0) y = 0;
+ window.scrollTo(window.pageXOffset, y);
+ }
+ }
+ }
+ else {
+ toggleButton.textContent = elData(toggleButton, 'title-collapse');
+ }
+ };
+
+ toggleButton.classList.add('jsToggleButtonEnabled');
+ toggleButton.addEventListener(WCF_CLICK_EVENT, toggle);
+
+ // expand boxes that are initially scrolled
+ if (container.scrollTop !== 0) {
+ toggle();
+ }
+ container.addEventListener('scroll', function () {
+ if (container.classList.contains('collapsed')) {
+ toggle();
+ }
+ });
+ })(container, toggleButton);
+ }
+
+ container.classList.remove('jsCollapsibleBbcode');
+ }
+ }
+ };
+});
+
+/**
+ * Provides data of the active user.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Captcha
+ */
+define('WoltLabSuite/Core/Controller/Captcha',['Dictionary'], function(Dictionary) {
+ "use strict";
+
+ var _captchas = new Dictionary();
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Captcha
+ */
+ return {
+ /**
+ * Registers a captcha with the given identifier and callback used to get captcha data.
+ *
+ * @param {string} captchaId captcha identifier
+ * @param {function} callback callback to get captcha data
+ */
+ add: function(captchaId, callback) {
+ if (_captchas.has(captchaId)) {
+ throw new Error("Captcha with id '" + captchaId + "' is already registered.");
+ }
+
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback for parameter 'callback'.");
+ }
+
+ _captchas.set(captchaId, callback);
+ },
+
+ /**
+ * Deletes the captcha with the given identifier.
+ *
+ * @param {string} captchaId identifier of the captcha to be deleted
+ */
+ 'delete': function(captchaId) {
+ if (!_captchas.has(captchaId)) {
+ throw new Error("Unknown captcha with id '" + captchaId + "'.");
+ }
+
+ _captchas.delete(captchaId);
+ },
+
+ /**
+ * Returns true if a captcha with the given identifier exists.
+ *
+ * @param {string} captchaId captcha identifier
+ * @return {boolean}
+ */
+ has: function(captchaId) {
+ return _captchas.has(captchaId);
+ },
+
+ /**
+ * Returns the data of the captcha with the given identifier.
+ *
+ * @param {string} captchaId captcha identifier
+ * @return {Object} captcha data
+ */
+ getData: function(captchaId) {
+ if (!_captchas.has(captchaId)) {
+ throw new Error("Unknown captcha with id '" + captchaId + "'.");
+ }
+
+ return _captchas.get(captchaId)();
+ }
+ };
+});
+
+/**
+ * Clipboard API Handler.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Clipboard
+ */
+define(
+ 'WoltLabSuite/Core/Controller/Clipboard',[
+ 'Ajax', 'Core', 'Dictionary', 'EventHandler',
+ 'Language', 'List', 'ObjectMap', 'Dom/ChangeListener',
+ 'Dom/Traverse', 'Dom/Util', 'Ui/Confirmation', 'Ui/SimpleDropdown',
+ 'WoltLabSuite/Core/Ui/Page/Action', 'Ui/Screen'
+ ],
+ function(
+ Ajax, Core, Dictionary, EventHandler,
+ Language, List, ObjectMap, DomChangeListener,
+ DomTraverse, DomUtil, UiConfirmation, UiSimpleDropdown,
+ UiPageAction, UiScreen
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ return {
+ setup: function() {},
+ reload: function() {},
+ _initContainers: function() {},
+ _loadMarkedItems: function() {},
+ _markAll: function() {},
+ _mark: function() {},
+ _saveState: function() {},
+ _executeAction: function() {},
+ _executeProxyAction: function() {},
+ _unmarkAll: function() {},
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _rebuildMarkings: function() {},
+ hideEditor: function() {},
+ showEditor: function() {},
+ unmark: function() {}
+ };
+ }
+
+ var _containers = new Dictionary();
+ var _editors = new Dictionary();
+ var _editorDropdowns = new Dictionary();
+ var _elements = elByClass('jsClipboardContainer');
+ var _itemData = new ObjectMap();
+ var _knownCheckboxes = new List();
+ var _options = {};
+ var _reloadPageOnSuccess = new Dictionary();
+
+ var _callbackCheckbox = null;
+ var _callbackItem = null;
+ var _callbackUnmarkAll = null;
+
+ var _specialCheckboxSelector = '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]';
+
+ /**
+ * Clipboard API
+ *
+ * @exports WoltLabSuite/Core/Controller/Clipboard
+ */
+ return {
+ /**
+ * Initializes the clipboard API handler.
+ *
+ * @param {Object} options initialization options
+ */
+ setup: function(options) {
+ if (!options.pageClassName) {
+ throw new Error("Expected a non-empty string for parameter 'pageClassName'.");
+ }
+
+ if (_callbackCheckbox === null) {
+ _callbackCheckbox = this._mark.bind(this);
+ _callbackItem = this._executeAction.bind(this);
+ _callbackUnmarkAll = this._unmarkAll.bind(this);
+
+ _options = Core.extend({
+ hasMarkedItems: false,
+ pageClassNames: [options.pageClassName],
+ pageObjectId: 0
+ }, options);
+
+ delete _options.pageClassName;
+ }
+ else {
+ if (options.pageObjectId) {
+ throw new Error("Cannot load secondary clipboard with page object id set.");
+ }
+
+ _options.pageClassNames.push(options.pageClassName);
+ }
+
+ if (!Element.prototype.matches) {
+ Element.prototype.matches = Element.prototype.msMatchesSelector;
+ }
+
+ this._initContainers();
+
+ if (_options.hasMarkedItems && _elements.length) {
+ this._loadMarkedItems();
+ }
+
+ DomChangeListener.add('WoltLabSuite/Core/Controller/Clipboard', this._initContainers.bind(this));
+ },
+
+ /**
+ * Reloads the clipboard data.
+ */
+ reload: function() {
+ if (_containers.size) {
+ this._loadMarkedItems();
+ }
+ },
+
+ /**
+ * Initializes clipboard containers.
+ */
+ _initContainers: function() {
+ for (var i = 0, length = _elements.length; i < length; i++) {
+ var container = _elements[i];
+ var containerId = DomUtil.identify(container);
+ var containerData = _containers.get(containerId);
+
+ if (containerData === undefined) {
+ var markAll = elBySel('.jsClipboardMarkAll', container);
+
+ if (markAll !== null) {
+ if (markAll.matches(_specialCheckboxSelector)) {
+ var label = markAll.closest('label');
+ elAttr(label, 'role', 'checkbox');
+ elAttr(label, 'tabindex', '0');
+ elAttr(label, 'aria-checked', false);
+ elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.markAll'));
+
+ label.addEventListener('keyup', function (event) {
+ if (event.keyCode === 13 || event.keyCode === 32) {
+ checkbox.click();
+ }
+ });
+ }
+
+ elData(markAll, 'container-id', containerId);
+ markAll.addEventListener(WCF_CLICK_EVENT, this._markAll.bind(this));
+ }
+
+ containerData = {
+ checkboxes: elByClass('jsClipboardItem', container),
+ element: container,
+ markAll: markAll,
+ markedObjectIds: new List()
+ };
+ _containers.set(containerId, containerData);
+ }
+
+ for (var j = 0, innerLength = containerData.checkboxes.length; j < innerLength; j++) {
+ var checkbox = containerData.checkboxes[j];
+
+ if (!_knownCheckboxes.has(checkbox)) {
+ elData(checkbox, 'container-id', containerId);
+
+ (function(checkbox) {
+ if (checkbox.matches(_specialCheckboxSelector)) {
+ var label = checkbox.closest('label');
+ elAttr(label, 'role', 'checkbox');
+ elAttr(label, 'tabindex', '0');
+ elAttr(label, 'aria-checked', false);
+ elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.mark'));
+
+ label.addEventListener('keyup', function (event) {
+ if (event.keyCode === 13 || event.keyCode === 32) {
+ checkbox.click();
+ }
+ });
+ }
+
+ var link = checkbox.closest('a');
+ if (link === null) {
+ checkbox.addEventListener(WCF_CLICK_EVENT, _callbackCheckbox);
+ }
+ else {
+ // Firefox will always trigger the link if the checkbox is
+ // inside of one. Since 2000. Thanks Firefox.
+ checkbox.addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+
+ window.setTimeout(function () {
+ checkbox.checked = !checkbox.checked;
+
+ _callbackCheckbox(null, checkbox);
+ }, 10);
+ });
+ }
+ })(checkbox);
+
+ _knownCheckboxes.add(checkbox);
+ }
+ }
+ }
+ },
+
+ /**
+ * Loads marked items from clipboard.
+ */
+ _loadMarkedItems: function() {
+ Ajax.api(this, {
+ actionName: 'getMarkedItems',
+ parameters: {
+ pageClassNames: _options.pageClassNames,
+ pageObjectID: _options.pageObjectId
+ }
+ });
+ },
+
+ /**
+ * Marks or unmarks all visible items at once.
+ *
+ * @param {object} event event object
+ */
+ _markAll: function(event) {
+ var checkbox = event.currentTarget;
+ var isMarked = (checkbox.nodeName !== 'INPUT' || checkbox.checked);
+
+ if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+ elAttr(checkbox.parentNode, 'aria-checked', isMarked);
+ }
+
+ var objectIds = [];
+
+ var containerId = elData(checkbox, 'container-id');
+ var data = _containers.get(containerId);
+ var type = elData(data.element, 'type');
+
+ for (var i = 0, length = data.checkboxes.length; i < length; i++) {
+ var item = data.checkboxes[i];
+ var objectId = ~~elData(item, 'object-id');
+
+ if (isMarked) {
+ if (!item.checked) {
+ item.checked = true;
+
+ data.markedObjectIds.add(objectId);
+ objectIds.push(objectId);
+ }
+ }
+ else {
+ if (item.checked) {
+ item.checked = false;
+
+ data.markedObjectIds['delete'](objectId);
+ objectIds.push(objectId);
+ }
+ }
+
+ if (elAttr(item.parentNode, 'role') === 'checkbox') {
+ elAttr(item.parentNode, 'aria-checked', isMarked);
+ }
+
+ var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
+ if (clipboardObject !== null) {
+ clipboardObject.classList[(isMarked ? 'addClass' : 'removeClass')]('jsMarked');
+ }
+ }
+
+ this._saveState(type, objectIds, isMarked);
+ },
+
+ /**
+ * Marks or unmarks an individual item.
+ *
+ * @param {object} event event object
+ * @param {Element=} checkbox checkbox element
+ */
+ _mark: function(event, checkbox) {
+ checkbox = (event instanceof Event) ? event.currentTarget : checkbox;
+ var objectId = ~~elData(checkbox, 'object-id');
+ var isMarked = checkbox.checked;
+ var containerId = elData(checkbox, 'container-id');
+ var data = _containers.get(containerId);
+ var type = elData(data.element, 'type');
+
+ var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
+ data.markedObjectIds[(isMarked ? 'add' : 'delete')](objectId);
+ clipboardObject.classList[(isMarked) ? 'add' : 'remove']('jsMarked');
+
+ if (data.markAll !== null) {
+ var markedAll = true;
+ for (var i = 0, length = data.checkboxes.length; i < length; i++) {
+ if (!data.checkboxes[i].checked) {
+ markedAll = false;
+
+ break;
+ }
+ }
+
+ data.markAll.checked = markedAll;
+
+ if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
+ elAttr(data.markAll.parentNode, 'aria-checked', isMarked);
+ }
+ }
+
+ if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+ elAttr(checkbox.parentNode, 'aria-checked', checkbox.checked);
+ }
+
+ this._saveState(type, [ objectId ], isMarked);
+ },
+
+ /**
+ * Saves the state for given item object ids.
+ *
+ * @param {string} type object type
+ * @param {int[]} objectIds item object ids
+ * @param {boolean} isMarked true if marked
+ */
+ _saveState: function(type, objectIds, isMarked) {
+ Ajax.api(this, {
+ actionName: (isMarked ? 'mark' : 'unmark'),
+ parameters: {
+ pageClassNames: _options.pageClassNames,
+ pageObjectID: _options.pageObjectId,
+ objectIDs: objectIds,
+ objectType: type
+ }
+ });
+ },
+
+ /**
+ * Executes an editor action.
+ *
+ * @param {object} event event object
+ */
+ _executeAction: function(event) {
+ var listItem = event.currentTarget;
+ var data = _itemData.get(listItem);
+
+ if (data.url) {
+ window.location.href = data.url;
+ return;
+ }
+
+ var triggerEvent = function() {
+ var type = elData(listItem, 'type');
+
+ EventHandler.fire('com.woltlab.wcf.clipboard', type, {
+ data: data,
+ listItem: listItem,
+ responseData: null
+ });
+ };
+
+ //noinspection JSUnresolvedVariable
+ var confirmMessage = (typeof data.internalData.confirmMessage === 'string') ? data.internalData.confirmMessage : '';
+ var fireEvent = true;
+
+ if (typeof data.parameters === 'object' && data.parameters.actionName && data.parameters.className) {
+ if (data.parameters.actionName === 'unmarkAll' || Array.isArray(data.parameters.objectIDs)) {
+ if (confirmMessage.length) {
+ //noinspection JSUnresolvedVariable
+ var template = (typeof data.internalData.template === 'string') ? data.internalData.template : '';
+
+ UiConfirmation.show({
+ confirm: (function() {
+ var formData = {};
+
+ if (template.length) {
+ var items = elBySelAll('input, select, textarea', UiConfirmation.getContentElement());
+ for (var i = 0, length = items.length; i < length; i++) {
+ var item = items[i];
+ var name = elAttr(item, 'name');
+
+ switch (item.nodeName) {
+ case 'INPUT':
+ if ((item.type !== "checkbox" && item.type !== "radio") || item.checked) {
+ formData[name] = elAttr(item, 'value');
+ }
+ break;
+
+ case 'SELECT':
+ formData[name] = item.value;
+ break;
+
+ case 'TEXTAREA':
+ formData[name] = item.value.trim();
+ break;
+ }
+ }
+ }
+
+ //noinspection JSUnresolvedFunction
+ this._executeProxyAction(listItem, data, formData);
+ }).bind(this),
+ message: confirmMessage,
+ template: template
+ });
+ }
+ else {
+ this._executeProxyAction(listItem, data);
+ }
+ }
+ }
+ else if (confirmMessage.length) {
+ fireEvent = false;
+
+ UiConfirmation.show({
+ confirm: triggerEvent,
+ message: confirmMessage
+ });
+ }
+
+ if (fireEvent) {
+ triggerEvent();
+ }
+ },
+
+ /**
+ * Forwards clipboard actions to an individual handler.
+ *
+ * @param {Element} listItem dropdown item element
+ * @param {Object} data action data
+ * @param {Object?} formData form data
+ */
+ _executeProxyAction: function(listItem, data, formData) {
+ formData = formData || {};
+
+ var objectIds = (data.parameters.actionName !== 'unmarkAll') ? data.parameters.objectIDs : [];
+ var parameters = { data: formData };
+
+ //noinspection JSUnresolvedVariable
+ if (typeof data.internalData.parameters === 'object') {
+ //noinspection JSUnresolvedVariable
+ for (var key in data.internalData.parameters) {
+ //noinspection JSUnresolvedVariable
+ if (data.internalData.parameters.hasOwnProperty(key)) {
+ //noinspection JSUnresolvedVariable
+ parameters[key] = data.internalData.parameters[key];
+ }
+ }
+ }
+
+ Ajax.api(this, {
+ actionName: data.parameters.actionName,
+ className: data.parameters.className,
+ objectIDs: objectIds,
+ parameters: parameters
+ }, (function(responseData) {
+ if (data.actionName !== 'unmarkAll') {
+ var type = elData(listItem, 'type');
+
+ EventHandler.fire('com.woltlab.wcf.clipboard', type, {
+ data: data,
+ listItem: listItem,
+ responseData: responseData
+ });
+
+ if (_reloadPageOnSuccess.has(type) && _reloadPageOnSuccess.get(type).indexOf(responseData.actionName) !== -1) {
+ window.location.reload();
+ return;
+ }
+ }
+
+ this._loadMarkedItems();
+ }).bind(this));
+ },
+
+ /**
+ * Unmarks all clipboard items for an object type.
+ *
+ * @param {object} event event object
+ */
+ _unmarkAll: function(event) {
+ var type = elData(event.currentTarget, 'type');
+
+ Ajax.api(this, {
+ actionName: 'unmarkAll',
+ parameters: {
+ objectType: type
+ }
+ });
+ },
+
+ /**
+ * Sets up ajax request object.
+ *
+ * @return {object} request options
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\clipboard\\item\\ClipboardItemAction'
+ }
+ };
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ if (data.actionName === 'unmarkAll') {
+ _containers.forEach((function(containerData) {
+ if (elData(containerData.element, 'type') === data.returnValues.objectType) {
+ var clipboardObjects = elByClass('jsMarked', containerData.element);
+ while (clipboardObjects.length) {
+ clipboardObjects[0].classList.remove('jsMarked');
+ }
+
+ if (containerData.markAll !== null) {
+ containerData.markAll.checked = false;
+
+ if (elAttr(containerData.markAll.parentNode, 'role') === 'checkbox') {
+ elAttr(containerData.markAll.parentNode, 'aria-checked', false);
+ }
+ }
+ for (var i = 0, length = containerData.checkboxes.length; i < length; i++) {
+ containerData.checkboxes[i].checked = false;
+
+ if (elAttr(containerData.checkboxes[i].parentNode, 'role') === 'checkbox') {
+ elAttr(containerData.checkboxes[i].parentNode, 'aria-checked', false);
+ }
+ }
+
+ UiPageAction.remove('wcfClipboard-' + data.returnValues.objectType);
+ }
+ }).bind(this));
+
+ return;
+ }
+
+ _itemData = new ObjectMap();
+ _reloadPageOnSuccess = new Dictionary();
+
+ // rebuild markings
+ _containers.forEach((function(containerData) {
+ var typeName = elData(containerData.element, 'type');
+
+ //noinspection JSUnresolvedVariable
+ var objectIds = (data.returnValues.markedItems && data.returnValues.markedItems.hasOwnProperty(typeName)) ? data.returnValues.markedItems[typeName] : [];
+ this._rebuildMarkings(containerData, objectIds);
+ }).bind(this));
+
+ var keepEditors = [], typeName;
+ if (data.returnValues && data.returnValues.items) {
+ for (typeName in data.returnValues.items) {
+ if (data.returnValues.items.hasOwnProperty(typeName)) {
+ keepEditors.push(typeName);
+ }
+ }
+ }
+
+ // clear editors
+ _editors.forEach(function(editor, typeName) {
+ if (keepEditors.indexOf(typeName) === -1) {
+ UiPageAction.remove('wcfClipboard-' + typeName);
+
+ _editorDropdowns.get(typeName).innerHTML = '';
+ }
+ });
+
+ // no items
+ if (!data.returnValues || !data.returnValues.items) {
+ return;
+ }
+
+ // rebuild editors
+ var actionName, created, dropdown, editor, typeData;
+ var divider, item, itemData, itemIndex, label, unmarkAll;
+ for (typeName in data.returnValues.items) {
+ if (!data.returnValues.items.hasOwnProperty(typeName)) {
+ continue;
+ }
+
+ typeData = data.returnValues.items[typeName];
+ //noinspection JSUnresolvedVariable
+ _reloadPageOnSuccess.set(typeName, typeData.reloadPageOnSuccess);
+ created = false;
+
+ editor = _editors.get(typeName);
+ dropdown = _editorDropdowns.get(typeName);
+ if (editor === undefined) {
+ created = true;
+
+ editor = elCreate('a');
+ editor.className = 'dropdownToggle';
+ editor.textContent = typeData.label;
+
+ _editors.set(typeName, editor);
+
+ dropdown = elCreate('ol');
+ dropdown.className = 'dropdownMenu';
+
+ _editorDropdowns.set(typeName, dropdown);
+ }
+ else {
+ editor.textContent = typeData.label;
+ dropdown.innerHTML = '';
+ }
+
+ // create editor items
+ for (itemIndex in typeData.items) {
+ if (!typeData.items.hasOwnProperty(itemIndex)) {
+ continue;
+ }
+
+ itemData = typeData.items[itemIndex];
+
+ item = elCreate('li');
+ label = elCreate('span');
+ label.textContent = itemData.label;
+ item.appendChild(label);
+ dropdown.appendChild(item);
+
+ elData(item, 'type', typeName);
+ item.addEventListener(WCF_CLICK_EVENT, _callbackItem);
+
+ _itemData.set(item, itemData);
+ }
+
+ divider = elCreate('li');
+ divider.classList.add('dropdownDivider');
+ dropdown.appendChild(divider);
+
+ // add 'unmark all'
+ unmarkAll = elCreate('li');
+ elData(unmarkAll, 'type', typeName);
+ label = elCreate('span');
+ label.textContent = Language.get('wcf.clipboard.item.unmarkAll');
+ unmarkAll.appendChild(label);
+ unmarkAll.addEventListener(WCF_CLICK_EVENT, _callbackUnmarkAll);
+ dropdown.appendChild(unmarkAll);
+
+ if (keepEditors.indexOf(typeName) !== -1) {
+ actionName = 'wcfClipboard-' + typeName;
+
+ if (UiPageAction.has(actionName)) {
+ UiPageAction.show(actionName);
+ }
+ else {
+ UiPageAction.add(actionName, editor);
+ }
+ }
+
+ if (created) {
+ editor.parentNode.classList.add('dropdown');
+ editor.parentNode.appendChild(dropdown);
+ UiSimpleDropdown.init(editor);
+ }
+ }
+ },
+
+ /**
+ * Rebuilds the mark state for each item.
+ *
+ * @param {Object} data container data
+ * @param {int[]} objectIds item object ids
+ */
+ _rebuildMarkings: function(data, objectIds) {
+ var markAll = true;
+
+ for (var i = 0, length = data.checkboxes.length; i < length; i++) {
+ var checkbox = data.checkboxes[i];
+ var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
+
+ var isMarked = (objectIds.indexOf(~~elData(checkbox, 'object-id')) !== -1);
+ if (!isMarked) markAll = false;
+
+ checkbox.checked = isMarked;
+ clipboardObject.classList[(isMarked ? 'add' : 'remove')]('jsMarked');
+
+ if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+ elAttr(checkbox.parentNode, 'aria-checked', isMarked);
+ }
+ }
+
+ if (data.markAll !== null) {
+ data.markAll.checked = markAll;
+
+ if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
+ elAttr(data.markAll.parentNode, 'aria-checked', markAll);
+ }
+
+ var parent = data.markAll;
+ while (parent = parent.parentNode) {
+ if (parent instanceof Element && parent.classList.contains('columnMark')) {
+ parent = parent.parentNode;
+ break;
+ }
+ }
+
+ if (parent) {
+ parent.classList[(markAll ? 'add' : 'remove')]('jsMarked');
+ }
+ }
+ },
+
+ /**
+ * Hides the clipboard editor for the given object type.
+ *
+ * @param {string} objectType
+ */
+ hideEditor: function(objectType) {
+ UiPageAction.remove('wcfClipboard-' + objectType);
+
+ UiScreen.pageOverlayOpen();
+ },
+
+ /**
+ * Shows the clipboard editor.
+ */
+ showEditor: function() {
+ this._loadMarkedItems();
+
+ UiScreen.pageOverlayClose();
+ },
+
+ /**
+ * Unmarks the objects with given clipboard object type and ids.
+ *
+ * @param {string} objectType
+ * @param {int[]} objectIds
+ */
+ unmark: function(objectType, objectIds) {
+ this._saveState(objectType, objectIds, false);
+ }
+ };
+});
+
+/**
+ * Provides helper functions for Exif metadata handling.
+ *
+ * @author Maximilian Mader
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Image/ExifUtil
+ */
+define('WoltLabSuite/Core/Image/ExifUtil',[], function() {
+ "use strict";
+
+ var _tagNames = {
+ 'SOI': 0xD8, // Start of image
+ 'APP0': 0xE0, // JFIF tag
+ 'APP1': 0xE1, // EXIF / XMP
+ 'APP2': 0xE2, // General purpose tag
+ 'APP3': 0xE3, // General purpose tag
+ 'APP4': 0xE4, // General purpose tag
+ 'APP5': 0xE5, // General purpose tag
+ 'APP6': 0xE6, // General purpose tag
+ 'APP7': 0xE7, // General purpose tag
+ 'APP8': 0xE8, // General purpose tag
+ 'APP9': 0xE9, // General purpose tag
+ 'APP10': 0xEA, // General purpose tag
+ 'APP11': 0xEB, // General purpose tag
+ 'APP12': 0xEC, // General purpose tag
+ 'APP13': 0xED, // General purpose tag
+ 'APP14': 0xEE, // Often used to store copyright information
+ 'COM': 0xFE, // Comments
+ };
+
+ // Known sequence signatures
+ var _signatureEXIF = 'Exif';
+ var _signatureXMP = 'http://ns.adobe.com/xap/1.0/';
+
+ return {
+ /**
+ * Extracts the EXIF / XMP sections of a JPEG blob.
+ *
+ * @param blob {Blob} JPEG blob
+ * @returns {Promise<Uint8Array | TypeError>} Promise resolving with the EXIF / XMP sections
+ */
+ getExifBytesFromJpeg: function (blob) {
+ return new Promise(function (resolve, reject) {
+ if (!(blob instanceof Blob) && !(blob instanceof File)) {
+ return reject(new TypeError('The argument must be a Blob or a File'));
+ }
+
+ var reader = new FileReader();
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ reader.addEventListener('load', function() {
+ var buffer = reader.result;
+ var bytes = new Uint8Array(buffer);
+ var exif = new Uint8Array();
+
+ if (bytes[0] !== 0xFF && bytes[1] !== _tagNames.SOI) {
+ return reject(new Error('Not a JPEG'));
+ }
+
+ for (var i = 2; i < bytes.length;) {
+ // each sequence starts with 0xFF
+ if (bytes[i] !== 0xFF) break;
+
+ var length = 2 + ((bytes[i + 2] << 8) | bytes[i + 3]);
+
+ // Check if the next byte indicates an EXIF sequence
+ if (bytes[i + 1] === _tagNames.APP1) {
+ var signature = '';
+ for (var j = i + 4; bytes[j] !== 0 && j < bytes.length; j++) {
+ signature += String.fromCharCode(bytes[j]);
+ }
+
+ // Only copy Exif and XMP data
+ if (signature === _signatureEXIF || signature === _signatureXMP) {
+ // append the found EXIF sequence, usually only a single EXIF (APP1) sequence should be defined
+ var sequence = Array.prototype.slice.call(bytes, i, length + i); // IE11 does not have slice in the Uint8Array prototype
+ var concat = new Uint8Array(exif.length + sequence.length);
+ concat.set(exif);
+ concat.set(sequence, exif.length);
+ exif = concat;
+ }
+ }
+
+ i += length
+ }
+
+ // No EXIF data found
+ resolve(exif);
+ });
+
+ reader.readAsArrayBuffer(blob);
+ });
+ },
+
+ /**
+ * Removes all EXIF and XMP sections of a JPEG blob.
+ *
+ * @param blob {Blob} JPEG blob
+ * @returns {Promise<Blob | TypeError>} Promise resolving with the altered JPEG blob
+ */
+ removeExifData: function (blob) {
+ return new Promise(function (resolve, reject) {
+ if (!(blob instanceof Blob) && !(blob instanceof File)) {
+ return reject(new TypeError('The argument must be a Blob or a File'));
+ }
+
+ var reader = new FileReader();
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ reader.addEventListener('load', function () {
+ var buffer = reader.result;
+ var bytes = new Uint8Array(buffer);
+
+ if (bytes[0] !== 0xFF && bytes[1] !== _tagNames.SOI) {
+ return reject(new Error('Not a JPEG'));
+ }
+
+ for (var i = 2; i < bytes.length;) {
+ // each sequence starts with 0xFF
+ if (bytes[i] !== 0xFF) break;
+
+ var length = 2 + ((bytes[i + 2] << 8) | bytes[i + 3]);
+
+ // Check if the next byte indicates an EXIF sequence
+ if (bytes[i + 1] === _tagNames.APP1) {
+ var signature = '';
+ for (var j = i + 4; bytes[j] !== 0 && j < bytes.length; j++) {
+ signature += String.fromCharCode(bytes[j]);
+ }
+
+ // Only remove Exif and XMP data
+ if (signature === _signatureEXIF || signature === _signatureXMP) {
+ var start = Array.prototype.slice.call(bytes, 0, i);
+ var end = Array.prototype.slice.call(bytes, i + length);
+ bytes = new Uint8Array(start.length + end.length);
+ bytes.set(start, 0);
+ bytes.set(end, start.length);
+ }
+ }
+ else {
+ i += length;
+ }
+ }
+
+ resolve(new Blob([bytes], {type: blob.type}));
+ });
+
+ reader.readAsArrayBuffer(blob);
+ });
+ },
+
+ /**
+ * Overrides the APP1 (EXIF / XMP) sections of a JPEG blob with the given data.
+ *
+ * @param blob {Blob} JPEG blob
+ * @param exif {Uint8Array} APP1 sections
+ * @returns {Promise<Blob | never>} Promise resolving with the altered JPEG blob
+ */
+ setExifData: function (blob, exif) {
+ return this.removeExifData(blob).then(function (blob) {
+ return new Promise(function (resolve) {
+ var reader = new FileReader();
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ reader.addEventListener('load', function () {
+ var buffer = reader.result;
+ var bytes = new Uint8Array(buffer);
+ var offset = 2;
+
+ // check if the second tag is the JFIF tag
+ if (bytes[2] === 0xFF && bytes[3] === _tagNames.APP0) {
+ offset += 2 + ((bytes[4] << 8) | bytes[5]);
+ }
+
+ var start = Array.prototype.slice.call(bytes, 0, offset);
+ var end = Array.prototype.slice.call(bytes, offset);
+
+ bytes = new Uint8Array(start.length + exif.length + end.length);
+ bytes.set(start);
+ bytes.set(exif, offset);
+ bytes.set(end, offset + exif.length);
+
+ resolve(new Blob([bytes], {type: blob.type}));
+ });
+
+ reader.readAsArrayBuffer(blob);
+ });
+ });
+ }
+ };
+});
+
+/**
+ * Provides helper functions for Image metadata handling.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Image/ImageUtil
+ */
+define('WoltLabSuite/Core/Image/ImageUtil',[], function() {
+ "use strict";
+
+ return {
+ /**
+ * Returns whether the given canvas contains transparent pixels.
+ *
+ * @param image {Canvas} Canvas to check
+ * @returns {bool}
+ */
+ containsTransparentPixels: function (canvas) {
+ var imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
+
+ for (var i = 3, max = imageData.data.length; i < max; i += 4) {
+ if (imageData.data[i] !== 255) return true;
+ }
+
+ return false;
+ }
+ };
+});
+
+/* (The MIT License)
+
+Copyright (C) 2014-2017 by Vitaly Puzrin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+/* pica 5.0.0 nodeca/pica */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define('Pica',[],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pica = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+// Collection of math functions
+//
+// 1. Combine components together
+// 2. Has async init to load wasm modules
+//
+ 'use strict';
+
+ var inherits = require('inherits');
+
+ var Multimath = require('multimath');
+
+ var mm_unsharp_mask = require('multimath/lib/unsharp_mask');
+
+ var mm_resize = require('./mm_resize');
+
+ function MathLib(requested_features) {
+ var __requested_features = requested_features || [];
+
+ var features = {
+ js: __requested_features.indexOf('js') >= 0,
+ wasm: __requested_features.indexOf('wasm') >= 0
+ };
+ Multimath.call(this, features);
+ this.features = {
+ js: features.js,
+ wasm: features.wasm && this.has_wasm
+ };
+ this.use(mm_unsharp_mask);
+ this.use(mm_resize);
+ }
+
+ inherits(MathLib, Multimath);
+
+ MathLib.prototype.resizeAndUnsharp = function resizeAndUnsharp(options, cache) {
+ var result = this.resize(options, cache);
+
+ if (options.unsharpAmount) {
+ this.unsharp_mask(result, options.toWidth, options.toHeight, options.unsharpAmount, options.unsharpRadius, options.unsharpThreshold);
+ }
+
+ return result;
+ };
+
+ module.exports = MathLib;
+
+ },{"./mm_resize":4,"inherits":15,"multimath":16,"multimath/lib/unsharp_mask":19}],2:[function(require,module,exports){
+// Resize convolvers, pure JS implementation
+//
+ 'use strict'; // Precision of fixed FP values
+//var FIXED_FRAC_BITS = 14;
+
+ function clampTo8(i) {
+ return i < 0 ? 0 : i > 255 ? 255 : i;
+ } // Convolve image in horizontal directions and transpose output. In theory,
+// transpose allow:
+//
+// - use the same convolver for both passes (this fails due different
+// types of input array and temporary buffer)
+// - making vertical pass by horisonltal lines inprove CPU cache use.
+//
+// But in real life this doesn't work :)
+//
+
+
+ function convolveHorizontally(src, dest, srcW, srcH, destW, filters) {
+ var r, g, b, a;
+ var filterPtr, filterShift, filterSize;
+ var srcPtr, srcY, destX, filterVal;
+ var srcOffset = 0,
+ destOffset = 0; // For each row
+
+ for (srcY = 0; srcY < srcH; srcY++) {
+ filterPtr = 0; // Apply precomputed filters to each destination row point
+
+ for (destX = 0; destX < destW; destX++) {
+ // Get the filter that determines the current output pixel.
+ filterShift = filters[filterPtr++];
+ filterSize = filters[filterPtr++];
+ srcPtr = srcOffset + filterShift * 4 | 0;
+ r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a
+
+ for (; filterSize > 0; filterSize--) {
+ filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10)
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
+
+ a = a + filterVal * src[srcPtr + 3] | 0;
+ b = b + filterVal * src[srcPtr + 2] | 0;
+ g = g + filterVal * src[srcPtr + 1] | 0;
+ r = r + filterVal * src[srcPtr] | 0;
+ srcPtr = srcPtr + 4 | 0;
+ } // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
+ //
+ // (!) Add 1/2 of value before clamping to get proper rounding. In other
+ // case brightness loss will be noticeable if you resize image with white
+ // border and place it on white background.
+ //
+
+
+ dest[destOffset + 3] = clampTo8(a + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset] = clampTo8(r + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ destOffset = destOffset + srcH * 4 | 0;
+ }
+
+ destOffset = (srcY + 1) * 4 | 0;
+ srcOffset = (srcY + 1) * srcW * 4 | 0;
+ }
+ } // Technically, convolvers are the same. But input array and temporary
+// buffer can be of different type (especially, in old browsers). So,
+// keep code in separate functions to avoid deoptimizations & speed loss.
+
+
+ function convolveVertically(src, dest, srcW, srcH, destW, filters) {
+ var r, g, b, a;
+ var filterPtr, filterShift, filterSize;
+ var srcPtr, srcY, destX, filterVal;
+ var srcOffset = 0,
+ destOffset = 0; // For each row
+
+ for (srcY = 0; srcY < srcH; srcY++) {
+ filterPtr = 0; // Apply precomputed filters to each destination row point
+
+ for (destX = 0; destX < destW; destX++) {
+ // Get the filter that determines the current output pixel.
+ filterShift = filters[filterPtr++];
+ filterSize = filters[filterPtr++];
+ srcPtr = srcOffset + filterShift * 4 | 0;
+ r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a
+
+ for (; filterSize > 0; filterSize--) {
+ filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10)
+ // Big thanks to @mraleph (Vyacheslav Egorov) for the tip.
+
+ a = a + filterVal * src[srcPtr + 3] | 0;
+ b = b + filterVal * src[srcPtr + 2] | 0;
+ g = g + filterVal * src[srcPtr + 1] | 0;
+ r = r + filterVal * src[srcPtr] | 0;
+ srcPtr = srcPtr + 4 | 0;
+ } // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with FIXED_FRAC_BITS bits of fractional part.
+ //
+ // (!) Add 1/2 of value before clamping to get proper rounding. In other
+ // case brightness loss will be noticeable if you resize image with white
+ // border and place it on white background.
+ //
+
+
+ dest[destOffset + 3] = clampTo8(a + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 2] = clampTo8(b + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset + 1] = clampTo8(g + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ dest[destOffset] = clampTo8(r + (1 << 13) >> 14
+ /*FIXED_FRAC_BITS*/
+ );
+ destOffset = destOffset + srcH * 4 | 0;
+ }
+
+ destOffset = (srcY + 1) * 4 | 0;
+ srcOffset = (srcY + 1) * srcW * 4 | 0;
+ }
+ }
+
+ module.exports = {
+ convolveHorizontally: convolveHorizontally,
+ convolveVertically: convolveVertically
+ };
+
+ },{}],3:[function(require,module,exports){
+// This is autogenerated file from math.wasm, don't edit.
+//
+ 'use strict';
+ /* eslint-disable max-len */
+
+ module.exports = 'AGFzbQEAAAABFAJgBn9/f39/fwBgB39/f39/f38AAg8BA2VudgZtZW1vcnkCAAEDAwIAAQQEAXAAAAcZAghjb252b2x2ZQAACmNvbnZvbHZlSFYAAQkBAArmAwLBAwEQfwJAIANFDQAgBEUNACAFQQRqIRVBACEMQQAhDQNAIA0hDkEAIRFBACEHA0AgB0ECaiESAn8gBSAHQQF0IgdqIgZBAmouAQAiEwRAQQAhCEEAIBNrIRQgFSAHaiEPIAAgDCAGLgEAakECdGohEEEAIQlBACEKQQAhCwNAIBAoAgAiB0EYdiAPLgEAIgZsIAtqIQsgB0H/AXEgBmwgCGohCCAHQRB2Qf8BcSAGbCAKaiEKIAdBCHZB/wFxIAZsIAlqIQkgD0ECaiEPIBBBBGohECAUQQFqIhQNAAsgEiATagwBC0EAIQtBACEKQQAhCUEAIQggEgshByABIA5BAnRqIApBgMAAakEOdSIGQf8BIAZB/wFIG0EQdEGAgPwHcUEAIAZBAEobIAtBgMAAakEOdSIGQf8BIAZB/wFIG0EYdEEAIAZBAEobciAJQYDAAGpBDnUiBkH/ASAGQf8BSBtBCHRBgP4DcUEAIAZBAEobciAIQYDAAGpBDnUiBkH/ASAGQf8BSBtB/wFxQQAgBkEAShtyNgIAIA4gA2ohDiARQQFqIhEgBEcNAAsgDCACaiEMIA1BAWoiDSADRw0ACwsLIQACQEEAIAIgAyAEIAUgABAAIAJBACAEIAUgBiABEAALCw==';
+
+ },{}],4:[function(require,module,exports){
+ 'use strict';
+
+ module.exports = {
+ name: 'resize',
+ fn: require('./resize'),
+ wasm_fn: require('./resize_wasm'),
+ wasm_src: require('./convolve_wasm_base64')
+ };
+
+ },{"./convolve_wasm_base64":3,"./resize":5,"./resize_wasm":8}],5:[function(require,module,exports){
+ 'use strict';
+
+ var createFilters = require('./resize_filter_gen');
+
+ var convolveHorizontally = require('./convolve').convolveHorizontally;
+
+ var convolveVertically = require('./convolve').convolveVertically;
+
+ function resetAlpha(dst, width, height) {
+ var ptr = 3,
+ len = width * height * 4 | 0;
+
+ while (ptr < len) {
+ dst[ptr] = 0xFF;
+ ptr = ptr + 4 | 0;
+ }
+ }
+
+ module.exports = function resize(options) {
+ var src = options.src;
+ var srcW = options.width;
+ var srcH = options.height;
+ var destW = options.toWidth;
+ var destH = options.toHeight;
+ var scaleX = options.scaleX || options.toWidth / options.width;
+ var scaleY = options.scaleY || options.toHeight / options.height;
+ var offsetX = options.offsetX || 0;
+ var offsetY = options.offsetY || 0;
+ var dest = options.dest || new Uint8Array(destW * destH * 4);
+ var quality = typeof options.quality === 'undefined' ? 3 : options.quality;
+ var alpha = options.alpha || false;
+ var filtersX = createFilters(quality, srcW, destW, scaleX, offsetX),
+ filtersY = createFilters(quality, srcH, destH, scaleY, offsetY);
+ var tmp = new Uint8Array(destW * srcH * 4); // To use single function we need src & tmp of the same type.
+ // But src can be CanvasPixelArray, and tmp - Uint8Array. So, keep
+ // vertical and horizontal passes separately to avoid deoptimization.
+
+ convolveHorizontally(src, tmp, srcW, srcH, destW, filtersX);
+ convolveVertically(tmp, dest, srcH, destW, destH, filtersY); // That's faster than doing checks in convolver.
+ // !!! Note, canvas data is not premultipled. We don't need other
+ // alpha corrections.
+
+ if (!alpha) resetAlpha(dest, destW, destH);
+ return dest;
+ };
+
+ },{"./convolve":2,"./resize_filter_gen":6}],6:[function(require,module,exports){
+// Calculate convolution filters for each destination point,
+// and pack data to Int16Array:
+//
+// [ shift, length, data..., shift2, length2, data..., ... ]
+//
+// - shift - offset in src image
+// - length - filter length (in src points)
+// - data - filter values sequence
+//
+ 'use strict';
+
+ var FILTER_INFO = require('./resize_filter_info'); // Precision of fixed FP values
+
+
+ var FIXED_FRAC_BITS = 14;
+
+ function toFixedPoint(num) {
+ return Math.round(num * ((1 << FIXED_FRAC_BITS) - 1));
+ }
+
+ module.exports = function resizeFilterGen(quality, srcSize, destSize, scale, offset) {
+ var filterFunction = FILTER_INFO[quality].filter;
+ var scaleInverted = 1.0 / scale;
+ var scaleClamped = Math.min(1.0, scale); // For upscale
+ // Filter window (averaging interval), scaled to src image
+
+ var srcWindow = FILTER_INFO[quality].win / scaleClamped;
+ var destPixel, srcPixel, srcFirst, srcLast, filterElementSize, floatFilter, fxpFilter, total, pxl, idx, floatVal, filterTotal, filterVal;
+ var leftNotEmpty, rightNotEmpty, filterShift, filterSize;
+ var maxFilterElementSize = Math.floor((srcWindow + 1) * 2);
+ var packedFilter = new Int16Array((maxFilterElementSize + 2) * destSize);
+ var packedFilterPtr = 0;
+ var slowCopy = !packedFilter.subarray || !packedFilter.set; // For each destination pixel calculate source range and built filter values
+
+ for (destPixel = 0; destPixel < destSize; destPixel++) {
+ // Scaling should be done relative to central pixel point
+ srcPixel = (destPixel + 0.5) * scaleInverted + offset;
+ srcFirst = Math.max(0, Math.floor(srcPixel - srcWindow));
+ srcLast = Math.min(srcSize - 1, Math.ceil(srcPixel + srcWindow));
+ filterElementSize = srcLast - srcFirst + 1;
+ floatFilter = new Float32Array(filterElementSize);
+ fxpFilter = new Int16Array(filterElementSize);
+ total = 0.0; // Fill filter values for calculated range
+
+ for (pxl = srcFirst, idx = 0; pxl <= srcLast; pxl++, idx++) {
+ floatVal = filterFunction((pxl + 0.5 - srcPixel) * scaleClamped);
+ total += floatVal;
+ floatFilter[idx] = floatVal;
+ } // Normalize filter, convert to fixed point and accumulate conversion error
+
+
+ filterTotal = 0;
+
+ for (idx = 0; idx < floatFilter.length; idx++) {
+ filterVal = floatFilter[idx] / total;
+ filterTotal += filterVal;
+ fxpFilter[idx] = toFixedPoint(filterVal);
+ } // Compensate normalization error, to minimize brightness drift
+
+
+ fxpFilter[destSize >> 1] += toFixedPoint(1.0 - filterTotal); //
+ // Now pack filter to useable form
+ //
+ // 1. Trim heading and tailing zero values, and compensate shitf/length
+ // 2. Put all to single array in this format:
+ //
+ // [ pos shift, data length, value1, value2, value3, ... ]
+ //
+
+ leftNotEmpty = 0;
+
+ while (leftNotEmpty < fxpFilter.length && fxpFilter[leftNotEmpty] === 0) {
+ leftNotEmpty++;
+ }
+
+ if (leftNotEmpty < fxpFilter.length) {
+ rightNotEmpty = fxpFilter.length - 1;
+
+ while (rightNotEmpty > 0 && fxpFilter[rightNotEmpty] === 0) {
+ rightNotEmpty--;
+ }
+
+ filterShift = srcFirst + leftNotEmpty;
+ filterSize = rightNotEmpty - leftNotEmpty + 1;
+ packedFilter[packedFilterPtr++] = filterShift; // shift
+
+ packedFilter[packedFilterPtr++] = filterSize; // size
+
+ if (!slowCopy) {
+ packedFilter.set(fxpFilter.subarray(leftNotEmpty, rightNotEmpty + 1), packedFilterPtr);
+ packedFilterPtr += filterSize;
+ } else {
+ // fallback for old IE < 11, without subarray/set methods
+ for (idx = leftNotEmpty; idx <= rightNotEmpty; idx++) {
+ packedFilter[packedFilterPtr++] = fxpFilter[idx];
+ }
+ }
+ } else {
+ // zero data, write header only
+ packedFilter[packedFilterPtr++] = 0; // shift
+
+ packedFilter[packedFilterPtr++] = 0; // size
+ }
+ }
+
+ return packedFilter;
+ };
+
+ },{"./resize_filter_info":7}],7:[function(require,module,exports){
+// Filter definitions to build tables for
+// resizing convolvers.
+//
+// Presets for quality 0..3. Filter functions + window size
+//
+ 'use strict';
+
+ module.exports = [{
+ // Nearest neibor (Box)
+ win: 0.5,
+ filter: function filter(x) {
+ return x >= -0.5 && x < 0.5 ? 1.0 : 0.0;
+ }
+ }, {
+ // Hamming
+ win: 1.0,
+ filter: function filter(x) {
+ if (x <= -1.0 || x >= 1.0) {
+ return 0.0;
+ }
+
+ if (x > -1.19209290E-07 && x < 1.19209290E-07) {
+ return 1.0;
+ }
+
+ var xpi = x * Math.PI;
+ return Math.sin(xpi) / xpi * (0.54 + 0.46 * Math.cos(xpi / 1.0));
+ }
+ }, {
+ // Lanczos, win = 2
+ win: 2.0,
+ filter: function filter(x) {
+ if (x <= -2.0 || x >= 2.0) {
+ return 0.0;
+ }
+
+ if (x > -1.19209290E-07 && x < 1.19209290E-07) {
+ return 1.0;
+ }
+
+ var xpi = x * Math.PI;
+ return Math.sin(xpi) / xpi * Math.sin(xpi / 2.0) / (xpi / 2.0);
+ }
+ }, {
+ // Lanczos, win = 3
+ win: 3.0,
+ filter: function filter(x) {
+ if (x <= -3.0 || x >= 3.0) {
+ return 0.0;
+ }
+
+ if (x > -1.19209290E-07 && x < 1.19209290E-07) {
+ return 1.0;
+ }
+
+ var xpi = x * Math.PI;
+ return Math.sin(xpi) / xpi * Math.sin(xpi / 3.0) / (xpi / 3.0);
+ }
+ }];
+
+ },{}],8:[function(require,module,exports){
+ 'use strict';
+
+ var createFilters = require('./resize_filter_gen');
+
+ function resetAlpha(dst, width, height) {
+ var ptr = 3,
+ len = width * height * 4 | 0;
+
+ while (ptr < len) {
+ dst[ptr] = 0xFF;
+ ptr = ptr + 4 | 0;
+ }
+ }
+
+ function asUint8Array(src) {
+ return new Uint8Array(src.buffer, 0, src.byteLength);
+ }
+
+ var IS_LE = true; // should not crash everything on module load in old browsers
+
+ try {
+ IS_LE = new Uint32Array(new Uint8Array([1, 0, 0, 0]).buffer)[0] === 1;
+ } catch (__) {}
+
+ function copyInt16asLE(src, target, target_offset) {
+ if (IS_LE) {
+ target.set(asUint8Array(src), target_offset);
+ return;
+ }
+
+ for (var ptr = target_offset, i = 0; i < src.length; i++) {
+ var data = src[i];
+ target[ptr++] = data & 0xFF;
+ target[ptr++] = data >> 8 & 0xFF;
+ }
+ }
+
+ module.exports = function resize_wasm(options) {
+ var src = options.src;
+ var srcW = options.width;
+ var srcH = options.height;
+ var destW = options.toWidth;
+ var destH = options.toHeight;
+ var scaleX = options.scaleX || options.toWidth / options.width;
+ var scaleY = options.scaleY || options.toHeight / options.height;
+ var offsetX = options.offsetX || 0.0;
+ var offsetY = options.offsetY || 0.0;
+ var dest = options.dest || new Uint8Array(destW * destH * 4);
+ var quality = typeof options.quality === 'undefined' ? 3 : options.quality;
+ var alpha = options.alpha || false;
+ var filtersX = createFilters(quality, srcW, destW, scaleX, offsetX),
+ filtersY = createFilters(quality, srcH, destH, scaleY, offsetY); // destination is 0 too.
+
+ var src_offset = 0; // buffer between convolve passes
+
+ var tmp_offset = this.__align(src_offset + Math.max(src.byteLength, dest.byteLength));
+
+ var filtersX_offset = this.__align(tmp_offset + srcH * destW * 4);
+
+ var filtersY_offset = this.__align(filtersX_offset + filtersX.byteLength);
+
+ var alloc_bytes = filtersY_offset + filtersY.byteLength;
+
+ var instance = this.__instance('resize', alloc_bytes); //
+ // Fill memory block with data to process
+ //
+
+
+ var mem = new Uint8Array(this.__memory.buffer);
+ var mem32 = new Uint32Array(this.__memory.buffer); // 32-bit copy is much faster in chrome
+
+ var src32 = new Uint32Array(src.buffer);
+ mem32.set(src32); // We should guarantee LE bytes order. Filters are not big, so
+ // speed difference is not significant vs direct .set()
+
+ copyInt16asLE(filtersX, mem, filtersX_offset);
+ copyInt16asLE(filtersY, mem, filtersY_offset); //
+ // Now call webassembly method
+ // emsdk does method names with '_'
+
+ var fn = instance.exports.convolveHV || instance.exports._convolveHV;
+ fn(filtersX_offset, filtersY_offset, tmp_offset, srcW, srcH, destW, destH); //
+ // Copy data back to typed array
+ //
+ // 32-bit copy is much faster in chrome
+
+ var dest32 = new Uint32Array(dest.buffer);
+ dest32.set(new Uint32Array(this.__memory.buffer, 0, destH * destW)); // That's faster than doing checks in convolver.
+ // !!! Note, canvas data is not premultipled. We don't need other
+ // alpha corrections.
+
+ if (!alpha) resetAlpha(dest, destW, destH);
+ return dest;
+ };
+
+ },{"./resize_filter_gen":6}],9:[function(require,module,exports){
+ 'use strict';
+
+ var GC_INTERVAL = 100;
+
+ function Pool(create, idle) {
+ this.create = create;
+ this.available = [];
+ this.acquired = {};
+ this.lastId = 1;
+ this.timeoutId = 0;
+ this.idle = idle || 2000;
+ }
+
+ Pool.prototype.acquire = function () {
+ var _this = this;
+
+ var resource;
+
+ if (this.available.length !== 0) {
+ resource = this.available.pop();
+ } else {
+ resource = this.create();
+ resource.id = this.lastId++;
+
+ resource.release = function () {
+ return _this.release(resource);
+ };
+ }
+
+ this.acquired[resource.id] = resource;
+ return resource;
+ };
+
+ Pool.prototype.release = function (resource) {
+ var _this2 = this;
+
+ delete this.acquired[resource.id];
+ resource.lastUsed = Date.now();
+ this.available.push(resource);
+
+ if (this.timeoutId === 0) {
+ this.timeoutId = setTimeout(function () {
+ return _this2.gc();
+ }, GC_INTERVAL);
+ }
+ };
+
+ Pool.prototype.gc = function () {
+ var _this3 = this;
+
+ var now = Date.now();
+ this.available = this.available.filter(function (resource) {
+ if (now - resource.lastUsed > _this3.idle) {
+ resource.destroy();
+ return false;
+ }
+
+ return true;
+ });
+
+ if (this.available.length !== 0) {
+ this.timeoutId = setTimeout(function () {
+ return _this3.gc();
+ }, GC_INTERVAL);
+ } else {
+ this.timeoutId = 0;
+ }
+ };
+
+ module.exports = Pool;
+
+ },{}],10:[function(require,module,exports){
+// Add intermediate resizing steps when scaling down by a very large factor.
+//
+// For example, when resizing 10000x10000 down to 10x10, it'll resize it to
+// 300x300 first.
+//
+// It's needed because tiler has issues when the entire tile is scaled down
+// to a few pixels (1024px source tile with border size 3 should result in
+// at least 3+3+2 = 8px target tile, so max scale factor is 128 here).
+//
+// Also, adding intermediate steps can speed up processing if we use lower
+// quality algorithms for first stages.
+//
+ 'use strict'; // min size = 0 results in infinite loop,
+// min size = 1 can consume large amount of memory
+
+ var MIN_INNER_TILE_SIZE = 2;
+
+ module.exports = function createStages(fromWidth, fromHeight, toWidth, toHeight, srcTileSize, destTileBorder) {
+ var scaleX = toWidth / fromWidth;
+ var scaleY = toHeight / fromHeight; // derived from createRegions equation:
+ // innerTileWidth = pixelFloor(srcTileSize * scaleX) - 2 * destTileBorder;
+
+ var minScale = (2 * destTileBorder + MIN_INNER_TILE_SIZE + 1) / srcTileSize; // refuse to scale image multiple times by less than twice each time,
+ // it could only happen because of invalid options
+
+ if (minScale > 0.5) return [[toWidth, toHeight]];
+ var stageCount = Math.ceil(Math.log(Math.min(scaleX, scaleY)) / Math.log(minScale)); // no additional resizes are necessary,
+ // stageCount can be zero or be negative when enlarging the image
+
+ if (stageCount <= 1) return [[toWidth, toHeight]];
+ var result = [];
+
+ for (var i = 0; i < stageCount; i++) {
+ var width = Math.round(Math.pow(Math.pow(fromWidth, stageCount - i - 1) * Math.pow(toWidth, i + 1), 1 / stageCount));
+ var height = Math.round(Math.pow(Math.pow(fromHeight, stageCount - i - 1) * Math.pow(toHeight, i + 1), 1 / stageCount));
+ result.push([width, height]);
+ }
+
+ return result;
+ };
+
+ },{}],11:[function(require,module,exports){
+// Split original image into multiple 1024x1024 chunks to reduce memory usage
+// (images have to be unpacked into typed arrays for resizing) and allow
+// parallel processing of multiple tiles at a time.
+//
+ 'use strict';
+ /*
+ * pixelFloor and pixelCeil are modified versions of Math.floor and Math.ceil
+ * functions which take into account floating point arithmetic errors.
+ * Those errors can cause undesired increments/decrements of sizes and offsets:
+ * Math.ceil(36 / (36 / 500)) = 501
+ * pixelCeil(36 / (36 / 500)) = 500
+ */
+
+ var PIXEL_EPSILON = 1e-5;
+
+ function pixelFloor(x) {
+ var nearest = Math.round(x);
+
+ if (Math.abs(x - nearest) < PIXEL_EPSILON) {
+ return nearest;
+ }
+
+ return Math.floor(x);
+ }
+
+ function pixelCeil(x) {
+ var nearest = Math.round(x);
+
+ if (Math.abs(x - nearest) < PIXEL_EPSILON) {
+ return nearest;
+ }
+
+ return Math.ceil(x);
+ }
+
+ module.exports = function createRegions(options) {
+ var scaleX = options.toWidth / options.width;
+ var scaleY = options.toHeight / options.height;
+ var innerTileWidth = pixelFloor(options.srcTileSize * scaleX) - 2 * options.destTileBorder;
+ var innerTileHeight = pixelFloor(options.srcTileSize * scaleY) - 2 * options.destTileBorder; // prevent infinite loop, this should never happen
+
+ if (innerTileWidth < 1 || innerTileHeight < 1) {
+ throw new Error('Internal error in pica: target tile width/height is too small.');
+ }
+
+ var x, y;
+ var innerX, innerY, toTileWidth, toTileHeight;
+ var tiles = [];
+ var tile; // we go top-to-down instead of left-to-right to make image displayed from top to
+ // doesn in the browser
+
+ for (innerY = 0; innerY < options.toHeight; innerY += innerTileHeight) {
+ for (innerX = 0; innerX < options.toWidth; innerX += innerTileWidth) {
+ x = innerX - options.destTileBorder;
+
+ if (x < 0) {
+ x = 0;
+ }
+
+ toTileWidth = innerX + innerTileWidth + options.destTileBorder - x;
+
+ if (x + toTileWidth >= options.toWidth) {
+ toTileWidth = options.toWidth - x;
+ }
+
+ y = innerY - options.destTileBorder;
+
+ if (y < 0) {
+ y = 0;
+ }
+
+ toTileHeight = innerY + innerTileHeight + options.destTileBorder - y;
+
+ if (y + toTileHeight >= options.toHeight) {
+ toTileHeight = options.toHeight - y;
+ }
+
+ tile = {
+ toX: x,
+ toY: y,
+ toWidth: toTileWidth,
+ toHeight: toTileHeight,
+ toInnerX: innerX,
+ toInnerY: innerY,
+ toInnerWidth: innerTileWidth,
+ toInnerHeight: innerTileHeight,
+ offsetX: x / scaleX - pixelFloor(x / scaleX),
+ offsetY: y / scaleY - pixelFloor(y / scaleY),
+ scaleX: scaleX,
+ scaleY: scaleY,
+ x: pixelFloor(x / scaleX),
+ y: pixelFloor(y / scaleY),
+ width: pixelCeil(toTileWidth / scaleX),
+ height: pixelCeil(toTileHeight / scaleY)
+ };
+ tiles.push(tile);
+ }
+ }
+
+ return tiles;
+ };
+
+ },{}],12:[function(require,module,exports){
+ 'use strict';
+
+ function objClass(obj) {
+ return Object.prototype.toString.call(obj);
+ }
+
+ module.exports.isCanvas = function isCanvas(element) {
+ //return (element.nodeName && element.nodeName.toLowerCase() === 'canvas') ||
+ var cname = objClass(element);
+ return cname === '[object HTMLCanvasElement]'
+ /* browser */
+ || cname === '[object Canvas]'
+ /* node-canvas */
+ ;
+ };
+
+ module.exports.isImage = function isImage(element) {
+ //return element.nodeName && element.nodeName.toLowerCase() === 'img';
+ return objClass(element) === '[object HTMLImageElement]';
+ };
+
+ module.exports.limiter = function limiter(concurrency) {
+ var active = 0,
+ queue = [];
+
+ function roll() {
+ if (active < concurrency && queue.length) {
+ active++;
+ queue.shift()();
+ }
+ }
+
+ return function limit(fn) {
+ return new Promise(function (resolve, reject) {
+ queue.push(function () {
+ fn().then(function (result) {
+ resolve(result);
+ active--;
+ roll();
+ }, function (err) {
+ reject(err);
+ active--;
+ roll();
+ });
+ });
+ roll();
+ });
+ };
+ };
+
+ module.exports.cib_quality_name = function cib_quality_name(num) {
+ switch (num) {
+ case 0:
+ return 'pixelated';
+
+ case 1:
+ return 'low';
+
+ case 2:
+ return 'medium';
+ }
+
+ return 'high';
+ };
+
+ module.exports.cib_support = function cib_support() {
+ return Promise.resolve().then(function () {
+ if (typeof createImageBitmap === 'undefined' || typeof document === 'undefined') {
+ return false;
+ }
+
+ var c = document.createElement('canvas');
+ c.width = 100;
+ c.height = 100;
+ return createImageBitmap(c, 0, 0, 100, 100, {
+ resizeWidth: 10,
+ resizeHeight: 10,
+ resizeQuality: 'high'
+ }).then(function (bitmap) {
+ var status = bitmap.width === 10; // Branch below is filtered on upper level. We do not call resize
+ // detection for basic ImageBitmap.
+ //
+ // https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap
+ // old Crome 51 has ImageBitmap without .close(). Then this code
+ // will throw and return 'false' as expected.
+ //
+
+ bitmap.close();
+ c = null;
+ return status;
+ });
+ }).catch(function () {
+ return false;
+ });
+ };
+
+ },{}],13:[function(require,module,exports){
+// Web Worker wrapper for image resize function
+ 'use strict';
+
+ module.exports = function () {
+ var MathLib = require('./mathlib');
+
+ var mathLib;
+ /* eslint-disable no-undef */
+
+ onmessage = function onmessage(ev) {
+ var opts = ev.data.opts;
+ if (!mathLib) mathLib = new MathLib(ev.data.features); // Use multimath's sync auto-init. Avoid Promise use in old browsers,
+ // because polyfills are not propagated to webworker.
+
+ var result = mathLib.resizeAndUnsharp(opts);
+ postMessage({
+ result: result
+ }, [result.buffer]);
+ };
+ };
+
+ },{"./mathlib":1}],14:[function(require,module,exports){
+// Calculate Gaussian blur of an image using IIR filter
+// The method is taken from Intel's white paper and code example attached to it:
+// https://software.intel.com/en-us/articles/iir-gaussian-blur-filter
+// -implementation-using-intel-advanced-vector-extensions
+
+ var a0, a1, a2, a3, b1, b2, left_corner, right_corner;
+
+ function gaussCoef(sigma) {
+ if (sigma < 0.5) {
+ sigma = 0.5;
+ }
+
+ var a = Math.exp(0.726 * 0.726) / sigma,
+ g1 = Math.exp(-a),
+ g2 = Math.exp(-2 * a),
+ k = (1 - g1) * (1 - g1) / (1 + 2 * a * g1 - g2);
+
+ a0 = k;
+ a1 = k * (a - 1) * g1;
+ a2 = k * (a + 1) * g1;
+ a3 = -k * g2;
+ b1 = 2 * g1;
+ b2 = -g2;
+ left_corner = (a0 + a1) / (1 - b1 - b2);
+ right_corner = (a2 + a3) / (1 - b1 - b2);
+
+ // Attempt to force type to FP32.
+ return new Float32Array([ a0, a1, a2, a3, b1, b2, left_corner, right_corner ]);
+ }
+
+ function convolveMono16(src, out, line, coeff, width, height) {
+ // takes src image and writes the blurred and transposed result into out
+
+ var prev_src, curr_src, curr_out, prev_out, prev_prev_out;
+ var src_index, out_index, line_index;
+ var i, j;
+ var coeff_a0, coeff_a1, coeff_b1, coeff_b2;
+
+ for (i = 0; i < height; i++) {
+ src_index = i * width;
+ out_index = i;
+ line_index = 0;
+
+ // left to right
+ prev_src = src[src_index];
+ prev_prev_out = prev_src * coeff[6];
+ prev_out = prev_prev_out;
+
+ coeff_a0 = coeff[0];
+ coeff_a1 = coeff[1];
+ coeff_b1 = coeff[4];
+ coeff_b2 = coeff[5];
+
+ for (j = 0; j < width; j++) {
+ curr_src = src[src_index];
+
+ curr_out = curr_src * coeff_a0 +
+ prev_src * coeff_a1 +
+ prev_out * coeff_b1 +
+ prev_prev_out * coeff_b2;
+
+ prev_prev_out = prev_out;
+ prev_out = curr_out;
+ prev_src = curr_src;
+
+ line[line_index] = prev_out;
+ line_index++;
+ src_index++;
+ }
+
+ src_index--;
+ line_index--;
+ out_index += height * (width - 1);
+
+ // right to left
+ prev_src = src[src_index];
+ prev_prev_out = prev_src * coeff[7];
+ prev_out = prev_prev_out;
+ curr_src = prev_src;
+
+ coeff_a0 = coeff[2];
+ coeff_a1 = coeff[3];
+
+ for (j = width - 1; j >= 0; j--) {
+ curr_out = curr_src * coeff_a0 +
+ prev_src * coeff_a1 +
+ prev_out * coeff_b1 +
+ prev_prev_out * coeff_b2;
+
+ prev_prev_out = prev_out;
+ prev_out = curr_out;
+
+ prev_src = curr_src;
+ curr_src = src[src_index];
+
+ out[out_index] = line[line_index] + prev_out;
+
+ src_index--;
+ line_index--;
+ out_index -= height;
+ }
+ }
+ }
+
+
+ function blurMono16(src, width, height, radius) {
+ // Quick exit on zero radius
+ if (!radius) { return; }
+
+ var out = new Uint16Array(src.length),
+ tmp_line = new Float32Array(Math.max(width, height));
+
+ var coeff = gaussCoef(radius);
+
+ convolveMono16(src, out, tmp_line, coeff, width, height, radius);
+ convolveMono16(out, src, tmp_line, coeff, height, width, radius);
+ }
+
+ module.exports = blurMono16;
+
+ },{}],15:[function(require,module,exports){
+ if (typeof Object.create === 'function') {
+ // implementation from standard node.js 'util' module
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ };
+ } else {
+ // old school shim for old browsers
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
+ }
+
+ },{}],16:[function(require,module,exports){
+ 'use strict';
+
+
+ var assign = require('object-assign');
+ var base64decode = require('./lib/base64decode');
+ var hasWebAssembly = require('./lib/wa_detect');
+
+
+ var DEFAULT_OPTIONS = {
+ js: true,
+ wasm: true
+ };
+
+
+ function MultiMath(options) {
+ if (!(this instanceof MultiMath)) return new MultiMath(options);
+
+ var opts = assign({}, DEFAULT_OPTIONS, options || {});
+
+ this.options = opts;
+
+ this.__cache = {};
+ this.has_wasm = hasWebAssembly();
+
+ this.__init_promise = null;
+ this.__modules = opts.modules || {};
+ this.__memory = null;
+ this.__wasm = {};
+
+ this.__isLE = ((new Uint32Array((new Uint8Array([ 1, 0, 0, 0 ])).buffer))[0] === 1);
+
+ if (!this.options.js && !this.options.wasm) {
+ throw new Error('mathlib: at least "js" or "wasm" should be enabled');
+ }
+ }
+
+
+ MultiMath.prototype.use = function (module) {
+ this.__modules[module.name] = module;
+
+ // Pin the best possible implementation
+ if (!this.has_wasm || !this.options.wasm || !module.wasm_fn) {
+ this[module.name] = module.fn;
+ } else {
+ this[module.name] = module.wasm_fn;
+ }
+
+ return this;
+ };
+
+
+ MultiMath.prototype.init = function () {
+ if (this.__init_promise) return this.__init_promise;
+
+ if (!this.options.js && this.options.wasm && !this.has_wasm) {
+ return Promise.reject(new Error('mathlib: only "wasm" was enabled, but it\'s not supported'));
+ }
+
+ var self = this;
+
+ this.__init_promise = Promise.all(Object.keys(self.__modules).map(function (name) {
+ var module = self.__modules[name];
+
+ if (!self.has_wasm || !self.options.wasm || !module.wasm_fn) return null;
+
+ // If already compiled - exit
+ if (self.__wasm[name]) return null;
+
+ // Compile wasm source
+ return WebAssembly.compile(self.__base64decode(module.wasm_src))
+ .then(function (m) { self.__wasm[name] = m; });
+ }))
+ .then(function () { return self; });
+
+ return this.__init_promise;
+ };
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Methods below are for internal use from plugins
+
+
+// Simple decode base64 to typed array. Useful to load embedded webassembly
+// code. You probably don't need to call this method directly.
+//
+ MultiMath.prototype.__base64decode = base64decode;
+
+
+// Increase current memory to include specified number of bytes. Do nothing if
+// size is already ok. You probably don't need to call this method directly,
+// because it will be invoked from `.__instance()`.
+//
+ MultiMath.prototype.__reallocate = function mem_grow_to(bytes) {
+ if (!this.__memory) {
+ this.__memory = new WebAssembly.Memory({
+ initial: Math.ceil(bytes / (64 * 1024))
+ });
+ return this.__memory;
+ }
+
+ var mem_size = this.__memory.buffer.byteLength;
+
+ if (mem_size < bytes) {
+ this.__memory.grow(Math.ceil((bytes - mem_size) / (64 * 1024)));
+ }
+
+ return this.__memory;
+ };
+
+
+// Returns instantinated webassembly item by name, with specified memory size
+// and environment.
+// - use cache if available
+// - do sync module init, if async init was not called earlier
+// - allocate memory if not enougth
+// - can export functions to webassembly via "env_extra",
+// for example, { exp: Math.exp }
+//
+ MultiMath.prototype.__instance = function instance(name, memsize, env_extra) {
+ if (memsize) this.__reallocate(memsize);
+
+ // If .init() was not called, do sync compile
+ if (!this.__wasm[name]) {
+ var module = this.__modules[name];
+ this.__wasm[name] = new WebAssembly.Module(this.__base64decode(module.wasm_src));
+ }
+
+ if (!this.__cache[name]) {
+ var env_base = {
+ memoryBase: 0,
+ memory: this.__memory,
+ tableBase: 0,
+ table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
+ };
+
+ this.__cache[name] = new WebAssembly.Instance(this.__wasm[name], {
+ env: assign(env_base, env_extra || {})
+ });
+ }
+
+ return this.__cache[name];
+ };
+
+
+// Helper to calculate memory aligh for pointers. Webassembly does not require
+// this, but you may wish to experiment. Default base = 8;
+//
+ MultiMath.prototype.__align = function align(number, base) {
+ base = base || 8;
+ var reminder = number % base;
+ return number + (reminder ? base - reminder : 0);
+ };
+
+
+ module.exports = MultiMath;
+
+ },{"./lib/base64decode":17,"./lib/wa_detect":23,"object-assign":24}],17:[function(require,module,exports){
+// base64 decode str -> Uint8Array, to load WA modules
+//
+ 'use strict';
+
+
+ var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+
+ module.exports = function base64decode(str) {
+ var input = str.replace(/[\r\n=]/g, ''), // remove CR/LF & padding to simplify scan
+ max = input.length;
+
+ var out = new Uint8Array((max * 3) >> 2);
+
+ // Collect by 6*4 bits (3 bytes)
+
+ var bits = 0;
+ var ptr = 0;
+
+ for (var idx = 0; idx < max; idx++) {
+ if ((idx % 4 === 0) && idx) {
+ out[ptr++] = (bits >> 16) & 0xFF;
+ out[ptr++] = (bits >> 8) & 0xFF;
+ out[ptr++] = bits & 0xFF;
+ }
+
+ bits = (bits << 6) | BASE64_MAP.indexOf(input.charAt(idx));
+ }
+
+ // Dump tail
+
+ var tailbits = (max % 4) * 6;
+
+ if (tailbits === 0) {
+ out[ptr++] = (bits >> 16) & 0xFF;
+ out[ptr++] = (bits >> 8) & 0xFF;
+ out[ptr++] = bits & 0xFF;
+ } else if (tailbits === 18) {
+ out[ptr++] = (bits >> 10) & 0xFF;
+ out[ptr++] = (bits >> 2) & 0xFF;
+ } else if (tailbits === 12) {
+ out[ptr++] = (bits >> 4) & 0xFF;
+ }
+
+ return out;
+ };
+
+ },{}],18:[function(require,module,exports){
+// Calculates 16-bit precision HSL lightness from 8-bit rgba buffer
+//
+ 'use strict';
+
+
+ module.exports = function hsl_l16_js(img, width, height) {
+ var size = width * height;
+ var out = new Uint16Array(size);
+ var r, g, b, min, max;
+ for (var i = 0; i < size; i++) {
+ r = img[4 * i];
+ g = img[4 * i + 1];
+ b = img[4 * i + 2];
+ max = (r >= g && r >= b) ? r : (g >= b && g >= r) ? g : b;
+ min = (r <= g && r <= b) ? r : (g <= b && g <= r) ? g : b;
+ out[i] = (max + min) * 257 >> 1;
+ }
+ return out;
+ };
+
+ },{}],19:[function(require,module,exports){
+ 'use strict';
+
+ module.exports = {
+ name: 'unsharp_mask',
+ fn: require('./unsharp_mask'),
+ wasm_fn: require('./unsharp_mask_wasm'),
+ wasm_src: require('./unsharp_mask_wasm_base64')
+ };
+
+ },{"./unsharp_mask":20,"./unsharp_mask_wasm":21,"./unsharp_mask_wasm_base64":22}],20:[function(require,module,exports){
+// Unsharp mask filter
+//
+// http://stackoverflow.com/a/23322820/1031804
+// USM(O) = O + (2 * (Amount / 100) * (O - GB))
+// GB - gaussian blur.
+//
+// Image is converted from RGB to HSL, unsharp mask is applied to the
+// lightness channel and then image is converted back to RGB.
+//
+ 'use strict';
+
+
+ var glur_mono16 = require('glur/mono16');
+ var hsl_l16 = require('./hsl_l16');
+
+
+ module.exports = function unsharp(img, width, height, amount, radius, threshold) {
+ var r, g, b;
+ var h, s, l;
+ var min, max;
+ var m1, m2, hShifted;
+ var diff, iTimes4;
+
+ if (amount === 0 || radius < 0.5) {
+ return;
+ }
+ if (radius > 2.0) {
+ radius = 2.0;
+ }
+
+ var lightness = hsl_l16(img, width, height);
+
+ var blured = new Uint16Array(lightness); // copy, because blur modify src
+
+ glur_mono16(blured, width, height, radius);
+
+ var amountFp = (amount / 100 * 0x1000 + 0.5)|0;
+ var thresholdFp = (threshold * 257)|0;
+
+ var size = width * height;
+
+ /* eslint-disable indent */
+ for (var i = 0; i < size; i++) {
+ diff = 2 * (lightness[i] - blured[i]);
+
+ if (Math.abs(diff) >= thresholdFp) {
+ iTimes4 = i * 4;
+ r = img[iTimes4];
+ g = img[iTimes4 + 1];
+ b = img[iTimes4 + 2];
+
+ // convert RGB to HSL
+ // take RGB, 8-bit unsigned integer per each channel
+ // save HSL, H and L are 16-bit unsigned integers, S is 12-bit unsigned integer
+ // math is taken from here: http://www.easyrgb.com/index.php?X=MATH&H=18
+ // and adopted to be integer (fixed point in fact) for sake of performance
+ max = (r >= g && r >= b) ? r : (g >= r && g >= b) ? g : b; // min and max are in [0..0xff]
+ min = (r <= g && r <= b) ? r : (g <= r && g <= b) ? g : b;
+ l = (max + min) * 257 >> 1; // l is in [0..0xffff] that is caused by multiplication by 257
+
+ if (min === max) {
+ h = s = 0;
+ } else {
+ s = (l <= 0x7fff) ?
+ (((max - min) * 0xfff) / (max + min))|0 :
+ (((max - min) * 0xfff) / (2 * 0xff - max - min))|0; // s is in [0..0xfff]
+ // h could be less 0, it will be fixed in backward conversion to RGB, |h| <= 0xffff / 6
+ h = (r === max) ? (((g - b) * 0xffff) / (6 * (max - min)))|0
+ : (g === max) ? 0x5555 + ((((b - r) * 0xffff) / (6 * (max - min)))|0) // 0x5555 == 0xffff / 3
+ : 0xaaaa + ((((r - g) * 0xffff) / (6 * (max - min)))|0); // 0xaaaa == 0xffff * 2 / 3
+ }
+
+ // add unsharp mask mask to the lightness channel
+ l += (amountFp * diff + 0x800) >> 12;
+ if (l > 0xffff) {
+ l = 0xffff;
+ } else if (l < 0) {
+ l = 0;
+ }
+
+ // convert HSL back to RGB
+ // for information about math look above
+ if (s === 0) {
+ r = g = b = l >> 8;
+ } else {
+ m2 = (l <= 0x7fff) ? (l * (0x1000 + s) + 0x800) >> 12 :
+ l + (((0xffff - l) * s + 0x800) >> 12);
+ m1 = 2 * l - m2 >> 8;
+ m2 >>= 8;
+ // save result to RGB channels
+ // R channel
+ hShifted = (h + 0x5555) & 0xffff; // 0x5555 == 0xffff / 3
+ r = (hShifted >= 0xaaaa) ? m1 // 0xaaaa == 0xffff * 2 / 3
+ : (hShifted >= 0x7fff) ? m1 + ((m2 - m1) * 6 * (0xaaaa - hShifted) + 0x8000 >> 16)
+ : (hShifted >= 0x2aaa) ? m2 // 0x2aaa == 0xffff / 6
+ : m1 + ((m2 - m1) * 6 * hShifted + 0x8000 >> 16);
+ // G channel
+ hShifted = h & 0xffff;
+ g = (hShifted >= 0xaaaa) ? m1 // 0xaaaa == 0xffff * 2 / 3
+ : (hShifted >= 0x7fff) ? m1 + ((m2 - m1) * 6 * (0xaaaa - hShifted) + 0x8000 >> 16)
+ : (hShifted >= 0x2aaa) ? m2 // 0x2aaa == 0xffff / 6
+ : m1 + ((m2 - m1) * 6 * hShifted + 0x8000 >> 16);
+ // B channel
+ hShifted = (h - 0x5555) & 0xffff;
+ b = (hShifted >= 0xaaaa) ? m1 // 0xaaaa == 0xffff * 2 / 3
+ : (hShifted >= 0x7fff) ? m1 + ((m2 - m1) * 6 * (0xaaaa - hShifted) + 0x8000 >> 16)
+ : (hShifted >= 0x2aaa) ? m2 // 0x2aaa == 0xffff / 6
+ : m1 + ((m2 - m1) * 6 * hShifted + 0x8000 >> 16);
+ }
+
+ img[iTimes4] = r;
+ img[iTimes4 + 1] = g;
+ img[iTimes4 + 2] = b;
+ }
+ }
+ };
+
+ },{"./hsl_l16":18,"glur/mono16":14}],21:[function(require,module,exports){
+ 'use strict';
+
+
+ module.exports = function unsharp(img, width, height, amount, radius, threshold) {
+ if (amount === 0 || radius < 0.5) {
+ return;
+ }
+
+ if (radius > 2.0) {
+ radius = 2.0;
+ }
+
+ var pixels = width * height;
+
+ var img_bytes_cnt = pixels * 4;
+ var hsl_bytes_cnt = pixels * 2;
+ var blur_bytes_cnt = pixels * 2;
+ var blur_line_byte_cnt = Math.max(width, height) * 4; // float32 array
+ var blur_coeffs_byte_cnt = 8 * 4; // float32 array
+
+ var img_offset = 0;
+ var hsl_offset = img_bytes_cnt;
+ var blur_offset = hsl_offset + hsl_bytes_cnt;
+ var blur_tmp_offset = blur_offset + blur_bytes_cnt;
+ var blur_line_offset = blur_tmp_offset + blur_bytes_cnt;
+ var blur_coeffs_offset = blur_line_offset + blur_line_byte_cnt;
+
+ var instance = this.__instance(
+ 'unsharp_mask',
+ img_bytes_cnt + hsl_bytes_cnt + blur_bytes_cnt * 2 + blur_line_byte_cnt + blur_coeffs_byte_cnt,
+ { exp: Math.exp }
+ );
+
+ // 32-bit copy is much faster in chrome
+ var img32 = new Uint32Array(img.buffer);
+ var mem32 = new Uint32Array(this.__memory.buffer);
+ mem32.set(img32);
+
+ // HSL
+ var fn = instance.exports.hsl_l16 || instance.exports._hsl_l16;
+ fn(img_offset, hsl_offset, width, height);
+
+ // BLUR
+ fn = instance.exports.blurMono16 || instance.exports._blurMono16;
+ fn(hsl_offset, blur_offset, blur_tmp_offset,
+ blur_line_offset, blur_coeffs_offset, width, height, radius);
+
+ // UNSHARP
+ fn = instance.exports.unsharp || instance.exports._unsharp;
+ fn(img_offset, img_offset, hsl_offset,
+ blur_offset, width, height, amount, threshold);
+
+ // 32-bit copy is much faster in chrome
+ img32.set(new Uint32Array(this.__memory.buffer, 0, pixels));
+ };
+
+ },{}],22:[function(require,module,exports){
+// This is autogenerated file from math.wasm, don't edit.
+//
+ 'use strict';
+
+ /* eslint-disable max-len */
+ module.exports = 'AGFzbQEAAAABMQZgAXwBfGACfX8AYAZ/f39/f38AYAh/f39/f39/fQBgBH9/f38AYAh/f39/f39/fwACGQIDZW52A2V4cAAAA2VudgZtZW1vcnkCAAEDBgUBAgMEBQQEAXAAAAdMBRZfX2J1aWxkX2dhdXNzaWFuX2NvZWZzAAEOX19nYXVzczE2X2xpbmUAAgpibHVyTW9ubzE2AAMHaHNsX2wxNgAEB3Vuc2hhcnAABQkBAAqJEAXZAQEGfAJAIAFE24a6Q4Ia+z8gALujIgOaEAAiBCAEoCIGtjgCECABIANEAAAAAAAAAMCiEAAiBbaMOAIUIAFEAAAAAAAA8D8gBKEiAiACoiAEIAMgA6CiRAAAAAAAAPA/oCAFoaMiArY4AgAgASAEIANEAAAAAAAA8L+gIAKioiIHtjgCBCABIAQgA0QAAAAAAADwP6AgAqKiIgO2OAIIIAEgBSACoiIEtow4AgwgASACIAegIAVEAAAAAAAA8D8gBqGgIgKjtjgCGCABIAMgBKEgAqO2OAIcCwu3AwMDfwR9CHwCQCADKgIUIQkgAyoCECEKIAMqAgwhCyADKgIIIQwCQCAEQX9qIgdBAEgiCA0AIAIgAC8BALgiDSADKgIYu6IiDiAJuyIQoiAOIAq7IhGiIA0gAyoCBLsiEqIgAyoCALsiEyANoqCgoCIPtjgCACACQQRqIQIgAEECaiEAIAdFDQAgBCEGA0AgAiAOIBCiIA8iDiARoiANIBKiIBMgAC8BALgiDaKgoKAiD7Y4AgAgAkEEaiECIABBAmohACAGQX9qIgZBAUoNAAsLAkAgCA0AIAEgByAFbEEBdGogAEF+ai8BACIIuCINIAu7IhGiIA0gDLsiEqKgIA0gAyoCHLuiIg4gCrsiE6KgIA4gCbsiFKKgIg8gAkF8aioCALugqzsBACAHRQ0AIAJBeGohAiAAQXxqIQBBACAFQQF0ayEHIAEgBSAEQQF0QXxqbGohBgNAIAghAyAALwEAIQggBiANIBGiIAO4Ig0gEqKgIA8iECAToqAgDiAUoqAiDyACKgIAu6CrOwEAIAYgB2ohBiAAQX5qIQAgAkF8aiECIBAhDiAEQX9qIgRBAUoNAAsLCwvfAgIDfwZ8AkAgB0MAAAAAWw0AIARE24a6Q4Ia+z8gB0MAAAA/l7ujIgyaEAAiDSANoCIPtjgCECAEIAxEAAAAAAAAAMCiEAAiDraMOAIUIAREAAAAAAAA8D8gDaEiCyALoiANIAwgDKCiRAAAAAAAAPA/oCAOoaMiC7Y4AgAgBCANIAxEAAAAAAAA8L+gIAuioiIQtjgCBCAEIA0gDEQAAAAAAADwP6AgC6KiIgy2OAIIIAQgDiALoiINtow4AgwgBCALIBCgIA5EAAAAAAAA8D8gD6GgIgujtjgCGCAEIAwgDaEgC6O2OAIcIAYEQCAFQQF0IQogBiEJIAIhCANAIAAgCCADIAQgBSAGEAIgACAKaiEAIAhBAmohCCAJQX9qIgkNAAsLIAVFDQAgBkEBdCEIIAUhAANAIAIgASADIAQgBiAFEAIgAiAIaiECIAFBAmohASAAQX9qIgANAAsLC7wBAQV/IAMgAmwiAwRAQQAgA2shBgNAIAAoAgAiBEEIdiIHQf8BcSECAn8gBEH/AXEiAyAEQRB2IgRB/wFxIgVPBEAgAyIIIAMgAk8NARoLIAQgBCAHIAIgA0kbIAIgBUkbQf8BcQshCAJAIAMgAk0EQCADIAVNDQELIAQgByAEIAMgAk8bIAIgBUsbQf8BcSEDCyAAQQRqIQAgASADIAhqQYECbEEBdjsBACABQQJqIQEgBkEBaiIGDQALCwvTBgEKfwJAIAazQwAAgEWUQwAAyEKVu0QAAAAAAADgP6CqIQ0gBSAEbCILBEAgB0GBAmwhDgNAQQAgAi8BACADLwEAayIGQQF0IgdrIAcgBkEASBsgDk8EQCAAQQJqLQAAIQUCfyAALQAAIgYgAEEBai0AACIESSIJRQRAIAYiCCAGIAVPDQEaCyAFIAUgBCAEIAVJGyAGIARLGwshCAJ/IAYgBE0EQCAGIgogBiAFTQ0BGgsgBSAFIAQgBCAFSxsgCRsLIgogCGoiD0GBAmwiEEEBdiERQQAhDAJ/QQAiCSAIIApGDQAaIAggCmsiCUH/H2wgD0H+AyAIayAKayAQQYCABEkbbSEMIAYgCEYEQCAEIAVrQf//A2wgCUEGbG0MAQsgBSAGayAGIARrIAQgCEYiBhtB//8DbCAJQQZsbUHVqgFBqtUCIAYbagshCSARIAcgDWxBgBBqQQx1aiIGQQAgBkEAShsiBkH//wMgBkH//wNIGyEGAkACfwJAIAxB//8DcSIFBEAgBkH//wFKDQEgBUGAIGogBmxBgBBqQQx2DAILIAZBCHYiBiEFIAYhBAwCCyAFIAZB//8Dc2xBgBBqQQx2IAZqCyIFQQh2IQcgBkEBdCAFa0EIdiIGIQQCQCAJQdWqAWpB//8DcSIFQanVAksNACAFQf//AU8EQEGq1QIgBWsgByAGa2xBBmxBgIACakEQdiAGaiEEDAELIAchBCAFQanVAEsNACAFIAcgBmtsQQZsQYCAAmpBEHYgBmohBAsCfyAGIgUgCUH//wNxIghBqdUCSw0AGkGq1QIgCGsgByAGa2xBBmxBgIACakEQdiAGaiAIQf//AU8NABogByIFIAhBqdUASw0AGiAIIAcgBmtsQQZsQYCAAmpBEHYgBmoLIQUgCUGr1QJqQf//A3EiCEGp1QJLDQAgCEH//wFPBEBBqtUCIAhrIAcgBmtsQQZsQYCAAmpBEHYgBmohBgwBCyAIQanVAEsEQCAHIQYMAQsgCCAHIAZrbEEGbEGAgAJqQRB2IAZqIQYLIAEgBDoAACABQQFqIAU6AAAgAUECaiAGOgAACyADQQJqIQMgAkECaiECIABBBGohACABQQRqIQEgC0F/aiILDQALCwsL';
+
+ },{}],23:[function(require,module,exports){
+// Detect WebAssembly support.
+// - Check global WebAssembly object
+// - Try to load simple module (can be disabled via CSP)
+//
+ 'use strict';
+
+
+ var wa;
+
+
+ module.exports = function hasWebAssembly() {
+ // use cache if called before;
+ if (typeof wa !== 'undefined') return wa;
+
+ wa = false;
+
+ if (typeof WebAssembly === 'undefined') return wa;
+
+ // If WebAssenbly is disabled, code can throw on compile
+ try {
+ // https://github.com/brion/min-wasm-fail/blob/master/min-wasm-fail.in.js
+ // Additional check that WA internals are correct
+
+ /* eslint-disable comma-spacing, max-len */
+ var bin = new Uint8Array([ 0,97,115,109,1,0,0,0,1,6,1,96,1,127,1,127,3,2,1,0,5,3,1,0,1,7,8,1,4,116,101,115,116,0,0,10,16,1,14,0,32,0,65,1,54,2,0,32,0,40,2,0,11 ]);
+ var module = new WebAssembly.Module(bin);
+ var instance = new WebAssembly.Instance(module, {});
+
+ // test storing to and loading from a non-zero location via a parameter.
+ // Safari on iOS 11.2.5 returns 0 unexpectedly at non-zero locations
+ if (instance.exports.test(4) !== 0) wa = true;
+
+ return wa;
+ } catch (__) {}
+
+ return wa;
+ };
+
+ },{}],24:[function(require,module,exports){
+ /*
+ object-assign
+ (c) Sindre Sorhus
+ @license MIT
+ */
+
+ 'use strict';
+ /* eslint-disable no-unused-vars */
+ var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+ var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+ function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+ }
+
+ function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+ }
+
+ module.exports = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+ };
+
+ },{}],25:[function(require,module,exports){
+ var bundleFn = arguments[3];
+ var sources = arguments[4];
+ var cache = arguments[5];
+
+ var stringify = JSON.stringify;
+
+ module.exports = function (fn, options) {
+ var wkey;
+ var cacheKeys = Object.keys(cache);
+
+ for (var i = 0, l = cacheKeys.length; i < l; i++) {
+ var key = cacheKeys[i];
+ var exp = cache[key].exports;
+ // Using babel as a transpiler to use esmodule, the export will always
+ // be an object with the default export as a property of it. To ensure
+ // the existing api and babel esmodule exports are both supported we
+ // check for both
+ if (exp === fn || exp && exp.default === fn) {
+ wkey = key;
+ break;
+ }
+ }
+
+ if (!wkey) {
+ wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
+ var wcache = {};
+ for (var i = 0, l = cacheKeys.length; i < l; i++) {
+ var key = cacheKeys[i];
+ wcache[key] = key;
+ }
+ sources[wkey] = [
+ 'function(require,module,exports){' + fn + '(self); }',
+ wcache
+ ];
+ }
+ var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
+
+ var scache = {}; scache[wkey] = wkey;
+ sources[skey] = [
+ 'function(require,module,exports){' +
+ // try to call default if defined to also support babel esmodule exports
+ 'var f = require(' + stringify(wkey) + ');' +
+ '(f.default ? f.default : f)(self);' +
+ '}',
+ scache
+ ];
+
+ var workerSources = {};
+ resolveSources(skey);
+
+ function resolveSources(key) {
+ workerSources[key] = true;
+
+ for (var depPath in sources[key][1]) {
+ var depKey = sources[key][1][depPath];
+ if (!workerSources[depKey]) {
+ resolveSources(depKey);
+ }
+ }
+ }
+
+ var src = '(' + bundleFn + ')({'
+ + Object.keys(workerSources).map(function (key) {
+ return stringify(key) + ':['
+ + sources[key][0]
+ + ',' + stringify(sources[key][1]) + ']'
+ ;
+ }).join(',')
+ + '},{},[' + stringify(skey) + '])'
+ ;
+
+ var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
+
+ var blob = new Blob([src], { type: 'text/javascript' });
+ if (options && options.bare) { return blob; }
+ var workerUrl = URL.createObjectURL(blob);
+ var worker = new Worker(workerUrl);
+ worker.objectURL = workerUrl;
+ return worker;
+ };
+
+ },{}],"/":[function(require,module,exports){
+ 'use strict';
+
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
+
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
+
+ function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
+
+ var assign = require('object-assign');
+
+ var webworkify = require('webworkify');
+
+ var MathLib = require('./lib/mathlib');
+
+ var Pool = require('./lib/pool');
+
+ var utils = require('./lib/utils');
+
+ var worker = require('./lib/worker');
+
+ var createStages = require('./lib/stepper');
+
+ var createRegions = require('./lib/tiler'); // Deduplicate pools & limiters with the same configs
+// when user creates multiple pica instances.
+
+
+ var singletones = {};
+ var NEED_SAFARI_FIX = false;
+
+ try {
+ if (typeof navigator !== 'undefined' && navigator.userAgent) {
+ NEED_SAFARI_FIX = navigator.userAgent.indexOf('Safari') >= 0;
+ }
+ } catch (e) {}
+
+ var concurrency = 1;
+
+ if (typeof navigator !== 'undefined') {
+ concurrency = Math.min(navigator.hardwareConcurrency || 1, 4);
+ }
+
+ var DEFAULT_PICA_OPTS = {
+ tile: 1024,
+ concurrency: concurrency,
+ features: ['js', 'wasm', 'ww'],
+ idle: 2000
+ };
+ var DEFAULT_RESIZE_OPTS = {
+ quality: 3,
+ alpha: false,
+ unsharpAmount: 0,
+ unsharpRadius: 0.0,
+ unsharpThreshold: 0
+ };
+ var CAN_NEW_IMAGE_DATA;
+ var CAN_CREATE_IMAGE_BITMAP;
+
+ function workerFabric() {
+ return {
+ value: webworkify(worker),
+ destroy: function destroy() {
+ this.value.terminate();
+
+ if (typeof window !== 'undefined') {
+ var url = window.URL || window.webkitURL || window.mozURL || window.msURL;
+
+ if (url && url.revokeObjectURL && this.value.objectURL) {
+ url.revokeObjectURL(this.value.objectURL);
+ }
+ }
+ }
+ };
+ } ////////////////////////////////////////////////////////////////////////////////
+// API methods
+
+
+ function Pica(options) {
+ if (!(this instanceof Pica)) return new Pica(options);
+ this.options = assign({}, DEFAULT_PICA_OPTS, options || {});
+ var limiter_key = "lk_".concat(this.options.concurrency); // Share limiters to avoid multiple parallel workers when user creates
+ // multiple pica instances.
+
+ this.__limit = singletones[limiter_key] || utils.limiter(this.options.concurrency);
+ if (!singletones[limiter_key]) singletones[limiter_key] = this.__limit; // List of supported features, according to options & browser/node.js
+
+ this.features = {
+ js: false,
+ // pure JS implementation, can be disabled for testing
+ wasm: false,
+ // webassembly implementation for heavy functions
+ cib: false,
+ // resize via createImageBitmap (only FF at this moment)
+ ww: false // webworkers
+
+ };
+ this.__workersPool = null; // Store requested features for webworkers
+
+ this.__requested_features = [];
+ this.__mathlib = null;
+ }
+
+ Pica.prototype.init = function () {
+ var _this = this;
+
+ if (this.__initPromise) return this.__initPromise; // Test if we can create ImageData without canvas and memory copy
+
+ if (CAN_NEW_IMAGE_DATA !== false && CAN_NEW_IMAGE_DATA !== true) {
+ CAN_NEW_IMAGE_DATA = false;
+
+ if (typeof ImageData !== 'undefined' && typeof Uint8ClampedArray !== 'undefined') {
+ try {
+ /* eslint-disable no-new */
+ new ImageData(new Uint8ClampedArray(400), 10, 10);
+ CAN_NEW_IMAGE_DATA = true;
+ } catch (__) {}
+ }
+ } // ImageBitmap can be effective in 2 places:
+ //
+ // 1. Threaded jpeg unpack (basic)
+ // 2. Built-in resize (blocked due problem in chrome, see issue #89)
+ //
+ // For basic use we also need ImageBitmap wo support .close() method,
+ // see https://developer.mozilla.org/ru/docs/Web/API/ImageBitmap
+
+
+ if (CAN_CREATE_IMAGE_BITMAP !== false && CAN_CREATE_IMAGE_BITMAP !== true) {
+ CAN_CREATE_IMAGE_BITMAP = false;
+
+ if (typeof ImageBitmap !== 'undefined') {
+ if (ImageBitmap.prototype && ImageBitmap.prototype.close) {
+ CAN_CREATE_IMAGE_BITMAP = true;
+ } else {
+ this.debug('ImageBitmap does not support .close(), disabled');
+ }
+ }
+ }
+
+ var features = this.options.features.slice();
+
+ if (features.indexOf('all') >= 0) {
+ features = ['cib', 'wasm', 'js', 'ww'];
+ }
+
+ this.__requested_features = features;
+ this.__mathlib = new MathLib(features); // Check WebWorker support if requested
+
+ if (features.indexOf('ww') >= 0) {
+ if (typeof window !== 'undefined' && 'Worker' in window) {
+ // IE <= 11 don't allow to create webworkers from string. We should check it.
+ // https://connect.microsoft.com/IE/feedback/details/801810/web-workers-from-blob-urls-in-ie-10-and-11
+ try {
+ var wkr = require('webworkify')(function () {});
+
+ wkr.terminate();
+ this.features.ww = true; // pool uniqueness depends on pool config + webworker config
+
+ var wpool_key = "wp_".concat(JSON.stringify(this.options));
+
+ if (singletones[wpool_key]) {
+ this.__workersPool = singletones[wpool_key];
+ } else {
+ this.__workersPool = new Pool(workerFabric, this.options.idle);
+ singletones[wpool_key] = this.__workersPool;
+ }
+ } catch (__) {}
+ }
+ }
+
+ var initMath = this.__mathlib.init().then(function (mathlib) {
+ // Copy detected features
+ assign(_this.features, mathlib.features);
+ });
+
+ var checkCibResize;
+
+ if (!CAN_CREATE_IMAGE_BITMAP) {
+ checkCibResize = Promise.resolve(false);
+ } else {
+ checkCibResize = utils.cib_support().then(function (status) {
+ if (_this.features.cib && features.indexOf('cib') < 0) {
+ _this.debug('createImageBitmap() resize supported, but disabled by config');
+
+ return;
+ }
+
+ if (features.indexOf('cib') >= 0) _this.features.cib = status;
+ });
+ } // Init math lib. That's async because can load some
+
+
+ this.__initPromise = Promise.all([initMath, checkCibResize]).then(function () {
+ return _this;
+ });
+ return this.__initPromise;
+ };
+
+ Pica.prototype.resize = function (from, to, options) {
+ var _this2 = this;
+
+ this.debug('Start resize...');
+ var opts = assign({}, DEFAULT_RESIZE_OPTS);
+
+ if (!isNaN(options)) {
+ opts = assign(opts, {
+ quality: options
+ });
+ } else if (options) {
+ opts = assign(opts, options);
+ }
+
+ opts.toWidth = to.width;
+ opts.toHeight = to.height;
+ opts.width = from.naturalWidth || from.width;
+ opts.height = from.naturalHeight || from.height; // Prevent stepper from infinite loop
+
+ if (to.width === 0 || to.height === 0) {
+ return Promise.reject(new Error("Invalid output size: ".concat(to.width, "x").concat(to.height)));
+ }
+
+ if (opts.unsharpRadius > 2) opts.unsharpRadius = 2;
+ var canceled = false;
+ var cancelToken = null;
+
+ if (opts.cancelToken) {
+ // Wrap cancelToken to avoid successive resolve & set flag
+ cancelToken = opts.cancelToken.then(function (data) {
+ canceled = true;
+ throw data;
+ }, function (err) {
+ canceled = true;
+ throw err;
+ });
+ }
+
+ var DEST_TILE_BORDER = 3; // Max possible filter window size
+
+ var destTileBorder = Math.ceil(Math.max(DEST_TILE_BORDER, 2.5 * opts.unsharpRadius | 0));
+ return this.init().then(function () {
+ if (canceled) return cancelToken; // if createImageBitmap supports resize, just do it and return
+
+ if (_this2.features.cib) {
+ var toCtx = to.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+
+ _this2.debug('Resize via createImageBitmap()');
+
+ return createImageBitmap(from, {
+ resizeWidth: opts.toWidth,
+ resizeHeight: opts.toHeight,
+ resizeQuality: utils.cib_quality_name(opts.quality)
+ }).then(function (imageBitmap) {
+ if (canceled) return cancelToken; // if no unsharp - draw directly to output canvas
+
+ if (!opts.unsharpAmount) {
+ toCtx.drawImage(imageBitmap, 0, 0);
+ imageBitmap.close();
+ toCtx = null;
+
+ _this2.debug('Finished!');
+
+ return to;
+ }
+
+ _this2.debug('Unsharp result');
+
+ var tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = opts.toWidth;
+ tmpCanvas.height = opts.toHeight;
+ var tmpCtx = tmpCanvas.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+ tmpCtx.drawImage(imageBitmap, 0, 0);
+ imageBitmap.close();
+ var iData = tmpCtx.getImageData(0, 0, opts.toWidth, opts.toHeight);
+
+ _this2.__mathlib.unsharp(iData.data, opts.toWidth, opts.toHeight, opts.unsharpAmount, opts.unsharpRadius, opts.unsharpThreshold);
+
+ toCtx.putImageData(iData, 0, 0);
+ iData = tmpCtx = tmpCanvas = toCtx = null;
+
+ _this2.debug('Finished!');
+
+ return to;
+ });
+ } //
+ // No easy way, let's resize manually via arrays
+ //
+ // Share cache between calls:
+ //
+ // - wasm instance
+ // - wasm memory object
+ //
+
+
+ var cache = {}; // Call resizer in webworker or locally, depending on config
+
+ var invokeResize = function invokeResize(opts) {
+ return Promise.resolve().then(function () {
+ if (!_this2.features.ww) return _this2.__mathlib.resizeAndUnsharp(opts, cache);
+ return new Promise(function (resolve, reject) {
+ var w = _this2.__workersPool.acquire();
+
+ if (cancelToken) cancelToken.catch(function (err) {
+ return reject(err);
+ });
+
+ w.value.onmessage = function (ev) {
+ w.release();
+ if (ev.data.err) reject(ev.data.err);else resolve(ev.data.result);
+ };
+
+ w.value.postMessage({
+ opts: opts,
+ features: _this2.__requested_features,
+ preload: {
+ wasm_nodule: _this2.__mathlib.__
+ }
+ }, [opts.src.buffer]);
+ });
+ });
+ };
+
+ var tileAndResize = function tileAndResize(from, to, opts) {
+ var srcCtx;
+ var srcImageBitmap;
+ var toCtx;
+
+ var processTile = function processTile(tile) {
+ return _this2.__limit(function () {
+ if (canceled) return cancelToken;
+ var srcImageData; // Extract tile RGBA buffer, depending on input type
+
+ if (utils.isCanvas(from)) {
+ _this2.debug('Get tile pixel data'); // If input is Canvas - extract region data directly
+
+
+ srcImageData = srcCtx.getImageData(tile.x, tile.y, tile.width, tile.height);
+ } else {
+ // If input is Image or decoded to ImageBitmap,
+ // draw region to temporary canvas and extract data from it
+ //
+ // Note! Attempt to reuse this canvas causes significant slowdown in chrome
+ //
+ _this2.debug('Draw tile imageBitmap/image to temporary canvas');
+
+ var tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = tile.width;
+ tmpCanvas.height = tile.height;
+ var tmpCtx = tmpCanvas.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+ tmpCtx.globalCompositeOperation = 'copy';
+ tmpCtx.drawImage(srcImageBitmap || from, tile.x, tile.y, tile.width, tile.height, 0, 0, tile.width, tile.height);
+
+ _this2.debug('Get tile pixel data');
+
+ srcImageData = tmpCtx.getImageData(0, 0, tile.width, tile.height);
+ tmpCtx = tmpCanvas = null;
+ }
+
+ var o = {
+ src: srcImageData.data,
+ width: tile.width,
+ height: tile.height,
+ toWidth: tile.toWidth,
+ toHeight: tile.toHeight,
+ scaleX: tile.scaleX,
+ scaleY: tile.scaleY,
+ offsetX: tile.offsetX,
+ offsetY: tile.offsetY,
+ quality: opts.quality,
+ alpha: opts.alpha,
+ unsharpAmount: opts.unsharpAmount,
+ unsharpRadius: opts.unsharpRadius,
+ unsharpThreshold: opts.unsharpThreshold
+ };
+
+ _this2.debug('Invoke resize math');
+
+ return Promise.resolve().then(function () {
+ return invokeResize(o);
+ }).then(function (result) {
+ if (canceled) return cancelToken;
+ srcImageData = null;
+ var toImageData;
+
+ _this2.debug('Convert raw rgba tile result to ImageData');
+
+ if (CAN_NEW_IMAGE_DATA) {
+ // this branch is for modern browsers
+ // If `new ImageData()` & Uint8ClampedArray suported
+ toImageData = new ImageData(new Uint8ClampedArray(result), tile.toWidth, tile.toHeight);
+ } else {
+ // fallback for `node-canvas` and old browsers
+ // (IE11 has ImageData but does not support `new ImageData()`)
+ toImageData = toCtx.createImageData(tile.toWidth, tile.toHeight);
+
+ if (toImageData.data.set) {
+ toImageData.data.set(result);
+ } else {
+ // IE9 don't have `.set()`
+ for (var i = toImageData.data.length - 1; i >= 0; i--) {
+ toImageData.data[i] = result[i];
+ }
+ }
+ }
+
+ _this2.debug('Draw tile');
+
+ if (NEED_SAFARI_FIX) {
+ // Safari draws thin white stripes between tiles without this fix
+ toCtx.putImageData(toImageData, tile.toX, tile.toY, tile.toInnerX - tile.toX, tile.toInnerY - tile.toY, tile.toInnerWidth + 1e-5, tile.toInnerHeight + 1e-5);
+ } else {
+ toCtx.putImageData(toImageData, tile.toX, tile.toY, tile.toInnerX - tile.toX, tile.toInnerY - tile.toY, tile.toInnerWidth, tile.toInnerHeight);
+ }
+
+ return null;
+ });
+ });
+ }; // Need to normalize data source first. It can be canvas or image.
+ // If image - try to decode in background if possible
+
+
+ return Promise.resolve().then(function () {
+ toCtx = to.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+
+ if (utils.isCanvas(from)) {
+ srcCtx = from.getContext('2d', {
+ alpha: Boolean(opts.alpha)
+ });
+ return null;
+ }
+
+ if (utils.isImage(from)) {
+ // try do decode image in background for faster next operations
+ if (!CAN_CREATE_IMAGE_BITMAP) return null;
+
+ _this2.debug('Decode image via createImageBitmap');
+
+ return createImageBitmap(from).then(function (imageBitmap) {
+ srcImageBitmap = imageBitmap;
+ });
+ }
+
+ throw new Error('".from" should be image or canvas');
+ }).then(function () {
+ if (canceled) return cancelToken;
+
+ _this2.debug('Calculate tiles'); //
+ // Here we are with "normalized" source,
+ // follow to tiling
+ //
+
+
+ var regions = createRegions({
+ width: opts.width,
+ height: opts.height,
+ srcTileSize: _this2.options.tile,
+ toWidth: opts.toWidth,
+ toHeight: opts.toHeight,
+ destTileBorder: destTileBorder
+ });
+ var jobs = regions.map(function (tile) {
+ return processTile(tile);
+ });
+
+ function cleanup() {
+ if (srcImageBitmap) {
+ srcImageBitmap.close();
+ srcImageBitmap = null;
+ }
+ }
+
+ _this2.debug('Process tiles');
+
+ return Promise.all(jobs).then(function () {
+ _this2.debug('Finished!');
+
+ cleanup();
+ return to;
+ }, function (err) {
+ cleanup();
+ throw err;
+ });
+ });
+ };
+
+ var processStages = function processStages(stages, from, to, opts) {
+ if (canceled) return cancelToken;
+
+ var _stages$shift = stages.shift(),
+ _stages$shift2 = _slicedToArray(_stages$shift, 2),
+ toWidth = _stages$shift2[0],
+ toHeight = _stages$shift2[1];
+
+ var isLastStage = stages.length === 0;
+ opts = assign({}, opts, {
+ toWidth: toWidth,
+ toHeight: toHeight,
+ // only use user-defined quality for the last stage,
+ // use simpler (Hamming) filter for the first stages where
+ // scale factor is large enough (more than 2-3)
+ quality: isLastStage ? opts.quality : Math.min(1, opts.quality)
+ });
+ var tmpCanvas;
+
+ if (!isLastStage) {
+ // create temporary canvas
+ tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = toWidth;
+ tmpCanvas.height = toHeight;
+ }
+
+ return tileAndResize(from, isLastStage ? to : tmpCanvas, opts).then(function () {
+ if (isLastStage) return to;
+ opts.width = toWidth;
+ opts.height = toHeight;
+ return processStages(stages, tmpCanvas, to, opts);
+ });
+ };
+
+ var stages = createStages(opts.width, opts.height, opts.toWidth, opts.toHeight, _this2.options.tile, destTileBorder);
+ return processStages(stages, from, to, opts);
+ });
+ }; // RGBA buffer resize
+//
+
+
+ Pica.prototype.resizeBuffer = function (options) {
+ var _this3 = this;
+
+ var opts = assign({}, DEFAULT_RESIZE_OPTS, options);
+ return this.init().then(function () {
+ return _this3.__mathlib.resizeAndUnsharp(opts);
+ });
+ };
+
+ Pica.prototype.toBlob = function (canvas, mimeType, quality) {
+ mimeType = mimeType || 'image/png';
+ return new Promise(function (resolve) {
+ if (canvas.toBlob) {
+ canvas.toBlob(function (blob) {
+ return resolve(blob);
+ }, mimeType, quality);
+ return;
+ } // Fallback for old browsers
+
+
+ var asString = atob(canvas.toDataURL(mimeType, quality).split(',')[1]);
+ var len = asString.length;
+ var asBuffer = new Uint8Array(len);
+
+ for (var i = 0; i < len; i++) {
+ asBuffer[i] = asString.charCodeAt(i);
+ }
+
+ resolve(new Blob([asBuffer], {
+ type: mimeType
+ }));
+ });
+ };
+
+ Pica.prototype.debug = function () {};
+
+ module.exports = Pica;
+
+ },{"./lib/mathlib":1,"./lib/pool":9,"./lib/stepper":10,"./lib/tiler":11,"./lib/utils":12,"./lib/worker":13,"object-assign":24,"webworkify":25}]},{},[])("/")
+});
+
+/**
+ * This module allows resizing and conversion of HTMLImageElements to Blob and File objects
+ *
+ * @author Maximilian Mader
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Image/Resizer
+ */
+define('WoltLabSuite/Core/Image/Resizer',[
+ 'WoltLabSuite/Core/FileUtil',
+ 'WoltLabSuite/Core/Image/ExifUtil',
+ 'Pica'
+], function(FileUtil, ExifUtil, Pica) {
+ "use strict";
+
+ var pica = new Pica({features: ['js', 'wasm', 'ww']});
+
+ /**
+ * @constructor
+ */
+ function ImageResizer() { }
+ ImageResizer.prototype = {
+ maxWidth: 800,
+ maxHeight: 600,
+ quality: 0.8,
+ fileType: 'image/jpeg',
+
+ /**
+ * Sets the default maximum width for this instance
+ *
+ * @param {Number} value the new default maximum width
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setMaxWidth: function (value) {
+ if (value == null) value = ImageResizer.prototype.maxWidth;
+
+ this.maxWidth = value;
+ return this;
+ },
+
+ /**
+ * Sets the default maximum height for this instance
+ *
+ * @param {Number} value the new default maximum height
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setMaxHeight: function (value) {
+ if (value == null) value = ImageResizer.prototype.maxHeight;
+
+ this.maxHeight = value;
+ return this;
+ },
+
+ /**
+ * Sets the default quality for this instance
+ *
+ * @param {Number} value the new default quality
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setQuality: function (value) {
+ if (value == null) value = ImageResizer.prototype.quality;
+
+ this.quality = value;
+ return this;
+ },
+
+ /**
+ * Sets the default file type for this instance
+ *
+ * @param {Number} value the new default file type
+ * @returns {ImageResizer} this ImageResizer instance
+ */
+ setFileType: function (value) {
+ if (value == null) value = ImageResizer.prototype.fileType;
+
+ this.fileType = value;
+ return this;
+ },
+
+ /**
+ * Converts the given object of exif data and image data into a File.
+ *
+ * @param {Object{exif: Uint8Array|undefined, image: Canvas} data object containing exif data and image data
+ * @param {String} fileName the name of the returned file
+ * @param {String} [fileType] the type of the returned image
+ * @param {Number} [quality] quality setting, currently only effective for "image/jpeg"
+ * @returns {Promise<File>} the File object
+ */
+ saveFile: function (data, fileName, fileType, quality) {
+ fileType = fileType || this.fileType;
+ quality = quality || this.quality;
+
+ var basename = fileName.match(/(.+)(\..+?)$/);
+
+ return pica.toBlob(data.image, fileType, quality)
+ .then(function (blob) {
+ if (fileType === 'image/jpeg' && typeof data.exif !== 'undefined') {
+ return ExifUtil.setExifData(blob, data.exif);
+ }
+
+ return blob;
+ })
+ .then(function (blob) {
+ return FileUtil.blobToFile(blob, basename[1] + '_autoscaled');
+ });
+ },
+
+ /**
+ * Loads the given file into an image object and parses Exif information.
+ *
+ * @param {File} file the file to load
+ * @returns {Promise} resulting image data
+ */
+ loadFile: function (file) {
+ var exif = undefined;
+ if (file.type === 'image/jpeg') {
+ // Extract EXIF data
+ exif = ExifUtil.getExifBytesFromJpeg(file);
+ }
+
+ var loader = new Promise(function (resolve, reject) {
+ var reader = new FileReader();
+ var image = new Image();
+
+ reader.addEventListener('load', function () {
+ image.src = reader.result;
+ });
+
+ reader.addEventListener('error', function () {
+ reader.abort();
+ reject(reader.error);
+ });
+
+ image.addEventListener('error', reject);
+
+ image.addEventListener('load', function () {
+ resolve(image);
+ });
+
+ reader.readAsDataURL(file);
+ });
+
+ return Promise.all([ exif, loader ])
+ .then(function (result) {
+ return { exif: result[0], image: result[1] };
+ });
+ },
+
+ /**
+ * Downscales an image given as File object.
+ *
+ * @param {Image} image the image to resize
+ * @param {Number} [maxWidth] maximum width
+ * @param {Number} [maxHeight] maximum height
+ * @param {Number} [quality] quality in percent
+ * @param {boolean} [force] whether to force scaling even if unneeded (thus re-encoding with a possibly smaller file size)
+ * @param {Promise} cancelPromise a Promise used to cancel pica's operation when it resolves
+ * @returns {Promise<Blob | undefined>} a Promise resolving with the resized image as a {Canvas} or undefined if no resizing happened
+ */
+ resize: function (image, maxWidth, maxHeight, quality, force, cancelPromise) {
+ maxWidth = maxWidth || this.maxWidth;
+ maxHeight = maxHeight || this.maxHeight;
+ quality = quality || this.quality;
+ force = force || false;
+
+ var canvas = document.createElement('canvas');
+
+ // Prevent upscaling
+ var newWidth = Math.min(maxWidth, image.width);
+ var newHeight = Math.min(maxHeight, image.height);
+
+ if (image.width <= newWidth && image.height <= newHeight && !force) {
+ return Promise.resolve(undefined);
+ }
+
+ // Keep image ratio
+ var ratio = Math.min(newWidth / image.width, newHeight / image.height);
+ canvas.width = Math.floor(image.width * ratio);
+ canvas.height = Math.floor(image.height * ratio);
+
+ // Map to Pica's quality
+ var resizeQuality = 1;
+ if (quality >= 0.8) {
+ resizeQuality = 3;
+ }
+ else if (quality >= 0.4) {
+ resizeQuality = 2;
+ }
+
+ var options = {
+ quality: resizeQuality,
+ cancelToken: cancelPromise,
+ alpha: true
+ };
+
+ return pica.resize(image, canvas, options);
+ }
+ };
+
+ return ImageResizer;
+});
+
+/**
+ * Dropdown language chooser.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language/Chooser
+ */
+define('WoltLabSuite/Core/Language/Chooser',['Dictionary', 'Language', 'Dom/Traverse', 'Dom/Util', 'ObjectMap', 'Ui/SimpleDropdown'], function(Dictionary, Language, DomTraverse, DomUtil, ObjectMap, UiSimpleDropdown) {
+ "use strict";
+
+ var _choosers = new Dictionary();
+ var _didInit = false;
+ var _forms = new ObjectMap();
+
+ var _callbackSubmit = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Language/Chooser
+ */
+ return {
+ /**
+ * Initializes a language chooser.
+ *
+ * @param {string} containerId input element container id
+ * @param {string} chooserId input element id
+ * @param {int} languageId selected language id
+ * @param {object<int, object<string, string>>} languages data of available languages
+ * @param {function} callback function called after a language is selected
+ * @param {boolean} allowEmptyValue true if no language may be selected
+ */
+ init: function(containerId, chooserId, languageId, languages, callback, allowEmptyValue) {
+ if (_choosers.has(chooserId)) {
+ return;
+ }
+
+ var container = elById(containerId);
+ if (container === null) {
+ throw new Error("Expected a valid container id, cannot find '" + chooserId + "'.");
+ }
+
+ var element = elById(chooserId);
+ if (element === null) {
+ element = elCreate('input');
+ elAttr(element, 'type', 'hidden');
+ elAttr(element, 'id', chooserId);
+ elAttr(element, 'name', chooserId);
+ elAttr(element, 'value', languageId);
+
+ container.appendChild(element);
+ }
+
+ this._initElement(chooserId, element, languageId, languages, callback, allowEmptyValue);
+ },
+
+ /**
+ * Caches common event listener callbacks.
+ */
+ _setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _callbackSubmit = this._submit.bind(this);
+ },
+
+ /**
+ * Sets up DOM and event listeners for a language chooser.
+ *
+ * @param {string} chooserId chooser id
+ * @param {Element} element chooser element
+ * @param {int} languageId selected language id
+ * @param {object<int, object<string, string>>} languages data of available languages
+ * @param {function} callback callback function invoked on selection change
+ * @param {boolean} allowEmptyValue true if no language may be selected
+ */
+ _initElement: function(chooserId, element, languageId, languages, callback, allowEmptyValue) {
+ var container;
+
+ if (element.parentNode.nodeName === 'DD') {
+ container = elCreate('div');
+ container.className = 'dropdown';
+
+ // language chooser is the first child so that descriptions and error messages
+ // are always shown below the language chooser
+ DomUtil.prepend(container, element.parentNode);
+ }
+ else {
+ container = element.parentNode;
+ container.classList.add('dropdown');
+ }
+
+ elHide(element);
+
+ var dropdownToggle = elCreate('a');
+ dropdownToggle.className = 'dropdownToggle dropdownIndicator boxFlag box24 inputPrefix' + (element.parentNode.nodeName === 'DD' ? ' button' : '');
+ container.appendChild(dropdownToggle);
+
+ var dropdownMenu = elCreate('ul');
+ dropdownMenu.className = 'dropdownMenu';
+ container.appendChild(dropdownMenu);
+
+ var callbackClick = (function(event) {
+ var languageId = ~~elData(event.currentTarget, 'language-id');
+
+ var activeItem = DomTraverse.childByClass(dropdownMenu, 'active');
+ if (activeItem !== null) activeItem.classList.remove('active');
+
+ if (languageId) event.currentTarget.classList.add('active');
+
+ this._select(chooserId, languageId, event.currentTarget);
+ }).bind(this);
+
+ // add language dropdown items
+ var link, img, listItem, span;
+ for (var availableLanguageId in languages) {
+ if (languages.hasOwnProperty(availableLanguageId)) {
+ var language = languages[availableLanguageId];
+
+ listItem = elCreate('li');
+ listItem.className = 'boxFlag';
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ elData(listItem, 'language-id', availableLanguageId);
+ if (language.languageCode !== undefined) elData(listItem, 'language-code', language.languageCode);
+ dropdownMenu.appendChild(listItem);
+
+ link = elCreate('a');
+ link.className = 'box24';
+ listItem.appendChild(link);
+
+ img = elCreate('img');
+ elAttr(img, 'src', language.iconPath);
+ elAttr(img, 'alt', '');
+ img.className = 'iconFlag';
+ link.appendChild(img);
+
+ span = elCreate('span');
+ span.textContent = language.languageName;
+ link.appendChild(span);
+
+ if (availableLanguageId == languageId) {
+ dropdownToggle.innerHTML = listItem.firstChild.innerHTML;
+ }
+ }
+ }
+
+ // add dropdown item for "no selection"
+ if (allowEmptyValue) {
+ listItem = elCreate('li');
+ listItem.className = 'dropdownDivider';
+ dropdownMenu.appendChild(listItem);
+
+ listItem = elCreate('li');
+ elData(listItem, 'language-id', 0);
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ dropdownMenu.appendChild(listItem);
+
+ link = elCreate('a');
+ link.textContent = Language.get('wcf.global.language.noSelection');
+ listItem.appendChild(link);
+
+ if (languageId === 0) {
+ dropdownToggle.innerHTML = listItem.firstChild.innerHTML;
+ }
+
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ }
+ else if (languageId === 0) {
+ dropdownToggle.innerHTML = null;
+
+ var div = elCreate('div');
+ dropdownToggle.appendChild(div);
+
+ span = elCreate('span');
+ span.className = 'icon icon24 fa-question';
+ div.appendChild(span);
+
+ span = elCreate('span');
+ span.textContent = Language.get('wcf.global.language.noSelection');
+ div.appendChild(span);
+ }
+
+ UiSimpleDropdown.init(dropdownToggle);
+
+ _choosers.set(chooserId, {
+ callback: callback,
+ dropdownMenu: dropdownMenu,
+ dropdownToggle: dropdownToggle,
+ element: element
+ });
+
+ // bind to submit event
+ var form = DomTraverse.parentByTag(element, 'FORM');
+ if (form !== null) {
+ form.addEventListener('submit', _callbackSubmit);
+
+ var chooserIds = _forms.get(form);
+ if (chooserIds === undefined) {
+ chooserIds = [];
+ _forms.set(form, chooserIds);
+ }
+
+ chooserIds.push(chooserId);
+ }
+ },
+
+ /**
+ * Selects a language from the dropdown list.
+ *
+ * @param {string} chooserId input element id
+ * @param {int} languageId language id or `0` to disable i18n
+ * @param {Element=} listItem selected list item
+ */
+ _select: function(chooserId, languageId, listItem) {
+ var chooser = _choosers.get(chooserId);
+
+ if (listItem === undefined) {
+ var listItems = chooser.dropdownMenu.childNodes;
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var _listItem = listItems[i];
+ if (~~elData(_listItem, 'language-id') === languageId) {
+ listItem = _listItem;
+ break;
+ }
+ }
+
+ if (listItem === undefined) {
+ throw new Error("Cannot select unknown language id '" + languageId + "'");
+ }
+ }
+
+ chooser.element.value = languageId;
+
+ chooser.dropdownToggle.innerHTML = listItem.firstChild.innerHTML;
+
+ _choosers.set(chooserId, chooser);
+
+ // execute callback
+ if (typeof chooser.callback === 'function') {
+ chooser.callback(listItem);
+ }
+ },
+
+ /**
+ * Inserts hidden fields for the language chooser value on submit.
+ *
+ * @param {object} event event object
+ */
+ _submit: function(event) {
+ var elementIds = _forms.get(event.currentTarget);
+
+ var input;
+ for (var i = 0, length = elementIds.length; i < length; i++) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = elementIds[i];
+ input.value = this.getLanguageId(elementIds[i]);
+
+ event.currentTarget.appendChild(input);
+ }
+ },
+
+ /**
+ * Returns the chooser for an input field.
+ *
+ * @param {string} chooserId input element id
+ * @return {Dictionary} data of the chooser
+ */
+ getChooser: function(chooserId) {
+ var chooser = _choosers.get(chooserId);
+ if (chooser === undefined) {
+ throw new Error("Expected a valid language chooser input element, '" + chooserId + "' is not i18n input field.");
+ }
+
+ return chooser;
+ },
+
+ /**
+ * Returns the selected language for a certain chooser.
+ *
+ * @param {string} chooserId input element id
+ * @return {int} chosen language id
+ */
+ getLanguageId: function(chooserId) {
+ return ~~this.getChooser(chooserId).element.value;
+ },
+
+ /**
+ * Removes the chooser with given id.
+ *
+ * @param {string} chooserId input element id
+ */
+ removeChooser: function(chooserId) {
+ if (_choosers.has(chooserId)) {
+ _choosers.delete(chooserId);
+ }
+ },
+
+ /**
+ * Sets the language for a certain chooser.
+ *
+ * @param {string} chooserId input element id
+ * @param {int} languageId language id to be set
+ */
+ setLanguageId: function(chooserId, languageId) {
+ if (_choosers.get(chooserId) === undefined) {
+ throw new Error("Expected a valid input element, '" + chooserId + "' is not i18n input field.");
+ }
+
+ this._select(chooserId, languageId);
+ }
+ };
+});
+
+/**
+ * I18n interface for input and textarea fields.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language/Input
+ */
+define('WoltLabSuite/Core/Language/Input',['Core', 'Dictionary', 'Language', 'ObjectMap', 'StringUtil', 'Dom/Traverse', 'Dom/Util', 'Ui/SimpleDropdown'], function(Core, Dictionary, Language, ObjectMap, StringUtil, DomTraverse, DomUtil, UiSimpleDropdown) {
+ "use strict";
+
+ var _elements = new Dictionary();
+ var _didInit = false;
+ var _forms = new ObjectMap();
+ var _values = new Dictionary();
+
+ var _callbackDropdownToggle = null;
+ var _callbackSubmit = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Language/Input
+ */
+ return {
+ /**
+ * Initializes an input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Object} values preset values per language id
+ * @param {Object} availableLanguages language names per language id
+ * @param {boolean} forceSelection require i18n input
+ */
+ init: function(elementId, values, availableLanguages, forceSelection) {
+ if (_values.has(elementId)) {
+ return;
+ }
+
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, cannot find '" + elementId + "'.");
+ }
+
+ this._setup();
+
+ // unescape values
+ var unescapedValues = new Dictionary();
+ for (var key in values) {
+ if (values.hasOwnProperty(key)) {
+ unescapedValues.set(~~key, StringUtil.unescapeHTML(values[key]));
+ }
+ }
+
+ _values.set(elementId, unescapedValues);
+
+ this._initElement(elementId, element, unescapedValues, availableLanguages, forceSelection);
+ },
+
+ /**
+ * Registers a callback for an element.
+ *
+ * @param {string} elementId
+ * @param {string} eventName
+ * @param {function} callback
+ */
+ registerCallback: function (elementId, eventName, callback) {
+ if (!_values.has(elementId)) {
+ throw new Error("Unknown element id '" + elementId + "'.");
+ }
+
+ _elements.get(elementId).callbacks.set(eventName, callback);
+ },
+
+ /**
+ * Unregisters the element with the given id.
+ *
+ * @param {string} elementId
+ * @since 5.2
+ */
+ unregister: function(elementId) {
+ if (!_values.has(elementId)) {
+ throw new Error("Unknown element id '" + elementId + "'.");
+ }
+
+ _values.delete(elementId);
+ _elements.delete(elementId);
+ },
+
+ /**
+ * Caches common event listener callbacks.
+ */
+ _setup: function() {
+ if (_didInit) return;
+ _didInit = true;
+
+ _callbackDropdownToggle = this._dropdownToggle.bind(this);
+ _callbackSubmit = this._submit.bind(this);
+ },
+
+ /**
+ * Sets up DOM and event listeners for an input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Element} element input or textarea element
+ * @param {Dictionary} values preset values per language id
+ * @param {Object} availableLanguages language names per language id
+ * @param {boolean} forceSelection require i18n input
+ */
+ _initElement: function(elementId, element, values, availableLanguages, forceSelection) {
+ var container = element.parentNode;
+ if (!container.classList.contains('inputAddon')) {
+ container = elCreate('div');
+ container.className = 'inputAddon' + (element.nodeName === 'TEXTAREA' ? ' inputAddonTextarea' : '');
+ //noinspection JSCheckFunctionSignatures
+ elData(container, 'input-id', elementId);
+
+ var hasFocus = document.activeElement === element;
+
+ // DOM manipulation causes focused element to lose focus
+ element.parentNode.insertBefore(container, element);
+ container.appendChild(element);
+
+ if (hasFocus) {
+ element.focus();
+ }
+ }
+
+ container.classList.add('dropdown');
+ var button = elCreate('span');
+ button.className = 'button dropdownToggle inputPrefix';
+
+ var span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.disabledI18n');
+
+ button.appendChild(span);
+ container.insertBefore(button, element);
+
+ var dropdownMenu = elCreate('ul');
+ dropdownMenu.className = 'dropdownMenu';
+ DomUtil.insertAfter(dropdownMenu, button);
+
+ var callbackClick = (function(event, isInit) {
+ var languageId = ~~elData(event.currentTarget, 'language-id');
+
+ var activeItem = DomTraverse.childByClass(dropdownMenu, 'active');
+ if (activeItem !== null) activeItem.classList.remove('active');
+
+ if (languageId) event.currentTarget.classList.add('active');
+
+ this._select(elementId, languageId, isInit || false);
+ }).bind(this);
+
+ // build language dropdown
+ var listItem;
+ for (var languageId in availableLanguages) {
+ if (availableLanguages.hasOwnProperty(languageId)) {
+ listItem = elCreate('li');
+ elData(listItem, 'language-id', languageId);
+
+ span = elCreate('span');
+ span.textContent = availableLanguages[languageId];
+
+ listItem.appendChild(span);
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ dropdownMenu.appendChild(listItem);
+ }
+ }
+
+ if (forceSelection !== true) {
+ listItem = elCreate('li');
+ listItem.className = 'dropdownDivider';
+ dropdownMenu.appendChild(listItem);
+
+ listItem = elCreate('li');
+ elData(listItem, 'language-id', 0);
+ span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.disabledI18n');
+ listItem.appendChild(span);
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ dropdownMenu.appendChild(listItem);
+ }
+
+ var activeItem = null;
+ if (forceSelection === true || values.size) {
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ if (~~elData(dropdownMenu.children[i], 'language-id') === LANGUAGE_ID) {
+ activeItem = dropdownMenu.children[i];
+ break;
+ }
+ }
+ }
+
+ UiSimpleDropdown.init(button);
+ UiSimpleDropdown.registerCallback(container.id, _callbackDropdownToggle);
+
+ _elements.set(elementId, {
+ buttonLabel: button.children[0],
+ callbacks: new Dictionary(),
+ element: element,
+ languageId: 0,
+ isEnabled: true,
+ forceSelection: forceSelection
+ });
+
+ // bind to submit event
+ var submit = DomTraverse.parentByTag(element, 'FORM');
+ if (submit !== null) {
+ submit.addEventListener('submit', _callbackSubmit);
+
+ var elementIds = _forms.get(submit);
+ if (elementIds === undefined) {
+ elementIds = [];
+ _forms.set(submit, elementIds);
+ }
+
+ elementIds.push(elementId);
+ }
+
+ if (activeItem !== null) {
+ callbackClick({ currentTarget: activeItem }, true);
+ }
+ },
+
+ /**
+ * Selects a language or non-i18n from the dropdown list.
+ *
+ * @param {string} elementId input element id
+ * @param {int} languageId language id or `0` to disable i18n
+ * @param {boolean} isInit triggers pre-selection on init
+ */
+ _select: function(elementId, languageId, isInit) {
+ var data = _elements.get(elementId);
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(data.element.closest('.inputAddon').id);
+ var item, label = '';
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ item = dropdownMenu.children[i];
+
+ var itemLanguageId = elData(item, 'language-id');
+ if (itemLanguageId.length && languageId === ~~itemLanguageId) {
+ label = item.children[0].textContent;
+ }
+ }
+
+ // save current value
+ if (data.languageId !== languageId) {
+ var values = _values.get(elementId);
+
+ if (data.languageId) {
+ values.set(data.languageId, data.element.value);
+ }
+
+ if (languageId === 0) {
+ _values.set(elementId, new Dictionary());
+ }
+ else if (data.buttonLabel.classList.contains('active') || isInit === true) {
+ data.element.value = (values.has(languageId)) ? values.get(languageId) : '';
+ }
+
+ // update label
+ data.buttonLabel.textContent = label;
+ data.buttonLabel.classList[(languageId ? 'add' : 'remove')]('active');
+
+ data.languageId = languageId;
+ }
+
+ if (!isInit) {
+ data.element.blur();
+ data.element.focus();
+ }
+
+ if (data.callbacks.has('select')) {
+ data.callbacks.get('select')(data.element);
+ }
+ },
+
+ /**
+ * Callback for dropdowns being opened, flags items with a missing value for one or more languages.
+ *
+ * @param {string} containerId dropdown container id
+ * @param {string} action toggle action, can be `open` or `close`
+ */
+ _dropdownToggle: function(containerId, action) {
+ if (action !== 'open') {
+ return;
+ }
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(containerId);
+ var elementId = elData(elById(containerId), 'input-id');
+ var data = _elements.get(elementId);
+ var values = _values.get(elementId);
+
+ var item, languageId;
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ item = dropdownMenu.children[i];
+ languageId = ~~elData(item, 'language-id');
+
+ if (languageId) {
+ var hasMissingValue = false;
+ if (data.languageId) {
+ if (languageId === data.languageId) {
+ hasMissingValue = (data.element.value.trim() === '');
+ }
+ else {
+ hasMissingValue = (!values.get(languageId));
+ }
+ }
+
+ item.classList[(hasMissingValue ? 'add' : 'remove')]('missingValue');
+ }
+ }
+ },
+
+ /**
+ * Inserts hidden fields for i18n input on submit.
+ *
+ * @param {Object} event event object
+ */
+ _submit: function(event) {
+ var elementIds = _forms.get(event.currentTarget);
+
+ var data, elementId, input, values;
+ for (var i = 0, length = elementIds.length; i < length; i++) {
+ elementId = elementIds[i];
+ data = _elements.get(elementId);
+ if (data.isEnabled) {
+ values = _values.get(elementId);
+
+ if (data.callbacks.has('submit')) {
+ data.callbacks.get('submit')(data.element);
+ }
+
+ // update with current value
+ if (data.languageId) {
+ values.set(data.languageId, data.element.value);
+ }
+
+ if (values.size) {
+ values.forEach(function(value, languageId) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = elementId + '_i18n[' + languageId + ']';
+ input.value = value;
+
+ event.currentTarget.appendChild(input);
+ });
+
+ // remove name attribute to enforce i18n values
+ data.element.removeAttribute('name');
+ }
+ }
+ }
+ },
+
+ /**
+ * Returns the values of an input field.
+ *
+ * @param {string} elementId input element id
+ * @return {Dictionary} values stored for the different languages
+ */
+ getValues: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ var values = _values.get(elementId);
+
+ // update with current value
+ values.set(element.languageId, element.element.value);
+
+ return values;
+ },
+
+ /**
+ * Sets the values of an input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Dictionary} values values for the different languages
+ */
+ setValues: function(elementId, values) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ if (Core.isPlainObject(values)) {
+ values = Dictionary.fromObject(values);
+ }
+
+ element.element.value = '';
+
+ if (values.has(0)) {
+ element.element.value = values.get(0);
+ values['delete'](0);
+ _values.set(elementId, values);
+ this._select(elementId, 0, true);
+ return;
+ }
+
+ _values.set(elementId, values);
+
+ element.languageId = 0;
+ //noinspection JSUnresolvedVariable
+ this._select(elementId, LANGUAGE_ID, true);
+ },
+
+ /**
+ * Disables the i18n interface for an input field.
+ *
+ * @param {string} elementId input element id
+ */
+ disable: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid element, '" + elementId + "' is not an i18n input field.");
+ }
+
+ if (!element.isEnabled) return;
+
+ element.isEnabled = false;
+
+ // hide language dropdown
+ //noinspection JSCheckFunctionSignatures
+ elHide(element.buttonLabel.parentNode);
+ var dropdownContainer = element.buttonLabel.parentNode.parentNode;
+ dropdownContainer.classList.remove('inputAddon');
+ dropdownContainer.classList.remove('dropdown');
+ },
+
+ /**
+ * Enables the i18n interface for an input field.
+ *
+ * @param {string} elementId input element id
+ */
+ enable: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ if (element.isEnabled) return;
+
+ element.isEnabled = true;
+
+ // show language dropdown
+ //noinspection JSCheckFunctionSignatures
+ elShow(element.buttonLabel.parentNode);
+ var dropdownContainer = element.buttonLabel.parentNode.parentNode;
+ dropdownContainer.classList.add('inputAddon');
+ dropdownContainer.classList.add('dropdown');
+ },
+
+ /**
+ * Returns true if i18n input is enabled for an input field.
+ *
+ * @param {string} elementId input element id
+ * @return {boolean}
+ */
+ isEnabled: function(elementId) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ return element.isEnabled;
+ },
+
+ /**
+ * Returns true if the value of an i18n input field is valid.
+ *
+ * If the element is disabled, true is returned.
+ *
+ * @param {string} elementId input element id
+ * @param {boolean} permitEmptyValue if true, input may be empty for all languages
+ * @return {boolean} true if input is valid
+ */
+ validate: function(elementId, permitEmptyValue) {
+ var element = _elements.get(elementId);
+ if (element === undefined) {
+ throw new Error("Expected a valid i18n input element, '" + elementId + "' is not i18n input field.");
+ }
+
+ if (!element.isEnabled) return true;
+
+ var values = _values.get(elementId);
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(element.element.parentNode.id);
+
+ if (element.languageId) {
+ values.set(element.languageId, element.element.value);
+ }
+
+ var item, languageId;
+ var hasEmptyValue = false, hasNonEmptyValue = false;
+ for (var i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
+ item = dropdownMenu.children[i];
+ languageId = ~~elData(item, 'language-id');
+
+ if (languageId) {
+ if (!values.has(languageId) || values.get(languageId).length === 0) {
+ // input has non-empty value for previously checked language
+ if (hasNonEmptyValue) {
+ return false;
+ }
+
+ hasEmptyValue = true;
+ }
+ else {
+ // input has empty value for previously checked language
+ if (hasEmptyValue) {
+ return false;
+ }
+
+ hasNonEmptyValue = true;
+ }
+ }
+ }
+
+ return (!hasEmptyValue || permitEmptyValue);
+ }
+ };
+});
+
+/**
+ * I18n interface for wysiwyg input fields.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Language/Text
+ */
+define('WoltLabSuite/Core/Language/Text',['Core', './Input'], function (Core, LanguageInput) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Language/Text
+ */
+ return {
+ /**
+ * Initializes an WYSIWYG input field.
+ *
+ * @param {string} elementId input element id
+ * @param {Object} values preset values per language id
+ * @param {Object} availableLanguages language names per language id
+ * @param {boolean} forceSelection require i18n input
+ */
+ init: function(elementId, values, availableLanguages, forceSelection) {
+ var element = elById(elementId);
+ if (!element || element.nodeName !== 'TEXTAREA' || !element.classList.contains('wysiwygTextarea')) {
+ throw new Error("Expected <textarea class=\"wysiwygTextarea\" /> for id '" + elementId + "'.");
+ }
+
+ LanguageInput.init(elementId, values, availableLanguages, forceSelection);
+
+ //noinspection JSUnresolvedFunction
+ LanguageInput.registerCallback(elementId, 'select', this._callbackSelect.bind(this));
+ //noinspection JSUnresolvedFunction
+ LanguageInput.registerCallback(elementId, 'submit', this._callbackSubmit.bind(this));
+ },
+
+ /**
+ * Refreshes the editor content on language switch.
+ *
+ * @param {Element} element input element
+ * @protected
+ */
+ _callbackSelect: function (element) {
+ if (window.jQuery !== undefined) {
+ window.jQuery(element).redactor('code.set', element.value);
+ }
+ },
+
+ /**
+ * Refreshes the input element value on submit.
+ *
+ * @param {Element} element input element
+ * @protected
+ */
+ _callbackSubmit: function (element) {
+ if (window.jQuery !== undefined) {
+ element.value = window.jQuery(element).redactor('code.get');
+ }
+ }
+ }
+});
+
+/**
+ * Handles editing media files via dialog.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Editor
+ */
+define(
+ 'WoltLabSuite/Core/Media/Editor',[
+ 'Ajax', 'Core', 'Dictionary', 'Dom/ChangeListener',
+ 'Dom/Traverse', 'Language', 'Ui/Dialog', 'Ui/Notification',
+ 'WoltLabSuite/Core/Language/Chooser', 'WoltLabSuite/Core/Language/Input', 'EventKey'
+ ],
+ function(
+ Ajax, Core, Dictionary, DomChangeListener,
+ DomTraverse, Language, UiDialog, UiNotification,
+ LanguageChooser, LanguageInput, EventKey
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _close: function() {},
+ _keyPress: function() {},
+ _saveData: function() {},
+ _updateLanguageFields: function() {},
+ edit: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaEditor(callbackObject) {
+ this._callbackObject = callbackObject || {};
+
+ if (this._callbackObject._editorClose && typeof this._callbackObject._editorClose !== 'function') {
+ throw new TypeError("Callback object has no function '_editorClose'.");
+ }
+ if (this._callbackObject._editorSuccess && typeof this._callbackObject._editorSuccess !== 'function') {
+ throw new TypeError("Callback object has no function '_editorSuccess'.");
+ }
+
+ this._media = null;
+ this._availableLanguageCount = 1;
+ this._categoryIds = [];
+ this._oldCategoryId = 0;
+
+ this._dialogs = new Dictionary();
+ }
+ MediaEditor.prototype = {
+ /**
+ * Returns the data for Ajax to setup the Ajax/Request object.
+ *
+ * @return {object} setup data for Ajax/Request object
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'update',
+ className: 'wcf\\data\\media\\MediaAction'
+ }
+ };
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ UiNotification.show();
+
+ if (this._callbackObject._editorSuccess) {
+ this._callbackObject._editorSuccess(this._media, this._oldCategoryId);
+ this._oldCategoryId = 0;
+ }
+
+ UiDialog.close('mediaEditor_' + this._media.mediaID);
+
+ this._media = null;
+ },
+
+ /**
+ * Is called if an editor is manually closed by the user.
+ */
+ _close: function() {
+ this._media = null;
+
+ if (this._callbackObject._editorClose) {
+ this._callbackObject._editorClose();
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` key to submit the form.
+ *
+ * @param {object} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ this._saveData();
+ }
+ },
+
+ /**
+ * Saves the data of the currently edited media.
+ */
+ _saveData: function() {
+ var content = UiDialog.getDialog('mediaEditor_' + this._media.mediaID).content;
+
+ var categoryId = elBySel('select[name=categoryID]', content);
+ var altText = elBySel('input[name=altText]', content);
+ var caption = elBySel('textarea[name=caption]', content);
+ var captionEnableHtml = elBySel('input[name=captionEnableHtml]', content);
+ var title = elBySel('input[name=title]', content);
+
+ var hasError = false;
+ var altTextError = (altText ? DomTraverse.childByClass(altText.parentNode.parentNode, 'innerError') : false);
+ var captionError = (caption ? DomTraverse.childByClass(caption.parentNode.parentNode, 'innerError') : false);
+ var titleError = DomTraverse.childByClass(title.parentNode.parentNode, 'innerError');
+
+ // category
+ this._oldCategoryId = this._media.categoryID;
+ if (this._categoryIds.length) {
+ this._media.categoryID = ~~categoryId.value;
+
+ // if the selected category id not valid (manipulated DOM), ignore
+ if (this._categoryIds.indexOf(this._media.categoryID) === -1) {
+ this._media.categoryID = 0;
+ }
+ }
+
+ // language and multilingualism
+ if (this._availableLanguageCount > 1) {
+ this._media.isMultilingual = ~~elBySel('input[name=isMultilingual]', content).checked;
+ this._media.languageID = this._media.isMultilingual ? null : LanguageChooser.getLanguageId('mediaEditor_' + this._media.mediaID + '_languageID');
+ }
+ else {
+ this._media.languageID = LANGUAGE_ID;
+ }
+
+ // altText, caption and title
+ this._media.altText = {};
+ this._media.caption = {};
+ this._media.title = {};
+ if (this._availableLanguageCount > 1 && this._media.isMultilingual) {
+ if (elById('altText_' + this._media.mediaID) && !LanguageInput.validate('altText_' + this._media.mediaID, true)) {
+ hasError = true;
+ if (!altTextError) {
+ var error = elCreate('small');
+ error.className = 'innerError';
+ error.textContent = Language.get('wcf.global.form.error.multilingual');
+ altText.parentNode.parentNode.appendChild(error);
+ }
+ }
+ if (elById('caption_' + this._media.mediaID) && !LanguageInput.validate('caption_' + this._media.mediaID, true)) {
+ hasError = true;
+ if (!captionError) {
+ var error = elCreate('small');
+ error.className = 'innerError';
+ error.textContent = Language.get('wcf.global.form.error.multilingual');
+ caption.parentNode.parentNode.appendChild(error);
+ }
+ }
+ if (!LanguageInput.validate('title_' + this._media.mediaID, true)) {
+ hasError = true;
+ if (!titleError) {
+ var error = elCreate('small');
+ error.className = 'innerError';
+ error.textContent = Language.get('wcf.global.form.error.multilingual');
+ title.parentNode.parentNode.appendChild(error);
+ }
+ }
+
+ this._media.altText = (elById('altText_' + this._media.mediaID) ? LanguageInput.getValues('altText_' + this._media.mediaID).toObject() : '');
+ this._media.caption = (elById('caption_' + this._media.mediaID) ? LanguageInput.getValues('caption_' + this._media.mediaID).toObject() : '');
+ this._media.title = LanguageInput.getValues('title_' + this._media.mediaID).toObject();
+ }
+ else {
+ this._media.altText[this._media.languageID] = (altText ? altText.value : '');
+ this._media.caption[this._media.languageID] = (caption ? caption.value : '');
+ this._media.title[this._media.languageID] = title.value;
+ }
+
+ // captionEnableHtml
+ this._media.captionEnableHtml = ~~elBySel('input[name=captionEnableHtml]', content).checked;
+
+ var aclValues = {
+ allowAll: ~~elById('mediaEditor_' + this._media.mediaID + '_aclAllowAll').checked,
+ group: [],
+ user: []
+ };
+
+ var aclGroups = elBySelAll('input[name="aclValues[group][]"]', content);
+ for (var i = 0, length = aclGroups.length; i < length; i++) {
+ aclValues.group.push(~~aclGroups[i].value);
+ }
+
+ var aclUsers = elBySelAll('input[name="aclValues[user][]"]', content);
+ for (var i = 0, length = aclUsers.length; i < length; i++) {
+ aclValues.user.push(~~aclUsers[i].value);
+ }
+
+ if (!hasError) {
+ if (altTextError) elRemove(altTextError);
+ if (captionError) elRemove(captionError);
+ if (titleError) elRemove(titleError);
+
+ Ajax.api(this, {
+ actionName: 'update',
+ objectIDs: [ this._media.mediaID ],
+ parameters: {
+ aclValues: aclValues,
+ altText: this._media.altText,
+ caption: this._media.caption,
+ data: {
+ captionEnableHtml: this._media.captionEnableHtml,
+ categoryID: this._media.categoryID,
+ isMultilingual: this._media.isMultilingual,
+ languageID: this._media.languageID
+ },
+ title: this._media.title
+ }
+ });
+ }
+ },
+
+ /**
+ * Updates language-related input fields depending on whether multilingualism
+ * is enabled.
+ */
+ _updateLanguageFields: function(event, element) {
+ if (event) element = event.currentTarget;
+
+ var languageChooserContainer = elById('mediaEditor_' + this._media.mediaID + '_languageIDContainer').parentNode;
+
+ if (element.checked) {
+ LanguageInput.enable('title_' + this._media.mediaID);
+ if (elById('caption_' + this._media.mediaID)) LanguageInput.enable('caption_' + this._media.mediaID);
+ if (elById('altText_' + this._media.mediaID)) LanguageInput.enable('altText_' + this._media.mediaID);
+
+ elHide(languageChooserContainer);
+ }
+ else {
+ LanguageInput.disable('title_' + this._media.mediaID);
+ if (elById('caption_' + this._media.mediaID)) LanguageInput.disable('caption_' + this._media.mediaID);
+ if (elById('altText_' + this._media.mediaID)) LanguageInput.disable('altText_' + this._media.mediaID);
+
+ elShow(languageChooserContainer);
+ }
+ },
+
+ /**
+ * Edits the media with the given data.
+ *
+ * @param {object|integer} media data of the edited media or media id for which the data will be loaded
+ */
+ edit: function(media) {
+ if (typeof media !== 'object') {
+ media = {
+ mediaID: ~~media
+ };
+ }
+
+ if (this._media !== null) {
+ throw new Error("Cannot edit media with id '" + media.mediaID + "' while editing media with id '" + this._media.mediaID + "'");
+ }
+
+ this._media = media;
+
+ if (!this._dialogs.has('mediaEditor_' + media.mediaID)) {
+ this._dialogs.set('mediaEditor_' + media.mediaID, {
+ _dialogSetup: function() {
+ return {
+ id: 'mediaEditor_' + media.mediaID,
+ options: {
+ backdropCloseOnClick: false,
+ onClose: this._close.bind(this),
+ title: Language.get('wcf.media.edit')
+ },
+ source: {
+ after: (function(content, data) {
+ this._availableLanguageCount = ~~data.returnValues.availableLanguageCount;
+ this._categoryIds = data.returnValues.categoryIDs.map(function(number) {
+ return ~~number;
+ });
+
+ var didLoadMediaData = false;
+ if (data.returnValues.mediaData) {
+ this._media = data.returnValues.mediaData;
+
+ didLoadMediaData = true;
+ }
+
+ // make sure that the language chooser is initialized first
+ setTimeout(function() {
+ if (this._availableLanguageCount > 1) {
+ LanguageChooser.setLanguageId('mediaEditor_' + this._media.mediaID + '_languageID', this._media.languageID || LANGUAGE_ID);
+ }
+
+ if (this._categoryIds.length) {
+ elBySel('select[name=categoryID]', content).value = ~~this._media.categoryID;
+ }
+
+ var title = elBySel('input[name=title]', content);
+ var altText = elBySel('input[name=altText]', content);
+ var caption = elBySel('textarea[name=caption]', content);
+
+ if (this._availableLanguageCount > 1 && this._media.isMultilingual) {
+ if (elById('altText_' + this._media.mediaID)) LanguageInput.setValues('altText_' + this._media.mediaID, Dictionary.fromObject(this._media.altText || { }));
+ if (elById('caption_' + this._media.mediaID)) LanguageInput.setValues('caption_' + this._media.mediaID, Dictionary.fromObject(this._media.caption || { }));
+ LanguageInput.setValues('title_' + this._media.mediaID, Dictionary.fromObject(this._media.title || { }));
+ }
+ else {
+ title.value = this._media.title ? this._media.title[this._media.languageID || LANGUAGE_ID] : '';
+ if (altText) altText.value = this._media.altText ? this._media.altText[this._media.languageID || LANGUAGE_ID] : '';
+ if (caption) caption.value = this._media.caption ? this._media.caption[this._media.languageID || LANGUAGE_ID] : '';
+ }
+
+ if (this._availableLanguageCount > 1) {
+ var isMultilingual = elBySel('input[name=isMultilingual]', content);
+ isMultilingual.addEventListener('change', this._updateLanguageFields.bind(this));
+
+ this._updateLanguageFields(null, isMultilingual);
+ }
+
+ var keyPress = this._keyPress.bind(this);
+ if (altText) altText.addEventListener('keypress', keyPress);
+ title.addEventListener('keypress', keyPress);
+
+ elBySel('button[data-type=submit]', content).addEventListener(WCF_CLICK_EVENT, this._saveData.bind(this));
+
+ // remove focus from input elements and scroll dialog to top
+ document.activeElement.blur();
+ elById('mediaEditor_' + this._media.mediaID).parentNode.scrollTop = 0;
+
+ DomChangeListener.trigger();
+ }.bind(this), 200);
+ }).bind(this),
+ data: {
+ actionName: 'getEditorDialog',
+ className: 'wcf\\data\\media\\MediaAction',
+ objectIDs: [media.mediaID]
+ }
+ }
+ };
+ }.bind(this)
+ });
+ }
+
+ UiDialog.open(this._dialogs.get('mediaEditor_' + media.mediaID));
+ }
+ };
+
+ return MediaEditor;
+});
+
+/**
+ * Uploads media files.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Upload
+ */
+define(
+ 'WoltLabSuite/Core/Media/Upload',[
+ 'Core',
+ 'DateUtil',
+ 'Dom/ChangeListener',
+ 'Dom/Traverse',
+ 'Dom/Util',
+ 'EventHandler',
+ 'Language',
+ 'Permission',
+ 'Upload',
+ 'User',
+ 'WoltLabSuite/Core/FileUtil'
+ ],
+ function(
+ Core,
+ DateUtil,
+ DomChangeListener,
+ DomTraverse,
+ DomUtil,
+ EventHandler,
+ Language,
+ Permission,
+ Upload,
+ User,
+ FileUtil
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _createFileElement: function() {},
+ _getParameters: function() {},
+ _success: function() {},
+ _uploadFiles: function() {},
+ _createButton: function() {},
+ _createFileElements: function() {},
+ _failure: function() {},
+ _insertButton: function() {},
+ _progress: function() {},
+ _removeButton: function() {},
+ _upload: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaUpload(buttonContainerId, targetId, options) {
+ options = options || {};
+
+ this._mediaManager = null;
+ if (options.mediaManager) {
+ this._mediaManager = options.mediaManager;
+ delete options.mediaManager;
+ }
+ this._categoryId = null;
+
+ Upload.call(this, buttonContainerId, targetId, Core.extend({
+ className: 'wcf\\data\\media\\MediaAction',
+ multiple: this._mediaManager ? true : false,
+ singleFileRequests: true
+ }, options));
+ }
+ Core.inherit(MediaUpload, Upload, {
+ /**
+ * @see WoltLabSuite/Core/Upload#_createFileElement
+ */
+ _createFileElement: function(file) {
+ var fileElement;
+ if (this._target.nodeName === 'OL' || this._target.nodeName === 'UL') {
+ fileElement = elCreate('li');
+ }
+ else if (this._target.nodeName === 'TBODY') {
+ var firstTr = elByTag('TR', this._target)[0];
+ var tableContainer = this._target.parentNode.parentNode;
+ if (tableContainer.style.getPropertyValue('display') === 'none') {
+ fileElement = firstTr;
+
+ tableContainer.style.removeProperty('display');
+
+ elRemove(elById(elData(this._target, 'no-items-info')));
+ }
+ else {
+ fileElement = firstTr.cloneNode(true);
+
+ // regenerate id of table row
+ fileElement.removeAttribute('id');
+ DomUtil.identify(fileElement);
+ }
+
+ var cells = elByTag('TD', fileElement), cell;
+ for (var i = 0, length = cells.length; i < length; i++) {
+ cell = cells[i];
+
+ if (cell.classList.contains('columnMark')) {
+ elBySelAll('[data-object-id]', cell, elHide);
+ }
+ else if (cell.classList.contains('columnIcon')) {
+ elBySelAll('[data-object-id]', cell, elHide);
+
+ elByClass('mediaEditButton', cell)[0].classList.add('jsMediaEditButton');
+ elData(elByClass('jsDeleteButton', cell)[0], 'confirm-message-html', Language.get('wcf.media.delete.confirmMessage', {
+ title: file.name
+ }));
+ }
+ else if (cell.classList.contains('columnFilename')) {
+ // replace copied image with spinner
+ var image = elByTag('IMG', cell);
+
+ if (!image.length) {
+ image = elByClass('icon48', cell);
+ }
+
+ var spinner = elCreate('span');
+ spinner.className = 'icon icon48 fa-spinner mediaThumbnail';
+
+ DomUtil.replaceElement(image[0], spinner);
+
+ // replace title and uploading user
+ var ps = elBySelAll('.box48 > div > p', cell);
+ ps[0].textContent = file.name;
+
+ var userLink = elByTag('A', ps[1])[0];
+ if (!userLink) {
+ userLink = elCreate('a');
+ elByTag('SMALL', ps[1])[0].appendChild(userLink);
+ }
+
+ userLink.setAttribute('href', User.getLink());
+ userLink.textContent = User.username;
+ }
+ else if (cell.classList.contains('columnUploadTime')) {
+ cell.innerHTML = '';
+ cell.appendChild(DateUtil.getTimeElement(new Date()));
+ }
+ else if (cell.classList.contains('columnDigits')) {
+ cell.textContent = FileUtil.formatFilesize(file.size);
+ }
+ else {
+ // empty the other cells
+ cell.innerHTML = '';
+ }
+ }
+
+ DomUtil.prepend(fileElement, this._target);
+
+ return fileElement;
+ }
+ else {
+ fileElement = elCreate('p');
+ }
+
+ var thumbnail = elCreate('div');
+ thumbnail.className = 'mediaThumbnail';
+ fileElement.appendChild(thumbnail);
+
+ var fileIcon = elCreate('span');
+ fileIcon.className = 'icon icon144 fa-spinner';
+ thumbnail.appendChild(fileIcon);
+
+ var mediaInformation = elCreate('div');
+ mediaInformation.className = 'mediaInformation';
+ fileElement.appendChild(mediaInformation);
+
+ var p = elCreate('p');
+ p.className = 'mediaTitle';
+ p.textContent = file.name;
+ mediaInformation.appendChild(p);
+
+ var progress = elCreate('progress');
+ elAttr(progress, 'max', 100);
+ mediaInformation.appendChild(progress);
+
+ DomUtil.prepend(fileElement, this._target);
+
+ DomChangeListener.trigger();
+
+ return fileElement;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_getParameters
+ */
+ _getParameters: function() {
+ if (this._mediaManager) {
+ var parameters = {
+ imagesOnly: this._mediaManager.getOption('imagesOnly')
+ };
+
+ var categoryId = this._mediaManager.getCategoryId();
+ if (categoryId) {
+ parameters.categoryID = categoryId;
+ }
+
+ return Core.extend(MediaUpload._super.prototype._getParameters.call(this), parameters);
+ }
+
+ return MediaUpload._super.prototype._getParameters.call(this);
+ },
+
+ /**
+ * Replaces the default or copied file icon with the actual file icon.
+ *
+ * @param {HTMLElement} fileIcon file icon element
+ * @param {object} media media data
+ * @param {integer} size size of the file icon in pixels
+ */
+ _replaceFileIcon: function(fileIcon, media, size) {
+ if (media.tinyThumbnailType) {
+ var img = elCreate('img');
+ elAttr(img, 'src', media.tinyThumbnailLink);
+ elAttr(img, 'alt', '');
+ img.style.setProperty('width', size + 'px');
+ img.style.setProperty('height', size + 'px');
+
+ DomUtil.replaceElement(fileIcon, img);
+ }
+ else {
+ fileIcon.classList.remove('fa-spinner');
+
+ var fileIconName = FileUtil.getIconNameByFilename(media.filename);
+ if (fileIconName) {
+ fileIconName = '-' + fileIconName;
+ }
+ fileIcon.classList.add('fa-file' + fileIconName + '-o');
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_success
+ */
+ _success: function(uploadId, data) {
+ var files = this._fileElements[uploadId];
+
+ for (var i = 0, length = files.length; i < length; i++) {
+ var file = files[i];
+ var internalFileId = elData(file, 'internal-file-id');
+ var media = data.returnValues.media[internalFileId];
+
+ if (file.tagName === 'TR') {
+ if (media) {
+ // update object id
+ var objectIdElements = elBySelAll('[data-object-id]', file);
+ for (var i = 0, length = objectIdElements.length; i < length; i++) {
+ elData(objectIdElements[i], 'object-id', ~~media.mediaID);
+ elShow(objectIdElements[i]);
+ }
+
+ elByClass('columnMediaID', file)[0].textContent = media.mediaID;
+
+ // update icon
+ var fileIcon = elByClass('fa-spinner', file)[0];
+ this._replaceFileIcon(fileIcon, media, 48);
+ }
+ else {
+ var error = data.returnValues.errors[internalFileId];
+ if (!error) {
+ error = {
+ errorType: 'uploadFailed',
+ filename: elData(file, 'filename')
+ };
+ }
+
+ var fileIcon = elByClass('fa-spinner', file)[0];
+ fileIcon.classList.remove('fa-spinner');
+ fileIcon.classList.add('fa-remove');
+ fileIcon.classList.add('pointer');
+ fileIcon.classList.add('jsTooltip');
+ elAttr(fileIcon, 'title', Language.get('wcf.global.button.delete'));
+ fileIcon.addEventListener(WCF_CLICK_EVENT, function (event) {
+ elRemove(event.currentTarget.parentNode.parentNode.parentNode);
+
+ EventHandler.fire('com.woltlab.wcf.media.upload', 'removedErroneousUploadRow');
+ });
+
+ file.classList.add('uploadFailed');
+
+ var p = elBySelAll('.columnFilename .box48 > div > p', file)[1];
+
+ elInnerError(p, Language.get('wcf.media.upload.error.' + error.errorType, {
+ filename: error.filename
+ }));
+
+ elRemove(p);
+ }
+ }
+ else {
+ elRemove(DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaInformation'), 'PROGRESS'));
+
+ if (media) {
+ var fileIcon = DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaThumbnail'), 'SPAN');
+ this._replaceFileIcon(fileIcon, media, 144);
+
+ file.className = 'jsClipboardObject mediaFile';
+ elData(file, 'object-id', media.mediaID);
+
+ if (this._mediaManager) {
+ this._mediaManager.setupMediaElement(media, file);
+ this._mediaManager.addMedia(media, file);
+ }
+ }
+ else {
+ var error = data.returnValues.errors[internalFileId];
+ if (!error) {
+ error = {
+ errorType: 'uploadFailed',
+ filename: elData(file, 'filename')
+ };
+ }
+
+ var fileIcon = DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaThumbnail'), 'SPAN');
+ fileIcon.classList.remove('fa-spinner');
+ fileIcon.classList.add('fa-remove');
+ fileIcon.classList.add('pointer');
+
+ file.classList.add('uploadFailed');
+ file.classList.add('jsTooltip');
+ elAttr(file, 'title', Language.get('wcf.global.button.delete'));
+ file.addEventListener(WCF_CLICK_EVENT, function () {
+ elRemove(this);
+ });
+
+ var title = DomTraverse.childByClass(DomTraverse.childByClass(file, 'mediaInformation'), 'mediaTitle');
+ title.innerText = Language.get('wcf.media.upload.error.' + error.errorType, {
+ filename: error.filename
+ });
+ }
+ }
+
+ DomChangeListener.trigger();
+ }
+
+ EventHandler.fire('com.woltlab.wcf.media.upload', 'success', {
+ files: files,
+ isMultiFileUpload: this._multiFileUploadIds.indexOf(uploadId) !== -1,
+ media: data.returnValues.media,
+ upload: this,
+ uploadId: uploadId
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_uploadFiles
+ */
+ _uploadFiles: function(files, blob) {
+ return MediaUpload._super.prototype._uploadFiles.call(this, files, blob);
+ }
+ });
+
+ return MediaUpload;
+});
+
+/**
+ * Uploads media files.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/List/Upload
+ */
+define(
+ 'WoltLabSuite/Core/Media/List/Upload',[
+ 'Core', 'Dom/Util', '../Upload'
+ ],
+ function(
+ Core, DomUtil, MediaUpload
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _createButton: function() {},
+ _success: function() {},
+ _upload: function() {},
+ _createFileElement: function() {},
+ _getParameters: function() {},
+ _uploadFiles: function() {},
+ _createFileElements: function() {},
+ _failure: function() {},
+ _insertButton: function() {},
+ _progress: function() {},
+ _removeButton: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaListUpload(buttonContainerId, targetId, options) {
+ MediaUpload.call(this, buttonContainerId, targetId, options);
+ }
+ Core.inherit(MediaListUpload, MediaUpload, {
+ /**
+ * Creates the upload button.
+ */
+ _createButton: function() {
+ MediaListUpload._super.prototype._createButton.call(this);
+
+ var span = elBySel('span', this._button);
+
+ var space = document.createTextNode(' ');
+ DomUtil.prepend(space, span);
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-upload';
+ DomUtil.prepend(icon, span);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_getParameters
+ */
+ _getParameters: function() {
+ if (this._options.categoryId) {
+ return Core.extend(MediaListUpload._super.prototype._getParameters.call(this), {
+ categoryID: this._options.categoryId
+ });
+ }
+
+ return MediaListUpload._super.prototype._getParameters.call(this);
+ }
+ });
+
+ return MediaListUpload;
+});
+
+/**
+ * Initializes modules required for media clipboard.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Clipboard
+ */
+define('WoltLabSuite/Core/Media/Clipboard',[
+ 'Ajax',
+ 'Dom/ChangeListener',
+ 'EventHandler',
+ 'Language',
+ 'Ui/Dialog',
+ 'Ui/Notification',
+ 'WoltLabSuite/Core/Controller/Clipboard',
+ 'WoltLabSuite/Core/Media/Editor',
+ 'WoltLabSuite/Core/Media/List/Upload'
+ ],
+ function(
+ Ajax,
+ DomChangeListener,
+ EventHandler,
+ Language,
+ UiDialog,
+ UiNotification,
+ Clipboard,
+ MediaEditor,
+ MediaListUpload
+ ) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _clipboardAction: function() {},
+ _dialogSetup: function() {},
+ _edit: function() {},
+ _setCategory: function() {}
+ };
+ return Fake;
+ }
+
+ var _clipboardObjectIds = [];
+ var _mediaManager;
+
+ /**
+ * @exports WoltLabSuite/Core/Media/Clipboard
+ */
+ return {
+ init: function(pageClassName, hasMarkedItems, mediaManager) {
+ Clipboard.setup({
+ hasMarkedItems: hasMarkedItems,
+ pageClassName: pageClassName
+ });
+
+ _mediaManager = mediaManager;
+
+ EventHandler.add('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.media', this._clipboardAction.bind(this));
+ },
+
+ /**
+ * Returns the data used to setup the AJAX request object.
+ *
+ * @return {object} setup data
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\media\\MediaAction'
+ }
+ }
+ },
+
+ /**
+ * Handles successful AJAX request.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'getSetCategoryDialog':
+ UiDialog.open(this, data.returnValues.template);
+
+ break;
+
+ case 'setCategory':
+ UiDialog.close(this);
+
+ UiNotification.show();
+
+ Clipboard.reload();
+
+ break;
+ }
+ },
+
+ /**
+ * Returns the data used to setup the dialog.
+ *
+ * @return {object} setup data
+ */
+ _dialogSetup: function() {
+ return {
+ id: 'mediaSetCategoryDialog',
+ options: {
+ onSetup: function(content) {
+ elBySel('button', content).addEventListener(WCF_CLICK_EVENT, function(event) {
+ event.preventDefault();
+
+ this._setCategory(~~elBySel('select[name="categoryID"]', content).value);
+
+ event.currentTarget.disabled = true;
+ }.bind(this));
+ }.bind(this),
+ title: Language.get('wcf.media.setCategory')
+ },
+ source: null
+ }
+ },
+
+ /**
+ * Handles successful clipboard actions.
+ *
+ * @param {object} actionData
+ */
+ _clipboardAction: function(actionData) {
+ var mediaIds = actionData.data.parameters.objectIDs;
+
+ switch (actionData.data.actionName) {
+ case 'com.woltlab.wcf.media.delete':
+ // only consider events if the action has been executed
+ if (actionData.responseData !== null) {
+ _mediaManager.clipboardDeleteMedia(mediaIds);
+ }
+
+ break;
+
+ case 'com.woltlab.wcf.media.insert':
+ _mediaManager.clipboardInsertMedia(mediaIds);
+
+ break;
+
+ case 'com.woltlab.wcf.media.setCategory':
+ _clipboardObjectIds = mediaIds;
+
+ Ajax.api(this, {
+ actionName: 'getSetCategoryDialog'
+ });
+
+ break;
+ }
+ },
+
+ /**
+ * Sets the category of the marked media files.
+ *
+ * @param {int} categoryID selected category id
+ */
+ _setCategory: function(categoryID) {
+ Ajax.api(this, {
+ actionName: 'setCategory',
+ objectIDs: _clipboardObjectIds,
+ parameters: {
+ categoryID: categoryID
+ }
+ });
+ }
+ }
+});
+/**
+ * Provides desktop notifications via periodic polling with an
+ * increasing request delay on inactivity.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Notification/Handler
+ */
+define('WoltLabSuite/Core/Notification/Handler',['Ajax', 'Core', 'EventHandler', 'StringUtil'], function(Ajax, Core, EventHandler, StringUtil) {
+ "use strict";
+
+ if (!('Promise' in window) || !('Notification' in window)) {
+ // fake object exposed to ancient browsers (*cough* IE11 *cough*)
+ return {
+ setup: function () {}
+ }
+ }
+
+ var _allowNotification = false;
+ var _icon = '';
+ var _inactiveSince = 0;
+ //noinspection JSUnresolvedVariable
+ var _lastRequestTimestamp = window.TIME_NOW;
+ var _requestTimer = null;
+ var _sessionKeepAlive = 0;
+
+ /**
+ * @exports WoltLabSuite/Core/Notification/Handler
+ */
+ return {
+ /**
+ * Initializes the desktop notification system.
+ *
+ * @param {Object} options initialization options
+ */
+ setup: function (options) {
+ options = Core.extend({
+ enableNotifications: false,
+ icon: '',
+ sessionKeepAlive: 0
+ }, options);
+
+ _icon = options.icon;
+ _sessionKeepAlive = options.sessionKeepAlive * 60;
+
+ this._prepareNextRequest();
+
+ document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
+ window.addEventListener('storage', this._onStorage.bind(this));
+
+ this._onVisibilityChange(null);
+
+ if (options.enableNotifications) {
+ switch (window.Notification.permission) {
+ case 'granted':
+ _allowNotification = true;
+ break;
+ case 'default':
+ window.Notification.requestPermission(function (result) {
+ if (result === 'granted') {
+ _allowNotification = true;
+ }
+ });
+ break;
+ }
+ }
+ },
+
+ /**
+ * Detects when this window is hidden or restored.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _onVisibilityChange: function(event) {
+ // document was hidden before
+ if (event !== null && !document.hidden) {
+ var difference = (Date.now() - _inactiveSince) / 60000;
+ if (difference > 4) {
+ this._resetTimer();
+ this._dispatchRequest();
+ }
+ }
+
+ _inactiveSince = (document.hidden) ? Date.now() : 0;
+ },
+
+ /**
+ * Returns the delay in minutes before the next request should be dispatched.
+ *
+ * @return {int}
+ * @protected
+ */
+ _getNextDelay: function() {
+ if (_inactiveSince === 0) return 5;
+
+ // milliseconds -> minutes
+ var inactiveMinutes = ~~((Date.now() - _inactiveSince) / 60000);
+ if (inactiveMinutes < 15) {
+ return 5;
+ }
+ else if (inactiveMinutes < 30) {
+ return 10;
+ }
+
+ return 15;
+ },
+
+ /**
+ * Resets the request delay timer.
+ *
+ * @protected
+ */
+ _resetTimer: function() {
+ if (_requestTimer !== null) {
+ window.clearTimeout(_requestTimer);
+ _requestTimer = null;
+ }
+ },
+
+ /**
+ * Schedules the next request using a calculated delay.
+ *
+ * @protected
+ */
+ _prepareNextRequest: function() {
+ this._resetTimer();
+
+ var delay = Math.min(this._getNextDelay(), _sessionKeepAlive);
+ _requestTimer = window.setTimeout(this._dispatchRequest.bind(this), delay * 60000);
+ },
+
+ /**
+ * Requests new data from the server.
+ *
+ * @protected
+ */
+ _dispatchRequest: function() {
+ var parameters = {};
+ EventHandler.fire('com.woltlab.wcf.notification', 'beforePoll', parameters);
+
+ // this timestamp is used to determine new notifications and to avoid
+ // notifications being displayed multiple times due to different origins
+ // (=subdomains) used, because we cannot synchronize them in the client
+ parameters.lastRequestTimestamp = _lastRequestTimestamp;
+
+ Ajax.api(this, {
+ parameters: parameters
+ });
+ },
+
+ /**
+ * Notifies subscribers for updated data received by another tab.
+ *
+ * @protected
+ */
+ _onStorage: function() {
+ // abort and re-schedule periodic request
+ this._prepareNextRequest();
+
+ var pollData, keepAliveData, abort = false;
+ try {
+ pollData = window.localStorage.getItem(Core.getStoragePrefix() + 'notification');
+ keepAliveData = window.localStorage.getItem(Core.getStoragePrefix() + 'keepAliveData');
+
+ pollData = JSON.parse(pollData);
+ keepAliveData = JSON.parse(keepAliveData);
+ }
+ catch (e) {
+ abort = true;
+ }
+
+ if (!abort) {
+ EventHandler.fire('com.woltlab.wcf.notification', 'onStorage', {
+ pollData: pollData,
+ keepAliveData: keepAliveData
+ });
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ var abort = false;
+ var keepAliveData = data.returnValues.keepAliveData;
+ var pollData = data.returnValues.pollData;
+
+ // forward keep alive data
+ window.WCF.System.PushNotification.executeCallbacks(keepAliveData);
+
+ // store response data in local storage
+ try {
+ window.localStorage.setItem(Core.getStoragePrefix() + 'notification', JSON.stringify(pollData));
+ window.localStorage.setItem(Core.getStoragePrefix() + 'keepAliveData', JSON.stringify(keepAliveData));
+ }
+ catch (e) {
+ // storage is unavailable, e.g. in private mode, log error and disable polling
+ abort = true;
+
+ window.console.log(e);
+ }
+
+ if (!abort) {
+ this._prepareNextRequest();
+ }
+
+ _lastRequestTimestamp = data.returnValues.lastRequestTimestamp;
+
+ EventHandler.fire('com.woltlab.wcf.notification', 'afterPoll', pollData);
+
+ this._showNotification(pollData);
+ },
+
+ /**
+ * Displays a desktop notification.
+ *
+ * @param {Object} pollData
+ * @protected
+ */
+ _showNotification: function(pollData) {
+ if (!_allowNotification) {
+ return;
+ }
+
+ //noinspection JSUnresolvedVariable
+ if (typeof pollData.notification === 'object' && typeof pollData.notification.message === 'string') {
+ //noinspection JSUnresolvedVariable
+ var notification = new window.Notification(pollData.notification.title, {
+ body: StringUtil.unescapeHTML(pollData.notification.message),
+ icon: _icon
+ });
+ notification.onclick = function () {
+ window.focus();
+ notification.close();
+
+ //noinspection JSUnresolvedVariable
+ window.location = pollData.notification.link;
+ };
+ }
+ },
+
+ _ajaxSetup: function() {
+ //noinspection JSUnresolvedVariable
+ return {
+ data: {
+ actionName: 'poll',
+ className: 'wcf\\data\\session\\SessionAction'
+ },
+ ignoreError: !window.ENABLE_DEBUG_MODE,
+ silent: !window.ENABLE_DEBUG_MODE
+ };
+ }
+ }
+});
+
+/**
+ * Drag and Drop file uploads.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/DragAndDrop
+ */
+define('WoltLabSuite/Core/Ui/Redactor/DragAndDrop',['Dictionary', 'EventHandler', 'Language'], function (Dictionary, EventHandler, Language) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _dragOver: function() {},
+ _drop: function() {},
+ _dragLeave: function() {},
+ _setup: function() {}
+ };
+ return Fake;
+ }
+
+ var _didInit = false;
+ var _dragArea = new Dictionary();
+ var _isDragging = false;
+ var _isFile = false;
+ var _timerLeave = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Redactor/DragAndDrop
+ */
+ return {
+ /**
+ * Initializes drag and drop support for provided editor instance.
+ *
+ * @param {$.Redactor} editor editor instance
+ */
+ init: function (editor) {
+ if (!_didInit) {
+ this._setup();
+ }
+
+ _dragArea.set(editor.uuid, {
+ editor: editor,
+ element: null
+ });
+ },
+
+ /**
+ * Handles items dragged into the browser window.
+ *
+ * @param {Event} event drag event
+ */
+ _dragOver: function (event) {
+ event.preventDefault();
+
+ //noinspection JSUnresolvedVariable
+ if (!event.dataTransfer || !event.dataTransfer.types) {
+ return;
+ }
+
+ var isFirefox = false;
+ //noinspection JSUnresolvedVariable
+ for (var property in event.dataTransfer) {
+ //noinspection JSUnresolvedVariable
+ if (event.dataTransfer.hasOwnProperty(property) && property.match(/^moz/)) {
+ isFirefox = true;
+ break;
+ }
+ }
+
+ // IE and WebKit set 'Files', Firefox sets 'application/x-moz-file' for files being dragged
+ // and Safari just provides 'Files' along with a huge list of garbage
+ _isFile = false;
+ if (isFirefox) {
+ // Firefox sets the 'Files' type even if the user is just dragging an on-page element
+ //noinspection JSUnresolvedVariable
+ if (event.dataTransfer.types[0] === 'application/x-moz-file') {
+ _isFile = true;
+ }
+ }
+ else {
+ //noinspection JSUnresolvedVariable
+ for (var i = 0; i < event.dataTransfer.types.length; i++) {
+ //noinspection JSUnresolvedVariable
+ if (event.dataTransfer.types[i] === 'Files') {
+ _isFile = true;
+ break;
+ }
+ }
+ }
+
+ if (!_isFile) {
+ // user is just dragging around some garbage, ignore it
+ return;
+ }
+
+ if (_isDragging) {
+ // user is still dragging the file around
+ return;
+ }
+
+ _isDragging = true;
+
+ _dragArea.forEach((function (data, uuid) {
+ var editor = data.editor.$editor[0];
+ if (!editor.parentNode) {
+ _dragArea.delete(uuid);
+ return;
+ }
+
+ var element = data.element;
+ if (element === null) {
+ element = elCreate('div');
+ element.className = 'redactorDropArea';
+ elData(element, 'element-id', data.editor.$element[0].id);
+ elData(element, 'drop-here', Language.get('wcf.attachment.dragAndDrop.dropHere'));
+ elData(element, 'drop-now', Language.get('wcf.attachment.dragAndDrop.dropNow'));
+
+ element.addEventListener('dragover', function () { element.classList.add('active'); });
+ element.addEventListener('dragleave', function () { element.classList.remove('active'); });
+ element.addEventListener('drop', this._drop.bind(this));
+
+ data.element = element;
+ }
+
+ editor.parentNode.insertBefore(element, editor);
+ element.style.setProperty('top', editor.offsetTop + 'px', '');
+ }).bind(this));
+ },
+
+ /**
+ * Handles items dropped onto an editor's drop area
+ *
+ * @param {Event} event drop event
+ * @protected
+ */
+ _drop: function (event) {
+ if (!_isFile) {
+ return;
+ }
+
+ //noinspection JSUnresolvedVariable
+ if (!event.dataTransfer || !event.dataTransfer.files.length) {
+ return;
+ }
+
+ event.preventDefault();
+
+ //noinspection JSCheckFunctionSignatures
+ var elementId = elData(event.currentTarget, 'element-id');
+
+ //noinspection JSUnresolvedVariable
+ for (var i = 0, length = event.dataTransfer.files.length; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'dragAndDrop_' + elementId, {
+ file: event.dataTransfer.files[i]
+ });
+ }
+
+ // this will reset all drop areas
+ this._dragLeave();
+ },
+
+ /**
+ * Invoked whenever the item is no longer dragged or was dropped.
+ *
+ * @protected
+ */
+ _dragLeave: function () {
+ if (!_isDragging || !_isFile) {
+ return;
+ }
+
+ if (_timerLeave !== null) {
+ window.clearTimeout(_timerLeave);
+ }
+
+ _timerLeave = window.setTimeout(function () {
+ if (!_isDragging) {
+ _dragArea.forEach(function (data) {
+ if (data.element && data.element.parentNode) {
+ data.element.classList.remove('active');
+ elRemove(data.element);
+ }
+ });
+ }
+
+ _timerLeave = null;
+ }, 100);
+
+ _isDragging = false;
+ },
+
+ /**
+ * Handles the global drop event.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _globalDrop: function (event) {
+ if (event.target.closest('.redactor-layer') === null) {
+ var eventData = { cancelDrop: true, event: event };
+ _dragArea.forEach(function(data) {
+ //noinspection JSUnresolvedVariable
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'dragAndDrop_globalDrop_' + data.editor.$element[0].id, eventData);
+ });
+
+ if (eventData.cancelDrop) {
+ event.preventDefault();
+ }
+ }
+
+ this._dragLeave(event);
+ },
+
+ /**
+ * Binds listeners to global events.
+ *
+ * @protected
+ */
+ _setup: function () {
+ // discard garbage event
+ window.addEventListener('dragend', function (event) { event.preventDefault(); });
+
+ window.addEventListener('dragover', this._dragOver.bind(this));
+ window.addEventListener('dragleave', this._dragLeave.bind(this));
+ window.addEventListener('drop', this._globalDrop.bind(this));
+
+ _didInit = true;
+ }
+ };
+});
+
+/**
+ * Generic interface for drag and Drop file uploads.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/DragAndDrop
+ */
+define('WoltLabSuite/Core/Ui/DragAndDrop',['Core', 'EventHandler', 'WoltLabSuite/Core/Ui/Redactor/DragAndDrop'], function (Core, EventHandler, UiRedactorDragAndDrop) {
+ /**
+ * @exports WoltLabSuite/Core/Ui/DragAndDrop
+ */
+ return {
+ /**
+ * @param {Object} options
+ */
+ register: function (options) {
+ var uuid = Core.getUuid();
+ options = Core.extend({
+ element: '',
+ elementId: '',
+ onDrop: function(data) {
+ /* data: { file: File } */
+ },
+ onGlobalDrop: function (data) {
+ /* data: { cancelDrop: boolean, event: DragEvent } */
+ }
+ });
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_' + options.elementId, options.onDrop);
+ EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_globalDrop_' + options.elementId, options.onGlobalDrop);
+
+ UiRedactorDragAndDrop.init({
+ uuid: uuid,
+ $editor: [options.element],
+ $element: [{id: options.elementId}]
+ });
+ }
+ };
+});
+
+/**
+ * Flexible UI element featuring both a list of items and an input field with suggestion support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Suggestion
+ */
+define('WoltLabSuite/Core/Ui/Suggestion',['Ajax', 'Core', 'Ui/SimpleDropdown'], function(Ajax, Core, UiSimpleDropdown) {
+ "use strict";
+
+ /**
+ * @constructor
+ * @param {string} elementId input element id
+ * @param {Object} options option list
+ */
+ function UiSuggestion(elementId, options) { this.init(elementId, options); }
+ UiSuggestion.prototype = {
+ /**
+ * Initializes a new suggestion input.
+ *
+ * @param {string} elementId input element id
+ * @param {Object} options option list
+ */
+ init: function(elementId, options) {
+ this._dropdownMenu = null;
+ this._value = '';
+
+ this._element = elById(elementId);
+ if (this._element === null) {
+ throw new Error("Expected a valid element id.");
+ }
+
+ this._options = Core.extend({
+ ajax: {
+ actionName: 'getSearchResultList',
+ className: '',
+ interfaceName: 'wcf\\data\\ISearchAction',
+ parameters: {
+ data: {}
+ }
+ },
+
+ // will be executed once a value from the dropdown has been selected
+ callbackSelect: null,
+ // list of excluded search values
+ excludedSearchValues: [],
+ // minimum number of characters required to trigger a search request
+ threshold: 3
+ }, options);
+
+ if (typeof this._options.callbackSelect !== 'function') {
+ throw new Error("Expected a valid callback for option 'callbackSelect'.");
+ }
+
+ this._element.addEventListener(WCF_CLICK_EVENT, function(event) { event.stopPropagation(); });
+ this._element.addEventListener('keydown', this._keyDown.bind(this));
+ this._element.addEventListener('keyup', this._keyUp.bind(this));
+ },
+
+ /**
+ * Adds an excluded search value.
+ *
+ * @param {string} value excluded value
+ */
+ addExcludedValue: function(value) {
+ if (this._options.excludedSearchValues.indexOf(value) === -1) {
+ this._options.excludedSearchValues.push(value);
+ }
+ },
+
+ /**
+ * Removes an excluded search value.
+ *
+ * @param {string} value excluded value
+ */
+ removeExcludedValue: function(value) {
+ var index = this._options.excludedSearchValues.indexOf(value);
+ if (index !== -1) {
+ this._options.excludedSearchValues.splice(index, 1);
+ }
+ },
+
+ /**
+ * Returns true if the suggestions are active.
+ * @return {boolean}
+ */
+ isActive: function() {
+ return (this._dropdownMenu !== null && UiSimpleDropdown.isOpen(this._element.id));
+ },
+
+ /**
+ * Handles the keyboard navigation for interaction with the suggestion list.
+ *
+ * @param {object} event event object
+ */
+ _keyDown: function(event) {
+ if (!this.isActive()) {
+ return true;
+ }
+
+ if (event.keyCode !== 13 && event.keyCode !== 27 && event.keyCode !== 38 && event.keyCode !== 40) {
+ return true;
+ }
+
+ var active, i = 0, length = this._dropdownMenu.childElementCount;
+ while (i < length) {
+ active = this._dropdownMenu.children[i];
+ if (active.classList.contains('active')) {
+ break;
+ }
+
+ i++;
+ }
+
+ if (event.keyCode === 13) {
+ // Enter
+ UiSimpleDropdown.close(this._element.id);
+
+ this._select(active);
+ }
+ else if (event.keyCode === 27) {
+ if (UiSimpleDropdown.isOpen(this._element.id)) {
+ UiSimpleDropdown.close(this._element.id);
+ }
+ else {
+ // let the event pass through
+ return true;
+ }
+ }
+ else {
+ var index = 0;
+
+ if (event.keyCode === 38) {
+ // ArrowUp
+ index = ((i === 0) ? length : i) - 1;
+ }
+ else if (event.keyCode === 40) {
+ // ArrowDown
+ index = i + 1;
+ if (index === length) index = 0;
+ }
+
+ if (index !== i) {
+ active.classList.remove('active');
+ this._dropdownMenu.children[index].classList.add('active');
+ }
+ }
+
+ event.preventDefault();
+ return false;
+ },
+
+ /**
+ * Selects an item from the list.
+ *
+ * @param {(Element|Event)} item list item or event object
+ */
+ _select: function(item) {
+ var isEvent = (item instanceof Event);
+ if (isEvent) {
+ item = item.currentTarget.parentNode;
+ }
+
+ var anchor = item.children[0];
+ this._options.callbackSelect(this._element.id, { objectId: elData(anchor, 'object-id'), value: item.textContent, type: elData(anchor, 'type') });
+
+ if (isEvent) {
+ this._element.focus();
+ }
+ },
+
+ /**
+ * Performs a search for the input value unless it is below the threshold.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ var value = event.currentTarget.value.trim();
+
+ if (this._value === value) {
+ return;
+ }
+ else if (value.length < this._options.threshold) {
+ if (this._dropdownMenu !== null) {
+ UiSimpleDropdown.close(this._element.id);
+ }
+
+ this._value = value;
+
+ return;
+ }
+
+ this._value = value;
+
+ Ajax.api(this, {
+ parameters: {
+ data: {
+ excludedSearchValues: this._options.excludedSearchValues,
+ searchString: value
+ }
+ }
+ });
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: this._options.ajax
+ };
+ },
+
+ /**
+ * Handles successful Ajax requests.
+ *
+ * @param {object} data response values
+ */
+ _ajaxSuccess: function(data) {
+ if (this._dropdownMenu === null) {
+ this._dropdownMenu = elCreate('div');
+ this._dropdownMenu.className = 'dropdownMenu';
+
+ UiSimpleDropdown.initFragment(this._element, this._dropdownMenu);
+ }
+ else {
+ this._dropdownMenu.innerHTML = '';
+ }
+
+ if (data.returnValues.length) {
+ var anchor, item, listItem;
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ item = data.returnValues[i];
+
+ anchor = elCreate('a');
+ if (item.icon) {
+ anchor.className = 'box16';
+ anchor.innerHTML = item.icon + ' <span></span>';
+ anchor.children[1].textContent = item.label;
+ }
+ else {
+ anchor.textContent = item.label;
+ }
+ elData(anchor, 'object-id', item.objectID);
+ if (item.type) elData(anchor, 'type', item.type);
+ anchor.addEventListener(WCF_CLICK_EVENT, this._select.bind(this));
+
+ listItem = elCreate('li');
+ if (i === 0) listItem.className = 'active';
+ listItem.appendChild(anchor);
+
+ this._dropdownMenu.appendChild(listItem);
+ }
+
+ UiSimpleDropdown.open(this._element.id, true);
+ }
+ else {
+ UiSimpleDropdown.close(this._element.id);
+ }
+ }
+ };
+
+ return UiSuggestion;
+});
+
+/**
+ * Flexible UI element featuring both a list of items and an input field with suggestion support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList
+ */
+define('WoltLabSuite/Core/Ui/ItemList',['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSuite/Core/Ui/Suggestion', 'Ui/SimpleDropdown'], function(Core, Dictionary, Language, DomTraverse, EventKey, UiSuggestion, UiSimpleDropdown) {
+ "use strict";
+
+ var _activeId = '';
+ var _data = new Dictionary();
+ var _didInit = false;
+
+ var _callbackKeyDown = null;
+ var _callbackKeyPress = null;
+ var _callbackKeyUp = null;
+ var _callbackPaste = null;
+ var _callbackRemoveItem = null;
+ var _callbackBlur = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/ItemList
+ */
+ return {
+ /**
+ * Initializes an item list.
+ *
+ * The `values` argument must be empty or contain a list of strings or object, e.g.
+ * `['foo', 'bar']` or `[{ objectId: 1337, value: 'baz'}, {...}]`
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of existing values
+ * @param {Object} options option list
+ */
+ init: function(elementId, values, options) {
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, '" + elementId + "' is invalid.");
+ }
+
+ // remove data from previous instance
+ if (_data.has(elementId)) {
+ var tmp = _data.get(elementId);
+
+ for (var key in tmp) {
+ if (tmp.hasOwnProperty(key)) {
+ var el = tmp[key];
+ if (el instanceof Element && el.parentNode) {
+ elRemove(el);
+ }
+ }
+ }
+
+ UiSimpleDropdown.destroy(elementId);
+ _data.delete(elementId);
+ }
+
+ options = Core.extend({
+ // search parameters for suggestions
+ ajax: {
+ actionName: 'getSearchResultList',
+ className: '',
+ data: {}
+ },
+
+ // list of excluded string values, e.g. `['ignore', 'these strings', 'when', 'searching']`
+ excludedSearchValues: [],
+ // maximum number of items this list may contain, `-1` for infinite
+ maxItems: -1,
+ // maximum length of an item value, `-1` for infinite
+ maxLength: -1,
+ // disallow custom values, only values offered by the suggestion dropdown are accepted
+ restricted: false,
+
+ // initial value will be interpreted as comma separated value and submitted as such
+ isCSV: false,
+
+ // will be invoked whenever the items change, receives the element id first and list of values second
+ callbackChange: null,
+ // callback once the form is about to be submitted
+ callbackSubmit: null,
+ // Callback for the custom shadow synchronization.
+ callbackSyncShadow: null,
+ // Callback to set values during the setup.
+ callbackSetupValues: null,
+ // value may contain the placeholder `{$objectId}`
+ submitFieldName: ''
+ }, options);
+
+ var form = DomTraverse.parentByTag(element, 'FORM');
+ if (form !== null) {
+ if (options.isCSV === false) {
+ if (!options.submitFieldName.length && typeof options.callbackSubmit !== 'function') {
+ throw new Error("Expected a valid function for option 'callbackSubmit', a non-empty value for option 'submitFieldName' or enabling the option 'submitFieldCSV'.");
+ }
+
+ form.addEventListener('submit', (function() {
+ var values = this.getValues(elementId);
+ if (options.submitFieldName.length) {
+ var input;
+ for (var i = 0, length = values.length; i < length; i++) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = options.submitFieldName.replace(/{$objectId}/, values[i].objectId);
+ input.value = values[i].value;
+
+ form.appendChild(input);
+ }
+ }
+ else {
+ options.callbackSubmit(form, values);
+ }
+ }).bind(this));
+ }
+ }
+
+ this._setup();
+
+ var data = this._createUI(element, options);
+ //noinspection JSUnresolvedVariable
+ var suggestion = new UiSuggestion(elementId, {
+ ajax: options.ajax,
+ callbackSelect: this._addItem.bind(this),
+ excludedSearchValues: options.excludedSearchValues
+ });
+
+ _data.set(elementId, {
+ dropdownMenu: null,
+ element: data.element,
+ list: data.list,
+ listItem: data.element.parentNode,
+ options: options,
+ shadow: data.shadow,
+ suggestion: suggestion
+ });
+
+ if (options.callbackSetupValues) {
+ values = options.callbackSetupValues();
+ }
+ else {
+ values = (data.values.length) ? data.values : values;
+ }
+
+ if (Array.isArray(values)) {
+ var value;
+ for (var i = 0, length = values.length; i < length; i++) {
+ value = values[i];
+ if (typeof value === 'string') {
+ value = { objectId: 0, value: value };
+ }
+
+ this._addItem(elementId, value);
+ }
+ }
+ },
+
+ /**
+ * Returns the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @return {Array} list of objects containing object id and value
+ */
+ getValues: function(elementId) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+ var values = [];
+ elBySelAll('.item > span', data.list, function(span) {
+ values.push({
+ objectId: ~~elData(span, 'object-id'),
+ value: span.textContent,
+ type: elData(span, 'type')
+ });
+ });
+
+ return values;
+ },
+
+ /**
+ * Sets the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of objects containing object id and value
+ */
+ setValues: function(elementId, values) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+
+ // remove all existing items first
+ var i, length;
+ var items = DomTraverse.childrenByClass(data.list, 'item');
+ for (i = 0, length = items.length; i < length; i++) {
+ this._removeItem(null, items[i], true);
+ }
+
+ // add new items
+ for (i = 0, length = values.length; i < length; i++) {
+ this._addItem(elementId, values[i]);
+ }
+ },
+
+ /**
+ * Binds static event listeners.
+ */
+ _setup: function() {
+ if (_didInit) {
+ return;
+ }
+
+ _didInit = true;
+
+ _callbackKeyDown = this._keyDown.bind(this);
+ _callbackKeyPress = this._keyPress.bind(this);
+ _callbackKeyUp = this._keyUp.bind(this);
+ _callbackPaste = this._paste.bind(this);
+ _callbackRemoveItem = this._removeItem.bind(this);
+ _callbackBlur = this._blur.bind(this);
+ },
+
+ /**
+ * Creates the DOM structure for target element. If `element` is a `<textarea>`
+ * it will be automatically replaced with an `<input>` element.
+ *
+ * @param {Element} element input element
+ * @param {Object} options option list
+ */
+ _createUI: function(element, options) {
+ var list = elCreate('ol');
+ list.className = 'inputItemList' + (element.disabled ? ' disabled' : '');
+ elData(list, 'element-id', element.id);
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ if (event.target === list) {
+ //noinspection JSUnresolvedFunction
+ element.focus();
+ }
+ });
+
+ var listItem = elCreate('li');
+ listItem.className = 'input';
+ list.appendChild(listItem);
+
+ element.addEventListener('keydown', _callbackKeyDown);
+ element.addEventListener('keypress', _callbackKeyPress);
+ element.addEventListener('keyup', _callbackKeyUp);
+ element.addEventListener('paste', _callbackPaste);
+ var hasFocus = element === document.activeElement;
+ if (hasFocus) {
+ //noinspection JSUnresolvedFunction
+ element.blur();
+ }
+ element.addEventListener('blur', _callbackBlur);
+ element.parentNode.insertBefore(list, element);
+ listItem.appendChild(element);
+ if (hasFocus) {
+ window.setTimeout(function() {
+ //noinspection JSUnresolvedFunction
+ element.focus();
+ }, 1);
+ }
+
+ if (options.maxLength !== -1) {
+ elAttr(element, 'maxLength', options.maxLength);
+ }
+
+ var shadow = null, values = [];
+ if (options.isCSV) {
+ shadow = elCreate('input');
+ shadow.className = 'itemListInputShadow';
+ shadow.type = 'hidden';
+ //noinspection JSUnresolvedVariable
+ shadow.name = element.name;
+ element.removeAttribute('name');
+
+ list.parentNode.insertBefore(shadow, list);
+
+ //noinspection JSUnresolvedVariable
+ var value, tmp = element.value.split(',');
+ for (var i = 0, length = tmp.length; i < length; i++) {
+ value = tmp[i].trim();
+ if (value.length) {
+ values.push(value);
+ }
+ }
+
+ if (element.nodeName === 'TEXTAREA') {
+ var inputElement = elCreate('input');
+ inputElement.type = 'text';
+ element.parentNode.insertBefore(inputElement, element);
+ inputElement.id = element.id;
+
+ elRemove(element);
+ element = inputElement;
+ }
+ }
+
+ return {
+ element: element,
+ list: list,
+ shadow: shadow,
+ values: values
+ };
+ },
+
+ /**
+ * Returns true if the input accepts new items.
+ *
+ * @param {string} elementId input element id
+ * @return {boolean} true if at least one more item can be added
+ * @protected
+ */
+ _acceptsNewItems: function (elementId) {
+ var data = _data.get(elementId);
+ if (data.options.maxItems === -1) {
+ return true;
+ }
+
+ return (data.list.childElementCount - 1 < data.options.maxItems);
+ },
+
+ /**
+ * Enforces the maximum number of items.
+ *
+ * @param {string} elementId input element id
+ */
+ _handleLimit: function(elementId) {
+ var data = _data.get(elementId);
+ if (this._acceptsNewItems(elementId)) {
+ if (data.element.disabled) {
+ data.element.disabled = false;
+ data.element.removeAttribute('placeholder');
+ }
+ }
+ else if (!data.element.disabled) {
+ data.element.disabled = true;
+ elAttr(data.element, 'placeholder', Language.get('wcf.global.form.input.maxItems'));
+ }
+ },
+
+ /**
+ * Sets the active item list id and handles keyboard access to remove an existing item.
+ *
+ * @param {object} event event object
+ */
+ _keyDown: function(event) {
+ var input = event.currentTarget;
+ var lastItem = input.parentNode.previousElementSibling;
+
+ _activeId = input.id;
+
+ if (event.keyCode === 8) {
+ // 8 = [BACKSPACE]
+ if (input.value.length === 0) {
+ if (lastItem !== null) {
+ if (lastItem.classList.contains('active')) {
+ this._removeItem(null, lastItem);
+ }
+ else {
+ lastItem.classList.add('active');
+ }
+ }
+ }
+ }
+ else if (event.keyCode === 27) {
+ // 27 = [ESC]
+ if (lastItem !== null && lastItem.classList.contains('active')) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` and `[,]` key to add an item to the list unless it is restricted.
+ *
+ * @param {Event} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event) || EventKey.Comma(event)) {
+ event.preventDefault();
+
+ if (_data.get(event.currentTarget.id).options.restricted) {
+ // restricted item lists only allow results from the dropdown to be picked
+ return;
+ }
+
+ var value = event.currentTarget.value.trim();
+ if (value.length) {
+ this._addItem(event.currentTarget.id, { objectId: 0, value: value });
+ }
+ }
+ },
+
+ /**
+ * Splits comma-separated values being pasted into the input field.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _paste: function (event) {
+ var text = '';
+ if (typeof window.clipboardData === 'object') {
+ // IE11
+ text = window.clipboardData.getData('Text');
+ }
+ else {
+ text = event.clipboardData.getData('text/plain');
+ }
+
+ var element = event.currentTarget;
+ var elementId = element.id;
+ var maxLength = ~~elAttr(element, 'maxLength');
+
+ text.split(/,/).forEach((function(item) {
+ item = item.trim();
+ if (maxLength && item.length > maxLength) {
+ // truncating items provides a better UX than throwing an error or silently discarding it
+ item = item.substr(0, maxLength);
+ }
+
+ if (item.length > 0 && this._acceptsNewItems(elementId)) {
+ this._addItem(elementId, {objectId: 0, value: item});
+ }
+ }).bind(this));
+
+ event.preventDefault();
+ },
+
+ /**
+ * Handles the keyup event to unmark an item for deletion.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ var input = event.currentTarget;
+
+ if (input.value.length > 0) {
+ var lastItem = input.parentNode.previousElementSibling;
+ if (lastItem !== null) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Adds an item to the list.
+ *
+ * @param {string} elementId input element id
+ * @param {object} value item value
+ */
+ _addItem: function(elementId, value) {
+ var data = _data.get(elementId);
+
+ var listItem = elCreate('li');
+ listItem.className = 'item';
+
+ var content = elCreate('span');
+ content.className = 'content';
+ elData(content, 'object-id', value.objectId);
+ if (value.type) elData(content, 'type', value.type);
+ content.textContent = value.value;
+ listItem.appendChild(content);
+
+ if (!data.element.disabled) {
+ var button = elCreate('a');
+ button.className = 'icon icon16 fa-times';
+ button.addEventListener(WCF_CLICK_EVENT, _callbackRemoveItem);
+ listItem.appendChild(button);
+ }
+
+ data.list.insertBefore(listItem, data.listItem);
+ data.suggestion.addExcludedValue(value.value);
+ data.element.value = '';
+
+ if (!data.element.disabled) {
+ this._handleLimit(elementId);
+ }
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Removes an item from the list.
+ *
+ * @param {?object} event event object
+ * @param {Element?} item list item
+ * @param {boolean?} noFocus input element will not be focused if true
+ */
+ _removeItem: function(event, item, noFocus) {
+ item = (event === null) ? item : event.currentTarget.parentNode;
+
+ var parent = item.parentNode;
+ //noinspection JSCheckFunctionSignatures
+ var elementId = elData(parent, 'element-id');
+ var data = _data.get(elementId);
+
+ data.suggestion.removeExcludedValue(item.children[0].textContent);
+ parent.removeChild(item);
+ if (!noFocus) data.element.focus();
+
+ this._handleLimit(elementId);
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Synchronizes the shadow input field with the current list item values.
+ *
+ * @param {object} data element data
+ */
+ _syncShadow: function(data) {
+ if (!data.options.isCSV) return null;
+ if (typeof data.options.callbackSyncShadow === 'function') {
+ return data.options.callbackSyncShadow(data);
+ }
+
+ var value = '', values = this.getValues(data.element.id);
+ for (var i = 0, length = values.length; i < length; i++) {
+ value += (value.length ? ',' : '') + values[i].value;
+ }
+
+ data.shadow.value = value;
+
+ return values;
+ },
+
+ /**
+ * Handles the blur event.
+ *
+ * @param {object} event event object
+ */
+ _blur: function(event) {
+ var input = event.currentTarget;
+ var data = _data.get(input.id);
+ if (data.options.restricted) {
+ // restricted item lists only allow results from the dropdown to be picked
+ return;
+ }
+
+ var value = input.value.trim();
+ if (value.length) {
+ if (!data.suggestion || !data.suggestion.isActive()) {
+ this._addItem(input.id, { objectId: 0, value: value });
+ }
+ }
+ }
+ };
+});
+
+/**
+ * Utility class to provide a 'Jump To' overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/JumpTo
+ */
+define('WoltLabSuite/Core/Ui/Page/JumpTo',['Language', 'ObjectMap', 'Ui/Dialog'], function(Language, ObjectMap, UiDialog) {
+ "use strict";
+
+ var _activeElement = null;
+ var _buttonSubmit = null;
+ var _description = null;
+ var _elements = new ObjectMap();
+ var _input = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/JumpTo
+ */
+ var UiPageJumpTo = {
+ /**
+ * Initializes a 'Jump To' element.
+ *
+ * @param {Element} element trigger element
+ * @param {function} callback callback function, receives the page number as first argument
+ */
+ init: function(element, callback) {
+ callback = callback || null;
+ if (callback === null) {
+ var redirectUrl = elData(element, 'link');
+ if (redirectUrl) {
+ callback = function(pageNo) {
+ window.location = redirectUrl.replace(/pageNo=%d/, 'pageNo=' + pageNo);
+ };
+ }
+ else {
+ callback = function() {};
+ }
+
+ }
+ else if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid function for parameter 'callback'.");
+ }
+
+ if (!_elements.has(element)) {
+ elBySelAll('.jumpTo', element, (function(jumpTo) {
+ jumpTo.addEventListener(WCF_CLICK_EVENT, this._click.bind(this, element));
+ _elements.set(element, { callback: callback });
+ }).bind(this));
+ }
+ },
+
+ /**
+ * Handles clicks on the trigger element.
+ *
+ * @param {Element} element trigger element
+ * @param {object} event event object
+ */
+ _click: function(element, event) {
+ _activeElement = element;
+
+ if (typeof event === 'object') {
+ event.preventDefault();
+ }
+
+ UiDialog.open(this);
+
+ var pages = elData(element, 'pages');
+ _input.value = pages;
+ _input.setAttribute('max', pages);
+ _input.select();
+
+ _description.textContent = Language.get('wcf.page.jumpTo.description').replace(/#pages#/, pages);
+ },
+
+ /**
+ * Handles changes to the page number input field.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ if (event.which === 13 && _buttonSubmit.disabled === false) {
+ this._submit();
+ return;
+ }
+
+ var pageNo = ~~_input.value;
+ if (pageNo < 1 || pageNo > ~~elAttr(_input, 'max')) {
+ _buttonSubmit.disabled = true;
+ }
+ else {
+ _buttonSubmit.disabled = false;
+ }
+ },
+
+ /**
+ * Invokes the callback with the chosen page number as first argument.
+ *
+ * @param {object} event event object
+ */
+ _submit: function(event) {
+ _elements.get(_activeElement).callback(~~_input.value);
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var source = '<dl>'
+ + '<dt><label for="jsPaginationPageNo">' + Language.get('wcf.page.jumpTo') + '</label></dt>'
+ + '<dd>'
+ + '<input type="number" id="jsPaginationPageNo" value="1" min="1" max="1" class="tiny">'
+ + '<small></small>'
+ + '</dd>'
+ + '</dl>'
+ + '<div class="formSubmit">'
+ + '<button class="buttonPrimary">' + Language.get('wcf.global.button.submit') + '</button>'
+ + '</div>';
+
+ return {
+ id: 'paginationOverlay',
+ options: {
+ onSetup: (function(content) {
+ _input = elByTag('input', content)[0];
+ _input.addEventListener('keyup', this._keyUp.bind(this));
+
+ _description = elByTag('small', content)[0];
+
+ _buttonSubmit = elByTag('button', content)[0];
+ _buttonSubmit.addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ }).bind(this),
+ title: Language.get('wcf.global.page.pagination')
+ },
+ source: source
+ };
+ }
+ };
+
+ return UiPageJumpTo;
+});
+/**
+ * Callback-based pagination.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Pagination
+ */
+define('WoltLabSuite/Core/Ui/Pagination',['Core', 'Language', 'ObjectMap', 'StringUtil', 'WoltLabSuite/Core/Ui/Page/JumpTo'], function(Core, Language, ObjectMap, StringUtil, UiPageJumpTo) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiPagination(element, options) { this.init(element, options); }
+ UiPagination.prototype = {
+ /**
+ * maximum number of displayed page links, should match the PHP implementation
+ * @var {int}
+ */
+ SHOW_LINKS: 11,
+
+ /**
+ * Initializes the pagination.
+ *
+ * @param {Element} element container element
+ * @param {object} options list of initialization options
+ */
+ init: function(element, options) {
+ this._element = element;
+ this._options = Core.extend({
+ activePage: 1,
+ maxPage: 1,
+
+ callbackShouldSwitch: null,
+ callbackSwitch: null
+ }, options);
+
+ if (typeof this._options.callbackShouldSwitch !== 'function') this._options.callbackShouldSwitch = null;
+ if (typeof this._options.callbackSwitch !== 'function') this._options.callbackSwitch = null;
+
+ this._element.classList.add('pagination');
+
+ this._rebuild(this._element);
+ },
+
+ /**
+ * Rebuilds the entire pagination UI.
+ */
+ _rebuild: function() {
+ var hasHiddenPages = false;
+
+ // clear content
+ this._element.innerHTML = '';
+
+ var list = elCreate('ul'), link;
+
+ var listItem = elCreate('li');
+ listItem.className = 'skip';
+ list.appendChild(listItem);
+
+ var iconClassNames = 'icon icon24 fa-chevron-left';
+ if (this._options.activePage > 1) {
+ link = elCreate('a');
+ link.className = iconClassNames + ' jsTooltip';
+ link.href = '#';
+ link.title = Language.get('wcf.global.page.previous');
+ link.rel = 'prev';
+ listItem.appendChild(link);
+
+ link.addEventListener(WCF_CLICK_EVENT, this.switchPage.bind(this, this._options.activePage - 1));
+ }
+ else {
+ listItem.innerHTML = '<span class="' + iconClassNames + '"></span>';
+ listItem.classList.add('disabled');
+ }
+
+ // add first page
+ list.appendChild(this._createLink(1));
+
+ // calculate page links
+ var maxLinks = this.SHOW_LINKS - 4;
+ var linksBefore = this._options.activePage - 2;
+ if (linksBefore < 0) linksBefore = 0;
+ var linksAfter = this._options.maxPage - (this._options.activePage + 1);
+ if (linksAfter < 0) linksAfter = 0;
+ if (this._options.activePage > 1 && this._options.activePage < this._options.maxPage) maxLinks--;
+
+ var half = maxLinks / 2;
+ var left = this._options.activePage;
+ var right = this._options.activePage;
+ if (left < 1) left = 1;
+ if (right < 1) right = 1;
+ if (right > this._options.maxPage - 1) right = this._options.maxPage - 1;
+
+ if (linksBefore >= half) {
+ left -= half;
+ }
+ else {
+ left -= linksBefore;
+ right += half - linksBefore;
+ }
+
+ if (linksAfter >= half) {
+ right += half;
+ }
+ else {
+ right += linksAfter;
+ left -= half - linksAfter;
+ }
+
+ right = Math.ceil(right);
+ left = Math.ceil(left);
+ if (left < 1) left = 1;
+ if (right > this._options.maxPage) right = this._options.maxPage;
+
+ // left ... links
+ var jumpToHtml = '<a class="jsTooltip" title="' + Language.get('wcf.page.jumpTo') + '">…</a>';
+ if (left > 1) {
+ if (left - 1 < 2) {
+ list.appendChild(this._createLink(2));
+ }
+ else {
+ listItem = elCreate('li');
+ listItem.className = 'jumpTo';
+ listItem.innerHTML = jumpToHtml;
+ list.appendChild(listItem);
+
+ hasHiddenPages = true;
+ }
+ }
+
+ // visible links
+ for (var i = left + 1; i < right; i++) {
+ list.appendChild(this._createLink(i));
+ }
+
+ // right ... links
+ if (right < this._options.maxPage) {
+ if (this._options.maxPage - right < 2) {
+ list.appendChild(this._createLink(this._options.maxPage - 1));
+ }
+ else {
+ listItem = elCreate('li');
+ listItem.className = 'jumpTo';
+ listItem.innerHTML = jumpToHtml;
+ list.appendChild(listItem);
+
+ hasHiddenPages = true;
+ }
+ }
+
+ // add last page
+ list.appendChild(this._createLink(this._options.maxPage));
+
+ // add next button
+ listItem = elCreate('li');
+ listItem.className = 'skip';
+ list.appendChild(listItem);
+
+ iconClassNames = 'icon icon24 fa-chevron-right';
+ if (this._options.activePage < this._options.maxPage) {
+ link = elCreate('a');
+ link.className = iconClassNames + ' jsTooltip';
+ link.href = '#';
+ link.title = Language.get('wcf.global.page.next');
+ link.rel = 'next';
+ listItem.appendChild(link);
+
+ link.addEventListener(WCF_CLICK_EVENT, this.switchPage.bind(this, this._options.activePage + 1));
+ }
+ else {
+ listItem.innerHTML = '<span class="' + iconClassNames + '"></span>';
+ listItem.classList.add('disabled');
+ }
+
+ if (hasHiddenPages) {
+ elData(list, 'pages', this._options.maxPage);
+
+ UiPageJumpTo.init(list, this.switchPage.bind(this));
+ }
+
+ this._element.appendChild(list);
+ },
+
+ /**
+ * Creates a link to a specific page.
+ *
+ * @param {int} pageNo page number
+ * @return {Element} link element
+ */
+ _createLink: function(pageNo) {
+ var listItem = elCreate('li');
+ if (pageNo !== this._options.activePage) {
+ var link = elCreate('a');
+ link.textContent = StringUtil.addThousandsSeparator(pageNo);
+ link.addEventListener(WCF_CLICK_EVENT, this.switchPage.bind(this, pageNo));
+ listItem.appendChild(link);
+ }
+ else {
+ listItem.classList.add('active');
+ listItem.innerHTML = '<span>' + StringUtil.addThousandsSeparator(pageNo) + '</span><span class="invisible">' + Language.get('wcf.page.pagePosition', { pageNo: pageNo, pages: this._options.maxPage }) + '</span>';
+ }
+
+ return listItem;
+ },
+
+ /**
+ * Returns the active page.
+ *
+ * @return {integer}
+ */
+ getActivePage: function() {
+ return this._options.activePage;
+ },
+
+ /**
+ * Returns the pagination Ui element.
+ *
+ * @return {HTMLElement}
+ */
+ getElement: function() {
+ return this._element;
+ },
+
+ /**
+ * Returns the maximum page.
+ *
+ * @return {integer}
+ */
+ getMaxPage: function() {
+ return this._options.maxPage;
+ },
+
+ /**
+ * Switches to given page number.
+ *
+ * @param {int} pageNo page number
+ * @param {object} event event object
+ */
+ switchPage: function(pageNo, event) {
+ if (typeof event === 'object') {
+ event.preventDefault();
+
+ // force tooltip to vanish and strip positioning
+ if (event.currentTarget && elData(event.currentTarget, 'tooltip')) {
+ var tooltip = elById('balloonTooltip');
+ if (tooltip) {
+ Core.triggerEvent(event.currentTarget, 'mouseleave');
+ tooltip.style.removeProperty('top');
+ tooltip.style.removeProperty('bottom');
+ }
+ }
+ }
+
+ pageNo = ~~pageNo;
+
+ if (pageNo > 0 && this._options.activePage !== pageNo && pageNo <= this._options.maxPage) {
+ if (this._options.callbackShouldSwitch !== null) {
+ if (this._options.callbackShouldSwitch(pageNo) !== true) {
+ return;
+ }
+ }
+
+ this._options.activePage = pageNo;
+ this._rebuild();
+
+ if (this._options.callbackSwitch !== null) {
+ this._options.callbackSwitch(pageNo);
+ }
+ }
+ }
+ };
+
+ return UiPagination;
+});
+
+/**
+ * Smoothly scrolls to an element while accounting for potential sticky headers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Scroll
+ */
+define('WoltLabSuite/Core/Ui/Scroll',['Dom/Util'], function(DomUtil) {
+ "use strict";
+
+ var _callback = null;
+ var _callbackScroll = null;
+ var _offset = null;
+ var _timeoutScroll = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Scroll
+ */
+ return {
+ /**
+ * Scrolls to target element, optionally invoking the provided callback once scrolling has ended.
+ *
+ * @param {Element} element target element
+ * @param {function=} callback callback invoked once scrolling has ended
+ */
+ element: function(element, callback) {
+ if (!(element instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element.");
+ }
+ else if (callback !== undefined && typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback function.");
+ }
+ else if (!document.body.contains(element)) {
+ throw new Error("Element must be part of the visible DOM.");
+ }
+ else if (_callback !== null) {
+ throw new Error("Cannot scroll to element, a concurrent request is running.");
+ }
+
+ if (callback) {
+ _callback = callback;
+
+ if (_callbackScroll === null) {
+ _callbackScroll = this._onScroll.bind(this);
+ }
+
+ window.addEventListener('scroll', _callbackScroll);
+ }
+
+ var y = DomUtil.offset(element).top;
+ if (_offset === null) {
+ _offset = 50;
+ var pageHeader = elById('pageHeaderPanel');
+ if (pageHeader !== null) {
+ var position = window.getComputedStyle(pageHeader).position;
+ if (position === 'fixed' || position === 'static') {
+ _offset = pageHeader.offsetHeight;
+ }
+ else {
+ _offset = 0;
+ }
+ }
+ }
+
+ if (_offset > 0) {
+ if (y <= _offset) {
+ y = 0;
+ }
+ else {
+ // add an offset to account for a sticky header
+ y -= _offset;
+ }
+ }
+
+ var offset = window.pageYOffset;
+
+ window.scrollTo({
+ left: 0,
+ top: y,
+ behavior: 'smooth'
+ });
+
+ window.setTimeout((function () {
+ // no scrolling took place
+ if (offset === window.pageYOffset) {
+ this._onScroll();
+ }
+ }).bind(this), 100);
+ },
+
+ /**
+ * Monitors scroll event to only execute the callback once scrolling has ended.
+ *
+ * @protected
+ */
+ _onScroll: function() {
+ if (_timeoutScroll !== null) window.clearTimeout(_timeoutScroll);
+
+ _timeoutScroll = window.setTimeout(function() {
+ if (_callback !== null) _callback();
+
+ window.removeEventListener('scroll', _callbackScroll);
+ _callback = null;
+ _timeoutScroll = null;
+ }, 100);
+ }
+ };
+});
+
+/**
+ * Initializes modules required for media list view.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Media/List
+ */
+define('WoltLabSuite/Core/Controller/Media/List',[
+ 'Dom/ChangeListener',
+ 'EventHandler',
+ 'WoltLabSuite/Core/Controller/Clipboard',
+ 'WoltLabSuite/Core/Media/Clipboard',
+ 'WoltLabSuite/Core/Media/Editor',
+ 'WoltLabSuite/Core/Media/List/Upload'
+ ],
+ function(
+ DomChangeListener,
+ EventHandler,
+ Clipboard,
+ MediaClipboard,
+ MediaEditor,
+ MediaListUpload
+ ) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _addButtonEventListeners: function() {},
+ _deleteCallback: function() {},
+ _deleteMedia: function(mediaIds) {},
+ _edit: function() {}
+ };
+ return Fake;
+ }
+
+ var _mediaEditor;
+ var _tableBody = elById('mediaListTableBody');
+ var _clipboardObjectIds = [];
+ var _upload;
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Media/List
+ */
+ return {
+ init: function(options) {
+ options = options || {};
+ _upload = new MediaListUpload('uploadButton', 'mediaListTableBody', {
+ categoryId: options.categoryId,
+ multiple: true
+ });
+
+ MediaClipboard.init(
+ 'wcf\\acp\\page\\MediaListPage',
+ options.hasMarkedItems || false,
+ this
+ );
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'removedErroneousUploadRow', this._deleteCallback.bind(this));
+
+ var deleteAction = new WCF.Action.Delete('wcf\\data\\media\\MediaAction', '.jsMediaRow');
+ deleteAction.setCallback(this._deleteCallback);
+
+ _mediaEditor = new MediaEditor({
+ _editorSuccess: function(media, oldCategoryId) {
+ if (media.categoryID != oldCategoryId) {
+ window.setTimeout(function() {
+ window.location.reload();
+ }, 500);
+ }
+ }
+ });
+
+ this._addButtonEventListeners();
+
+ DomChangeListener.add('WoltLabSuite/Core/Controller/Media/List', this._addButtonEventListeners.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._openEditorAfterUpload.bind(this));
+ },
+
+ /**
+ * Adds the `click` event listeners to the media edit icons in
+ * new media table rows.
+ */
+ _addButtonEventListeners: function() {
+ var buttons = elByClass('jsMediaEditButton', _tableBody), button;
+ while (buttons.length) {
+ button = buttons[0];
+ button.classList.remove('jsMediaEditButton');
+ button.addEventListener(WCF_CLICK_EVENT, this._edit.bind(this));
+ }
+ },
+
+ /**
+ * Is triggered after media files have been deleted using the delete icon.
+ *
+ * @param {int[]?} objectIds
+ */
+ _deleteCallback: function(objectIds) {
+ var tableRowCount = elByTag('tr', _tableBody).length;
+ if (objectIds.length === undefined) {
+ if (!tableRowCount) {
+ window.location.reload();
+ }
+ }
+ else if (objectIds.length === tableRowCount) {
+ // table is empty, reload page
+ window.location.reload();
+ }
+ else {
+ Clipboard.reload.bind(Clipboard)
+ }
+ },
+
+ /**
+ * Is called when a media edit icon is clicked.
+ *
+ * @param {Event} event
+ */
+ _edit: function(event) {
+ _mediaEditor.edit(elData(event.currentTarget, 'object-id'));
+ },
+
+ /**
+ * Opens the media editor after uploading a single file.
+ *
+ * @param {object} data upload event data
+ * @since 5.2
+ */
+ _openEditorAfterUpload: function(data) {
+ if (data.upload === _upload && !data.isMultiFileUpload && !_upload.hasPendingUploads()) {
+ var keys = Object.keys(data.media);
+
+ if (keys.length) {
+ _mediaEditor.edit(this._media.get(~~data.media[keys[0]].mediaID));
+ }
+ }
+ },
+
+ /**
+ * Is called after the media files with the given ids have been deleted via clipboard.
+ *
+ * @param {int[]} mediaIds ids of deleted media files
+ */
+ clipboardDeleteMedia: function(mediaIds) {
+ var mediaRows = elByClass('jsMediaRow');
+ for (var i = 0; i < mediaRows.length; i++) {
+ var media = mediaRows[i];
+ var mediaID = ~~elData(elByClass('jsClipboardItem', media)[0], 'object-id');
+
+ if (mediaIds.indexOf(mediaID) !== -1) {
+ elRemove(media);
+ i--;
+ }
+ }
+
+ if (!mediaRows.length) {
+ window.location.reload();
+ }
+ }
+ }
+});
+/**
+ * Handles dismissible user notices.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Notice/Dismiss
+ */
+define('WoltLabSuite/Core/Controller/Notice/Dismiss',['Ajax'], function(Ajax) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/Notice/Dismiss
+ */
+ var ControllerNoticeDismiss = {
+ /**
+ * Initializes dismiss buttons.
+ */
+ setup: function() {
+ var buttons = elByClass('jsDismissNoticeButton');
+
+ if (buttons.length) {
+ var clickCallback = this._click.bind(this);
+ for (var i = 0, length = buttons.length; i < length; i++) {
+ buttons[i].addEventListener(WCF_CLICK_EVENT, clickCallback);
+ }
+ }
+ },
+
+ /**
+ * Sends a request to dismiss a notice and removes it afterwards.
+ */
+ _click: function(event) {
+ var button = event.currentTarget;
+
+ Ajax.apiOnce({
+ data: {
+ actionName: 'dismiss',
+ className: 'wcf\\data\\notice\\NoticeAction',
+ objectIDs: [ elData(button, 'object-id') ]
+ },
+ success: function() {
+ elRemove(button.parentNode);
+ }
+ });
+ }
+ };
+
+ return ControllerNoticeDismiss;
+});
+
+/**
+ * Manages form field dependencies.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager',['Dictionary', 'Dom/ChangeListener', 'EventHandler', 'List', 'Dom/Traverse', 'Dom/Util', 'ObjectMap'], function(Dictionary, DomChangeListener, EventHandler, List, DomTraverse, DomUtil, ObjectMap) {
+ "use strict";
+
+ /**
+ * is `true` if containters are currently checked for their availablility, otherwise `false`
+ * @type {boolean}
+ * @private
+ */
+ var _checkingContainers = false;
+
+ /**
+ * is `true` if containter will be checked again after the current check for their availablility
+ * has finished, otherwise `false`
+ * @type {boolean}
+ * @private
+ */
+ var _checkContainersAgain = true;
+
+ /**
+ * list of containers hidden due to their own dependencies
+ * @type {List}
+ * @private
+ */
+ var _dependencyHiddenNodes = new List();
+
+ /**
+ * list of fields for which event listeners have been registered
+ * @type {Dictionary}
+ * @private
+ */
+ var _fields = new Dictionary();
+
+ /**
+ * list of registered forms
+ * @type {List}
+ * @private
+ */
+ var _forms = new List();
+
+ /**
+ * list of dependencies grouped by the dependent node they belong to
+ * @type {Dictionary}
+ * @private
+ */
+ var _nodeDependencies = new Dictionary();
+
+ /**
+ * cache of validation-related properties of hidden form fields
+ * @type {ObjectMap}
+ * @private
+ */
+ var _validatedFieldProperties = new ObjectMap();
+
+ return {
+ /**
+ * Hides the given node because of its own dependencies.
+ *
+ * @param {HTMLElement} node hidden node
+ * @protected
+ */
+ _hide: function(node) {
+ elHide(node);
+ _dependencyHiddenNodes.add(node);
+
+ // also hide tab menu entry
+ if (node.classList.contains('tabMenuContent')) {
+ elBySelAll('li', DomTraverse.prevByClass(node, 'tabMenu'), function(tabLink) {
+ if (elData(tabLink, 'name') === elData(node, 'name')) {
+ elHide(tabLink);
+ }
+ });
+ }
+
+ elBySelAll('[max], [maxlength], [min], [required]', node, function(validatedField) {
+ var properties = new Dictionary();
+
+ var max = elAttr(validatedField, 'max');
+ if (max) {
+ properties.set('max', max);
+ validatedField.removeAttribute('max');
+ }
+
+ var maxlength = elAttr(validatedField, 'maxlength');
+ if (maxlength) {
+ properties.set('maxlength', maxlength);
+ validatedField.removeAttribute('maxlength');
+ }
+
+ var min = elAttr(validatedField, 'min');
+ if (min) {
+ properties.set('min', min);
+ validatedField.removeAttribute('min');
+ }
+
+ if (validatedField.required) {
+ properties.set('required', true);
+ validatedField.removeAttribute('required');
+ }
+
+ _validatedFieldProperties.set(validatedField, properties);
+ });
+ },
+
+ /**
+ * Shows the given node because of its own dependencies.
+ *
+ * @param {HTMLElement} node shown node
+ * @protected
+ */
+ _show: function(node) {
+ elShow(node);
+ _dependencyHiddenNodes.delete(node);
+
+ // also show tab menu entry
+ if (node.classList.contains('tabMenuContent')) {
+ elBySelAll('li', DomTraverse.prevByClass(node, 'tabMenu'), function(tabLink) {
+ if (elData(tabLink, 'name') === elData(node, 'name')) {
+ elShow(tabLink);
+ }
+ });
+ }
+
+ elBySelAll('input, select', node, function(validatedField) {
+ // if a container is shown, ignore all fields that
+ // have a hidden parent element within the container
+ var parentNode = validatedField.parentNode;
+ while (parentNode !== node && parentNode.style.getPropertyValue('display') !== 'none') {
+ parentNode = parentNode.parentNode;
+ }
+
+ if (parentNode === node && _validatedFieldProperties.has(validatedField)) {
+ var properties = _validatedFieldProperties.get(validatedField);
+
+ if (properties.has('max')) {
+ elAttr(validatedField, 'max', properties.get('max'));
+ }
+ if (properties.has('maxlength')) {
+ elAttr(validatedField, 'maxlength', properties.get('maxlength'));
+ }
+ if (properties.has('min')) {
+ elAttr(validatedField, 'min', properties.get('min'));
+ }
+ if (properties.has('required')) {
+ elAttr(validatedField, 'required', '');
+ }
+
+ _validatedFieldProperties.delete(validatedField);
+ }
+ });
+ },
+
+ /**
+ * Registers a new form field dependency.
+ *
+ * @param {WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract} dependency new dependency
+ */
+ addDependency: function(dependency) {
+ var dependentNode = dependency.getDependentNode();
+ if (!_nodeDependencies.has(dependentNode.id)) {
+ _nodeDependencies.set(dependentNode.id, [dependency]);
+ }
+ else {
+ _nodeDependencies.get(dependentNode.id).push(dependency);
+ }
+
+ var fields = dependency.getFields();
+ for (var i = 0, length = fields.length; i < length; i++) {
+ var field = fields[i];
+ var id = DomUtil.identify(field);
+
+ if (!_fields.has(id)) {
+ _fields.set(id, field);
+
+ if (field.tagName === 'INPUT' && (field.type === 'checkbox' || field.type === 'radio')) {
+ field.addEventListener('change', this.checkDependencies.bind(this));
+ }
+ else {
+ field.addEventListener('input', this.checkDependencies.bind(this));
+ }
+ }
+ }
+ },
+
+ /**
+ * Checks if all dependencies are met.
+ */
+ checkDependencies: function() {
+ var obsoleteNodes = [];
+
+ _nodeDependencies.forEach(function(nodeDependencies, nodeId) {
+ var dependentNode = elById(nodeId);
+
+ // check if dependent node still exists
+ if (dependentNode === null) {
+ obsoleteNodes.push(dependentNode);
+ return;
+ }
+
+ for (var i = 0, length = nodeDependencies.length; i < length; i++) {
+ // if any dependency is not met, hide the element
+ if (!nodeDependencies[i].checkDependency()) {
+ this._hide(dependentNode);
+ return;
+ }
+ }
+
+ // all node dependency is met
+ this._show(dependentNode);
+ }.bind(this));
+
+ // delete dependencies for removed elements
+ for (var i = 0, length = obsoleteNodes.length; i < length; i++) {
+ _nodeDependencies.delete(obsoleteNodes.id);
+ }
+
+ this.checkContainers();
+ },
+
+ /**
+ * Adds the given callback to the list of callbacks called when checking containers.
+ *
+ * @param {function} callback
+ */
+ addContainerCheckCallback: function(callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError("Expected a valid callback for parameter 'callback'.");
+ }
+
+ EventHandler.add('com.woltlab.wcf.form.builder.dependency', 'checkContainers', callback);
+ },
+
+ /**
+ * Checks the containers for their availability.
+ *
+ * If this function is called while containers are currently checked, the containers
+ * will be checked after the current check has been finished completely.
+ */
+ checkContainers: function() {
+ // check if containers are currently being checked
+ if (_checkingContainers === true) {
+ // and if that is the case, calling this method indicates, that after the current round,
+ // containters should be checked to properly propagate changes in children to their parents
+ _checkContainersAgain = true;
+
+ return;
+ }
+
+ // starting to check containers also resets the flag to check containers again after the current check
+ _checkingContainers = true;
+ _checkContainersAgain = false;
+
+ EventHandler.fire('com.woltlab.wcf.form.builder.dependency', 'checkContainers');
+
+ // finish checking containers and check if containters should be checked again
+ _checkingContainers = false;
+ if (_checkContainersAgain) {
+ this.checkContainers();
+ }
+ },
+
+ /**
+ * Returns `true` if the given node has been hidden because of its own dependencies.
+ *
+ * @param {HTMLElement} node checked node
+ * @return {boolean}
+ */
+ isHiddenByDependencies: function(node) {
+ if (_dependencyHiddenNodes.has(node)) {
+ return true;
+ }
+
+ var returnValue = false;
+ _dependencyHiddenNodes.forEach(function(hiddenNode) {
+ if (DomUtil.contains(hiddenNode, node)) {
+ returnValue = true;
+ }
+ });
+
+ return returnValue;
+ },
+
+ /**
+ * Registers the form with the given id with the dependency manager.
+ *
+ * @param {string} formId id of register form
+ * @throws {Error} if given form id is invalid or has already been registered
+ */
+ register: function(formId) {
+ var form = elById(formId);
+
+ if (form === null) {
+ throw new Error("Unknown element with id '" + formId + "'");
+ }
+
+ if (_forms.has(form)) {
+ throw new Error("Form with id '" + formId + "' has already been registered.");
+ }
+
+ _forms.add(form);
+ },
+
+ /**
+ * Unregisters the form with the given id and all of its dependencies.
+ *
+ * @param {string} formId id of unregistered form
+ */
+ unregister: function(formId) {
+ var form = elById(formId);
+
+ if (form === null) {
+ throw new Error("Unknown element with id '" + formId + "'");
+ }
+
+ if (!_forms.has(form)) {
+ throw new Error("Form with id '" + formId + "' has not been registered.");
+ }
+
+ _forms.delete(form);
+
+ _dependencyHiddenNodes.forEach(function(hiddenNode) {
+ if (form.contains(hiddenNode)) {
+ _dependencyHiddenNodes.delete(hiddenNode);
+ }
+ });
+ _nodeDependencies.forEach(function(dependencies, nodeId) {
+ if (form.contains(elById(nodeId))) {
+ _nodeDependencies.delete(nodeId);
+ }
+
+ for (var i = 0, length = dependencies.length; i < length; i++) {
+ var fields = dependencies[i].getFields();
+ for (var j = 0, length = fields.length; j < length; j++) {
+ var field = fields[j];
+
+ _fields.delete(field.id);
+
+ _validatedFieldProperties.delete(field);
+ }
+ }
+ });
+ }
+ };
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Field
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Field',[], function() {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderField(fieldId) {
+ this.init(fieldId);
+ };
+ FormBuilderField.prototype = {
+ /**
+ * Initializes the form field.
+ *
+ * @param {string} fieldId id of the relevant form builder field
+ */
+ init: function(fieldId) {
+ this._fieldId = fieldId;
+
+ this._readField();
+ },
+
+ /**
+ * Returns the current data of the field or a promise returning the current data
+ * of the field.
+ *
+ * @return {Promise|data}
+ */
+ _getData: function() {
+ throw new Error("Missing implementation of WoltLabSuite/Core/Form/Builder/Field/Field._getData!");
+ },
+
+ /**
+ * Reads the field HTML element.
+ */
+ _readField: function() {
+ this._field = elById(this._fieldId);
+
+ if (this._field === null) {
+ throw new Error("Unknown field with id '" + this._fieldId + "'.");
+ }
+ },
+
+ /**
+ * Destroys the field.
+ *
+ * This function is useful for remove registered elements from other APIs like dialogs.
+ */
+ destroy: function() {
+ // does nothing
+ },
+
+ /**
+ * Returns a promise returning the current data of the field.
+ *
+ * @return {Promise}
+ */
+ getData: function() {
+ return Promise.resolve(this._getData());
+ },
+
+ /**
+ * Returns the id of the field.
+ *
+ * @return {string}
+ */
+ getId: function() {
+ return this._fieldId;
+ }
+ };
+
+ return FormBuilderField;
+});
+
+/**
+ * Manager for registered Ajax forms and its fields that can be used to retrieve the current data
+ * of the registered forms.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Manager
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Manager',[
+ 'Core',
+ 'Dictionary',
+ './Field/Dependency/Manager',
+ './Field/Field'
+], function(
+ Core,
+ Dictionary,
+ FormBuilderFieldDependencyManager,
+ FormBuilderField
+) {
+ "use strict";
+
+ var _fields = new Dictionary();
+ var _forms = new Dictionary();
+
+ return {
+ /**
+ * Returns a promise returning the data of the form with the given id.
+ *
+ * @param {string} formId
+ * @return {Promise}
+ */
+ getData: function(formId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ var promises = [];
+
+ _fields.get(formId).forEach(function(field) {
+ var fieldData = field.getData();
+
+ if (!(fieldData instanceof Promise)) {
+ throw new TypeError("Data for field with id '" + field.getId() + "' is no promise.");
+ }
+
+ promises.push(fieldData);
+ });
+
+ return Promise.all(promises).then(function(promiseData) {
+ var data = {};
+
+ for (var i = 0, length = promiseData.length; i < length; i++) {
+ data = Core.extend(data, promiseData[i]);
+ }
+
+ return data;
+ });
+ },
+
+ /**
+ * Returns the registered form with given id.
+ *
+ * @param {string} formId
+ * @return {HTMLElement}
+ */
+ getForm: function(formId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ return _forms.get(formId);
+ },
+
+ /**
+ * Returns `true` if a field with the given id has been registered for the form with
+ * the given id and `false` otherwise.
+ *
+ * @param {string} formId
+ * @param {string} fieldId
+ * @return {boolean}
+ */
+ hasField: function(formId, fieldId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ return _fields.get(formId).has(fieldId);
+ },
+
+ /**
+ * Returns `true` if a form with the given id has been registered and `false`
+ * otherwise.
+ *
+ * @param {string} formId
+ * @return {boolean}
+ */
+ hasForm: function(formId) {
+ return _forms.has(formId);
+ },
+
+ /**
+ * Registers the given field for the form with the given id.
+ *
+ * @param {string} formId
+ * @param {WoltLabSuite/Core/Form/Builder/Field/Field} field
+ */
+ registerField: function(formId, field) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ if (!(field instanceof FormBuilderField)) {
+ throw new Error("Add field is no instance of 'WoltLabSuite/Core/Form/Builder/Field/Field'.");
+ }
+
+ var fieldId = field.getId();
+
+ if (this.hasField(formId, fieldId)) {
+ throw new Error("Form field with id '" + fieldId + "' has already been registered for form with id '" + fieldId + "'.");
+ }
+
+ _fields.get(formId).set(fieldId, field);
+ },
+
+ /**
+ * Registers the form with the given id.
+ *
+ * @param {string} formId
+ */
+ registerForm: function(formId) {
+ if (this.hasForm(formId)) {
+ throw new Error("Form with id '" + formId + "' has already been registered.");
+ }
+
+ var form = elById(formId);
+ if (form === null) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ _forms.set(formId, form);
+ _fields.set(formId, new Dictionary());
+ },
+
+ /**
+ * Unregisters the form with the given id.
+ *
+ * @param {string} formId
+ */
+ unregisterForm: function(formId) {
+ if (!this.hasForm(formId)) {
+ throw new Error("Unknown form with id '" + formId + "'.");
+ }
+
+ _forms.delete(formId);
+
+ _fields.get(formId).forEach(function(field) {
+ field.destroy();
+ });
+
+ _fields.delete(formId);
+
+ FormBuilderFieldDependencyManager.unregister(formId);
+ }
+ };
+});
+
+/**
+ * Provides API to easily create a dialog form created by form builder.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Dialog
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Dialog',['Ajax', 'Core', './Manager', 'Ui/Dialog'], function(Ajax, Core, FormBuilderManager, UiDialog) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderDialog(dialogId, className, actionName, options) {
+ this.init(dialogId, className, actionName, options);
+ };
+ FormBuilderDialog.prototype = {
+ /**
+ * Initializes the dialog.
+ *
+ * @param {string} dialogId
+ * @param {string} className
+ * @param {string} actionName
+ * @param {{actionParameters: object, destoryOnClose: boolean, dialog: object}} options
+ */
+ init: function(dialogId, className, actionName, options) {
+ this._dialogId = dialogId;
+ this._className = className;
+ this._actionName = actionName;
+ this._options = Core.extend({
+ actionParameters: {},
+ destroyOnClose: false,
+ usesDboAction: this._className.match(/\w+\\data\\/)
+ }, options);
+ this._options.dialog = Core.extend(this._options.dialog || {}, {
+ onClose: this._dialogOnClose.bind(this)
+ });
+
+ this._formId = '';
+ this._dialogContent = '';
+ },
+
+ /**
+ * Returns the data for Ajax to setup the Ajax/Request object.
+ *
+ * @return {object} setup data for Ajax/Request object
+ */
+ _ajaxSetup: function() {
+ var options = {
+ data: {
+ actionName: this._actionName,
+ className: this._className,
+ parameters: this._options.actionParameters
+ }
+ };
+
+ // by default, `AJAXProxyAction` is used which relies on an `IDatabaseObjectAction`
+ // object; if no such object is used but an `IAJAXInvokeAction` object,
+ // `AJAXInvokeAction` has to be used
+ if (!this._options.usesDboAction) {
+ options.url = 'index.php?ajax-invoke/&t=' + SECURITY_TOKEN;
+ options.withCredentials = true;
+ }
+
+ return options;
+ },
+
+ /**
+ * Handles successful Ajax requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case this._actionName:
+ if (data.returnValues === undefined) {
+ throw new Error("Missing return data.");
+ }
+ else if (data.returnValues.dialog === undefined) {
+ throw new Error("Missing dialog template in return data.");
+ }
+ else if (data.returnValues.formId === undefined) {
+ throw new Error("Missing form id in return data.");
+ }
+
+ this._openDialogContent(data.returnValues.formId, data.returnValues.dialog);
+
+ break;
+
+ case this._options.submitActionName:
+ // if the validation failed, the dialog is shown again
+ if (data.returnValues && data.returnValues.formId && data.returnValues.dialog) {
+ if (data.returnValues.formId !== this._formId) {
+ throw new Error("Mismatch between form ids: expected '" + this._formId + "' but got '" + data.returnValues.formId + "'.");
+ }
+
+ this._openDialogContent(data.returnValues.formId, data.returnValues.dialog);
+ }
+ else {
+ this.destroy();
+
+ if (typeof this._options.successCallback === 'function') {
+ this._options.successCallback(data.returnValues || {});
+ }
+ }
+
+ break;
+
+ default:
+ throw new Error("Cannot handle action '" + data.actionName + "'.");
+ }
+ },
+
+ /**
+ * Is called when clicking on the dialog form's close button.
+ */
+ _closeDialog: function() {
+ UiDialog.close(this);
+ },
+
+ /**
+ * Is called by the dialog API when the dialog is closed.
+ */
+ _dialogOnClose: function() {
+ if (this._options.destroyOnClose) {
+ this.destroy();
+ }
+ },
+
+ /**
+ * Returns the data used to setup the dialog.
+ *
+ * @return {object} setup data
+ */
+ _dialogSetup: function() {
+ return {
+ id: this._dialogId,
+ options : this._options.dialog,
+ source: this._dialogContent
+ };
+ },
+
+ /**
+ * Is called by the dialog API when the dialog form is submitted.
+ */
+ _dialogSubmit: function() {
+ this.getData().then(this._submitForm.bind(this));
+ },
+
+ /**
+ * Opens the form dialog with the given form content.
+ *
+ * @param {string} formId
+ * @param {string} dialogContent
+ */
+ _openDialogContent: function(formId, dialogContent) {
+ this.destroy(true);
+
+ this._formId = formId;
+ this._dialogContent = dialogContent;
+
+ var dialogData = UiDialog.open(this, this._dialogContent);
+
+ var cancelButton = elBySel('button[data-type=cancel]', dialogData.content);
+ if (cancelButton !== null && !elDataBool(cancelButton, 'has-event-listener')) {
+ cancelButton.addEventListener('click', this._closeDialog.bind(this));
+ elData(cancelButton, 'has-event-listener', 1);
+ }
+ },
+
+ /**
+ * Submits the form with the given form data.
+ *
+ * @param {object} formData
+ */
+ _submitForm: function(formData) {
+ var submitButton = elBySel('button[data-type=submit]', UiDialog.getDialog(this).content);
+
+ if (typeof this._options.onSubmit === 'function') {
+ this._options.onSubmit(formData, submitButton);
+ }
+ else if (typeof this._options.submitActionName === 'string') {
+ submitButton.disabled = true;
+
+ Ajax.api(this, {
+ actionName: this._options.submitActionName,
+ parameters: {
+ data: formData,
+ formId: this._formId
+ }
+ });
+ }
+ },
+
+ /**
+ * Destroys the dialog.
+ *
+ * @param {boolean} ignoreDialog if `true`, the actual dialog is not destroyed, only the form is
+ */
+ destroy: function(ignoreDialog) {
+ if (this._formId !== '') {
+ if (FormBuilderManager.hasForm(this._formId)) {
+ FormBuilderManager.unregisterForm(this._formId);
+ }
+
+ if (ignoreDialog !== true) {
+ UiDialog.destroy(this);
+ }
+ }
+ },
+
+ /**
+ * Returns a promise that all of the dialog form's data.
+ *
+ * @return {Promise}
+ */
+ getData: function() {
+ if (this._formId === '') {
+ throw new Error("Form has not been requested yet.");
+ }
+
+ return FormBuilderManager.getData(this._formId);
+ },
+
+ /**
+ * Opens the dialog form.
+ */
+ open: function() {
+ if (UiDialog.getDialog(this._dialogId)) {
+ UiDialog.openStatic(this._dialogId);
+ }
+ else {
+ Ajax.api(this);
+ }
+ }
+ };
+
+ return FormBuilderDialog;
+});
+
+/**
+ * Provides the media search for the media manager.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Search
+ */
+define('WoltLabSuite/Core/Media/Manager/Search',['Ajax', 'Core', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language', 'Ui/SimpleDropdown'], function(Ajax, Core, DomTraverse, DomUtil, EventKey, Language, UiSimpleDropdown) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {},
+ _cancelSearch: function() {},
+ _keyPress: function() {},
+ _search: function() {},
+ hideSearch: function() {},
+ resetSearch: function() {},
+ showSearch: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaManagerSearch(mediaManager) {
+ this._mediaManager = mediaManager;
+ this._searchMode = false;
+
+ this._searchContainer = elByClass('mediaManagerSearch', mediaManager.getDialog())[0];
+ this._input = elByClass('mediaManagerSearchField', mediaManager.getDialog())[0];
+ this._input.addEventListener('keypress', this._keyPress.bind(this));
+
+ this._cancelButton = elByClass('mediaManagerSearchCancelButton', mediaManager.getDialog())[0];
+ this._cancelButton.addEventListener(WCF_CLICK_EVENT, this._cancelSearch.bind(this));
+ }
+ MediaManagerSearch.prototype = {
+ /**
+ * Returns the data for Ajax to setup the Ajax/Request object.
+ *
+ * @return {object} setup data for Ajax/Request object
+ */
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getSearchResultList',
+ className: 'wcf\\data\\media\\MediaAction',
+ interfaceName: 'wcf\\data\\ISearchAction'
+ }
+ };
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {object} data response data
+ */
+ _ajaxSuccess: function(data) {
+ this._mediaManager.setMedia(data.returnValues.media || { }, data.returnValues.template || '', {
+ pageCount: data.returnValues.pageCount || 0,
+ pageNo: data.returnValues.pageNo || 0
+ });
+
+ elByClass('dialogContent', this._mediaManager.getDialog())[0].scrollTop = 0;
+ },
+
+ /**
+ * Cancels the search after clicking on the cancel search button.
+ */
+ _cancelSearch: function() {
+ if (this._searchMode) {
+ this._searchMode = false;
+
+ this.resetSearch();
+ this._mediaManager.resetMedia();
+ }
+ },
+
+ /**
+ * Hides the search string threshold error.
+ */
+ _hideStringThresholdError: function() {
+ var innerInfo = DomTraverse.childByClass(this._input.parentNode.parentNode, 'innerInfo');
+ if (innerInfo) {
+ elHide(innerInfo);
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` key to submit the form.
+ *
+ * @param {Event} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ if (this._input.value.length >= this._mediaManager.getOption('minSearchLength')) {
+ this._hideStringThresholdError();
+
+ this.search();
+ }
+ else {
+ this._showStringThresholdError();
+ }
+ }
+ },
+
+ /**
+ * Shows the search string threshold error.
+ */
+ _showStringThresholdError: function() {
+ var innerInfo = DomTraverse.childByClass(this._input.parentNode.parentNode, 'innerInfo');
+ if (innerInfo) {
+ elShow(innerInfo);
+ }
+ else {
+ innerInfo = elCreate('p');
+ innerInfo.className = 'innerInfo';
+ innerInfo.textContent = Language.get('wcf.media.search.info.searchStringThreshold', {
+ minSearchLength: this._mediaManager.getOption('minSearchLength')
+ });
+
+ DomUtil.insertAfter(innerInfo, this._input.parentNode);
+ }
+ },
+
+ /**
+ * Hides the media search.
+ */
+ hideSearch: function() {
+ elHide(this._searchContainer);
+ },
+
+ /**
+ * Resets the media search.
+ */
+ resetSearch: function() {
+ this._input.value = '';
+ },
+
+ /**
+ * Shows the media search.
+ */
+ showSearch: function() {
+ elShow(this._searchContainer);
+ },
+
+ /**
+ * Sends an AJAX request to fetch search results.
+ *
+ * @param {integer} pageNo
+ */
+ search: function(pageNo) {
+ if (typeof pageNo !== "number") {
+ pageNo = 1;
+ }
+
+ var searchString = this._input.value;
+ if (searchString && this._input.value.length < this._mediaManager.getOption('minSearchLength')) {
+ this._showStringThresholdError();
+
+ searchString = '';
+ }
+ else {
+ this._hideStringThresholdError();
+ }
+
+ this._searchMode = true;
+
+ Ajax.api(this, {
+ parameters: {
+ categoryID: this._mediaManager.getCategoryId(),
+ imagesOnly: this._mediaManager.getOption('imagesOnly'),
+ mode: this._mediaManager.getMode(),
+ pageNo: pageNo,
+ searchString: searchString
+ }
+ });
+ },
+ };
+
+ return MediaManagerSearch;
+});
+
+/**
+ * Provides the media manager dialog.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Base
+ */
+define(
+ 'WoltLabSuite/Core/Media/Manager/Base',[
+ 'Core', 'Dictionary', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'EventHandler', 'Language', 'List',
+ 'Permission', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Controller/Clipboard',
+ 'WoltLabSuite/Core/Media/Editor', 'WoltLabSuite/Core/Media/Upload', 'WoltLabSuite/Core/Media/Manager/Search', 'StringUtil',
+ 'WoltLabSuite/Core/Ui/Pagination',
+ 'WoltLabSuite/Core/Media/Clipboard'
+ ],
+ function(
+ Core, Dictionary, DomChangeListener, DomTraverse,
+ DomUtil, EventHandler, Language, List,
+ Permission, UiDialog, UiNotification, Clipboard,
+ MediaEditor, MediaUpload, MediaManagerSearch, StringUtil,
+ UiPagination,
+ MediaClipboard
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _addButtonEventListeners: function() {},
+ _click: function() {},
+ _dialogClose: function() {},
+ _dialogInit: function() {},
+ _dialogSetup: function() {},
+ _dialogShow: function() {},
+ _editMedia: function() {},
+ _editorClose: function() {},
+ _editorSuccess: function() {},
+ _removeClipboardCheckboxes: function() {},
+ _setMedia: function() {},
+ addMedia: function() {},
+ clipboardDeleteMedia: function() {},
+ getDialog: function() {},
+ getMode: function() {},
+ getOption: function() {},
+ removeMedia: function() {},
+ resetMedia: function() {},
+ setMedia: function() {},
+ setupMediaElement: function() {}
+ };
+ return Fake;
+ }
+
+ var _mediaManagerCounter = 0;
+
+ /**
+ * @constructor
+ */
+ function MediaManagerBase(options) {
+ this._options = Core.extend({
+ dialogTitle: Language.get('wcf.media.manager'),
+ imagesOnly: false,
+ minSearchLength: 3
+ }, options);
+
+ this._id = 'mediaManager' + _mediaManagerCounter++;
+ this._listItems = new Dictionary();
+ this._media = new Dictionary();
+ this._mediaManagerMediaList = null;
+ this._search = null;
+ this._upload = null;
+ this._forceClipboard = false;
+ this._hadInitiallyMarkedItems = false;
+ this._pagination = null;
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ this._mediaEditor = new MediaEditor(this);
+ }
+
+ DomChangeListener.add('WoltLabSuite/Core/Media/Manager', this._addButtonEventListeners.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._openEditorAfterUpload.bind(this));
+ }
+ MediaManagerBase.prototype = {
+ /**
+ * Adds click event listeners to media buttons.
+ */
+ _addButtonEventListeners: function() {
+ if (!this._mediaManagerMediaList) return;
+
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ var editIcon = elByClass('jsMediaEditButton', listItem)[0];
+ if (editIcon) {
+ editIcon.classList.remove('jsMediaEditButton');
+ editIcon.addEventListener(WCF_CLICK_EVENT, this._editMedia.bind(this));
+ }
+ }
+ }
+ },
+
+ /**
+ * Is called when a new category is selected.
+ */
+ _categoryChange: function() {
+ this._search.search();
+ },
+
+ /**
+ * Handles clicks on the media manager button.
+ *
+ * @param {object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ UiDialog.open(this);
+ },
+
+ /**
+ * Is called if the media manager dialog is closed.
+ */
+ _dialogClose: function() {
+ // only show media clipboard if editor is open
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ Clipboard.hideEditor('com.woltlab.wcf.media');
+ }
+ },
+
+ /**
+ * Initializes the dialog when first loaded.
+ *
+ * @param {string} content dialog content
+ * @param {object} data AJAX request's response data
+ */
+ _dialogInit: function(content, data) {
+ // store media data locally
+ var media = data.returnValues.media || { };
+ for (var mediaId in media) {
+ if (objOwns(media, mediaId)) {
+ this._media.set(~~mediaId, media[mediaId]);
+ }
+ }
+
+ this._initPagination(~~data.returnValues.pageCount);
+
+ this._hadInitiallyMarkedItems = data.returnValues.hasMarkedItems;
+ },
+
+ /**
+ * Returns all data to setup the media manager dialog.
+ *
+ * @return {object} dialog setup data
+ */
+ _dialogSetup: function() {
+ return {
+ id: this._id,
+ options: {
+ onClose: this._dialogClose.bind(this),
+ onShow: this._dialogShow.bind(this),
+ title: this._options.dialogTitle
+ },
+ source: {
+ after: this._dialogInit.bind(this),
+ data: {
+ actionName: 'getManagementDialog',
+ className: 'wcf\\data\\media\\MediaAction',
+ parameters: {
+ mode: this.getMode(),
+ imagesOnly: this._options.imagesOnly
+ }
+ }
+ }
+ };
+ },
+
+ /**
+ * Is called if the media manager dialog is shown.
+ */
+ _dialogShow: function() {
+ if (!this._mediaManagerMediaList) {
+ var dialog = this.getDialog();
+
+ this._mediaManagerMediaList = elByClass('mediaManagerMediaList', dialog)[0];
+
+ this._mediaCategorySelect = elBySel('.mediaManagerCategoryList > select', dialog);
+ if (this._mediaCategorySelect) {
+ this._mediaCategorySelect.addEventListener('change', this._categoryChange.bind(this));
+ }
+
+ // store list items locally
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ this._listItems.set(~~elData(listItem, 'object-id'), listItem);
+ }
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ var uploadButton = elByClass('mediaManagerMediaUploadButton', UiDialog.getDialog(this).dialog)[0];
+ this._upload = new MediaUpload(DomUtil.identify(uploadButton), DomUtil.identify(this._mediaManagerMediaList), {
+ mediaManager: this
+ });
+
+ var deleteAction = new WCF.Action.Delete('wcf\\data\\media\\MediaAction', '.mediaFile');
+ deleteAction._didTriggerEffect = function(element) {
+ this.removeMedia(elData(element[0], 'object-id'));
+ }.bind(this);
+ }
+
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ MediaClipboard.init(
+ 'menuManagerDialog-' + this.getMode(),
+ this._hadInitiallyMarkedItems ? true : false,
+ this
+ );
+ }
+ else {
+ this._removeClipboardCheckboxes();
+ }
+
+ this._search = new MediaManagerSearch(this);
+
+ if (!listItems.length) {
+ this._search.hideSearch();
+ }
+ }
+
+ // only show media clipboard if editor is open
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ Clipboard.showEditor('com.woltlab.wcf.media');
+ }
+ },
+
+ /**
+ * Opens the media editor for a media file.
+ *
+ * @param {Event} event event object for clicks on edit icons
+ */
+ _editMedia: function(event) {
+ if (!Permission.get('admin.content.cms.canManageMedia')) {
+ throw new Error("You are not allowed to edit media files.");
+ }
+
+ UiDialog.close(this);
+
+ this._mediaEditor.edit(this._media.get(~~elData(event.currentTarget, 'object-id')));
+ },
+
+ /**
+ * Re-opens the manager dialog after closing the editor dialog.
+ */
+ _editorClose: function() {
+ UiDialog.open(this);
+ },
+
+ /**
+ * Re-opens the manager dialog and updates the media data after
+ * successfully editing a media file.
+ *
+ * @param {object} media updated media file data
+ * @param {integer} oldCategoryId old category id
+ */
+ _editorSuccess: function(media, oldCategoryId) {
+ // if the category changed of media changed and category
+ // is selected, check if media list needs to be refreshed
+ if (this._mediaCategorySelect) {
+ var selectedCategoryId = ~~this._mediaCategorySelect.value;
+
+ if (selectedCategoryId) {
+ var newCategoryId = ~~media.categoryID;
+
+ if (oldCategoryId != newCategoryId && (oldCategoryId == selectedCategoryId || newCategoryId == selectedCategoryId)) {
+ this._search.search();
+ }
+ }
+ }
+
+ UiDialog.open(this);
+
+ this._media.set(~~media.mediaID, media);
+
+ var listItem = this._listItems.get(~~media.mediaID);
+ var p = elByClass('mediaTitle', listItem)[0];
+ if (media.isMultilingual) {
+ p.textContent = media.title[LANGUAGE_ID] || media.filename;
+ }
+ else {
+ p.textContent = media.title[media.languageID] || media.filename;
+ }
+ },
+
+ /**
+ * Initializes the dialog pagination.
+ *
+ * @param {integer} pageCount
+ * @param {integer} pageNo
+ */
+ _initPagination: function(pageCount, pageNo) {
+ if (pageNo === undefined) pageNo = 1;
+
+ if (pageCount > 1) {
+ var newPagination = elCreate('div');
+ newPagination.className = 'paginationBottom jsPagination';
+ DomUtil.replaceElement(elBySel('.jsPagination', UiDialog.getDialog(this).content), newPagination);
+
+ this._pagination = new UiPagination(newPagination, {
+ activePage: pageNo,
+ callbackSwitch: this._search.search.bind(this._search),
+ maxPage: pageCount
+ });
+ }
+ else if (this._pagination) {
+ elHide(this._pagination.getElement());
+ }
+ },
+
+ /**
+ * Removes all media clipboard checkboxes.
+ */
+ _removeClipboardCheckboxes: function() {
+ var checkboxes = elByClass('mediaCheckbox', this._mediaManagerMediaList);
+ while (checkboxes.length) {
+ elRemove(checkboxes[0]);
+ }
+ },
+
+ /**
+ * Opens the media editor after uploading a single file.
+ *
+ * @param {object} data upload event data
+ * @since 5.2
+ */
+ _openEditorAfterUpload: function(data) {
+ if (data.upload === this._upload && !data.isMultiFileUpload && !this._upload.hasPendingUploads()) {
+ var keys = Object.keys(data.media);
+
+ if (keys.length) {
+ UiDialog.close(this);
+
+ this._mediaEditor.edit(this._media.get(~~data.media[keys[0]].mediaID));
+ }
+ }
+ },
+
+ /**
+ * Sets the displayed media (after a search).
+ *
+ * @param {Dictionary} media media to be set as active
+ */
+ _setMedia: function(media) {
+ if (Core.isPlainObject(media)) {
+ this._media = Dictionary.fromObject(media);
+ }
+ else {
+ this._media = media;
+ }
+
+ var info = DomTraverse.nextByClass(this._mediaManagerMediaList, 'info');
+
+ if (this._media.size) {
+ if (info) {
+ elHide(info);
+ }
+ }
+ else {
+ if (info === null) {
+ info = elCreate('p');
+ info.className = 'info';
+ info.textContent = Language.get('wcf.media.search.noResults');
+ }
+
+ elShow(info);
+ DomUtil.insertAfter(info, this._mediaManagerMediaList);
+ }
+
+ var mediaListItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = mediaListItems.length; i < length; i++) {
+ var listItem = mediaListItems[i];
+
+ if (!this._media.has(elData(listItem, 'object-id'))) {
+ elHide(listItem);
+ }
+ else {
+ elShow(listItem);
+ }
+ }
+
+ DomChangeListener.trigger();
+
+ if (Permission.get('admin.content.cms.canManageMedia') || this._forceClipboard) {
+ Clipboard.reload();
+ }
+ else {
+ this._removeClipboardCheckboxes();
+ }
+ },
+
+ /**
+ * Adds a media file to the manager.
+ *
+ * @param {object} media data of the media file
+ * @param {Element} listItem list item representing the file
+ */
+ addMedia: function(media, listItem) {
+ if (!media.languageID) media.isMultilingual = 1;
+
+ this._media.set(~~media.mediaID, media);
+ this._listItems.set(~~media.mediaID, listItem);
+
+ if (this._listItems.size === 1) {
+ this._search.showSearch();
+ }
+ },
+
+ /**
+ * Is called after the media files with the given ids have been deleted via clipboard.
+ *
+ * @param {int[]} mediaIds ids of deleted media files
+ */
+ clipboardDeleteMedia: function(mediaIds) {
+ for (var i = 0, length = mediaIds.length; i < length; i++) {
+ this.removeMedia(~~mediaIds[i], true);
+ }
+
+ UiNotification.show();
+ },
+
+ /**
+ * Returns the id of the currently selected category or `0` if no category is selected.
+ *
+ * @return {integer}
+ */
+ getCategoryId: function() {
+ if (this._mediaCategorySelect) {
+ return this._mediaCategorySelect.value;
+ }
+
+ return 0;
+ },
+
+ /**
+ * Returns the media manager dialog element.
+ *
+ * @return {Element} media manager dialog
+ */
+ getDialog: function() {
+ return UiDialog.getDialog(this).dialog;
+ },
+
+ /**
+ * Returns the mode of the media manager.
+ *
+ * @return {string}
+ */
+ getMode: function() {
+ return '';
+ },
+
+ /**
+ * Returns the media manager option with the given name.
+ *
+ * @param {string} name option name
+ * @return {mixed} option value or null
+ */
+ getOption: function(name) {
+ if (this._options[name]) {
+ return this._options[name];
+ }
+
+ return null;
+ },
+
+ /**
+ * Removes a media file.
+ *
+ * @param {int} mediaId id of the removed media file
+ */
+ removeMedia: function(mediaId) {
+ if (this._listItems.has(mediaId)) {
+ // remove list item
+ try {
+ elRemove(this._listItems.get(mediaId));
+ }
+ catch (e) {
+ // ignore errors if item has already been removed like by WCF.Action.Delete
+ }
+
+ this._listItems.delete(mediaId);
+ this._media.delete(mediaId);
+ }
+ },
+
+ /**
+ * Changes the displayed media to the previously displayed media.
+ */
+ resetMedia: function() {
+ // calling WoltLabSuite/Core/Media/Manager/Search.search() reloads the first page of the dialog
+ this._search.search();
+ },
+
+ /**
+ * Sets the media files currently displayed.
+ *
+ * @param {object} media media data
+ * @param {string} template
+ * @param {object} additionalData
+ */
+ setMedia: function(media, template, additionalData) {
+ var hasMedia = false;
+ for (var mediaId in media) {
+ if (objOwns(media, mediaId)) {
+ hasMedia = true;
+ }
+ }
+
+ var newListItems = [];
+ if (hasMedia) {
+ var ul = elCreate('ul');
+ ul.innerHTML = template;
+
+ var listItems = DomTraverse.childrenByTag(ul, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+ if (!this._listItems.has(~~elData(listItem, 'object-id'))) {
+ this._listItems.set(elData(listItem, 'object-id'), listItem);
+
+ this._mediaManagerMediaList.appendChild(listItem);
+ }
+ }
+ }
+
+ this._initPagination(additionalData.pageCount, additionalData.pageNo);
+
+ this._setMedia(media);
+ },
+
+ /**
+ * Sets up a new media element.
+ *
+ * @param {object} media data of the media file
+ * @param {HTMLElement} mediaElement element representing the media file
+ */
+ setupMediaElement: function(media, mediaElement) {
+ var mediaInformation = DomTraverse.childByClass(mediaElement, 'mediaInformation');
+
+ var buttonGroupNavigation = elCreate('nav');
+ buttonGroupNavigation.className = 'jsMobileNavigation buttonGroupNavigation';
+ mediaInformation.parentNode.appendChild(buttonGroupNavigation);
+
+ var buttons = elCreate('ul');
+ buttons.className = 'buttonList iconList';
+ buttonGroupNavigation.appendChild(buttons);
+
+ var listItem = elCreate('li');
+ listItem.className = 'mediaCheckbox';
+ buttons.appendChild(listItem);
+
+ var a = elCreate('a');
+ listItem.appendChild(a);
+
+ var label = elCreate('label');
+ a.appendChild(label);
+
+ var checkbox = elCreate('input');
+ checkbox.className = 'jsClipboardItem';
+ elAttr(checkbox, 'type', 'checkbox');
+ elData(checkbox, 'object-id', media.mediaID);
+ label.appendChild(checkbox);
+
+ if (Permission.get('admin.content.cms.canManageMedia')) {
+ listItem = elCreate('li');
+ listItem.className = 'jsMediaEditButton';
+ elData(listItem, 'object-id', media.mediaID);
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-pencil jsTooltip" title="' + Language.get('wcf.global.button.edit') + '"></span> <span class="invisible">' + Language.get('wcf.global.button.edit') + '</span></a>';
+
+ listItem = elCreate('li');
+ listItem.className = 'jsDeleteButton';
+ elData(listItem, 'object-id', media.mediaID);
+
+ // use temporary title to not unescape html in filename
+ var uuid = Core.getUuid();
+ elData(listItem, 'confirm-message-html', StringUtil.unescapeHTML(Language.get('wcf.media.delete.confirmMessage', {
+ title: uuid
+ })).replace(uuid, StringUtil.escapeHTML(media.filename)));
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-times jsTooltip" title="' + Language.get('wcf.global.button.delete') + '"></span> <span class="invisible">' + Language.get('wcf.global.button.delete') + '</span></a>';
+ }
+ }
+ };
+
+ return MediaManagerBase;
+});
+
+/**
+ * Provides the media manager dialog for selecting media for Redactor editors.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Editor
+ */
+define('WoltLabSuite/Core/Media/Manager/Editor',['Core', 'Dictionary', 'Dom/Traverse', 'EventHandler', 'Language', 'Permission', 'Ui/Dialog', 'WoltLabSuite/Core/Controller/Clipboard', 'WoltLabSuite/Core/Media/Manager/Base'],
+ function(Core, Dictionary, DomTraverse, EventHandler, Language, Permission, UiDialog, ControllerClipboard, MediaManagerBase) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _addButtonEventListeners: function() {},
+ _buildInsertDialog: function() {},
+ _click: function() {},
+ _getInsertDialogId: function() {},
+ _getThumbnailSizes: function() {},
+ _insertMedia: function() {},
+ _insertMediaGallery: function() {},
+ _insertMediaItem: function() {},
+ _openInsertDialog: function() {},
+ insertMedia: function() {},
+ getMode: function() {},
+ setupMediaElement: function() {},
+ _dialogClose: function() {},
+ _dialogInit: function() {},
+ _dialogSetup: function() {},
+ _dialogShow: function() {},
+ _editMedia: function() {},
+ _editorClose: function() {},
+ _editorSuccess: function() {},
+ _removeClipboardCheckboxes: function() {},
+ _setMedia: function() {},
+ addMedia: function() {},
+ clipboardInsertMedia: function() {},
+ getDialog: function() {},
+ getOption: function() {},
+ removeMedia: function() {},
+ resetMedia: function() {},
+ setMedia: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaManagerEditor(options) {
+ options = Core.extend({
+ callbackInsert: null
+ }, options);
+
+ MediaManagerBase.call(this, options);
+
+ this._forceClipboard = true;
+ this._activeButton = null;
+ var context = (this._options.editor) ? this._options.editor.core.toolbar()[0] : undefined;
+ this._buttons = elByClass(this._options.buttonClass || 'jsMediaEditorButton', context);
+ for (var i = 0, length = this._buttons.length; i < length; i++) {
+ this._buttons[i].addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }
+ this._mediaToInsert = new Dictionary();
+ this._mediaToInsertByClipboard = false;
+ this._uploadData = null;
+ this._uploadId = null;
+
+ if (this._options.editor && !this._options.editor.opts.woltlab.attachments) {
+ var editorId = elData(this._options.editor.$editor[0], 'element-id');
+
+ var uuid1 = EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, this._editorUpload.bind(this));
+ var uuid2 = EventHandler.add('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + editorId, this._editorUpload.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'destory_' + editorId, function() {
+ EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid1);
+ EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid2);
+ });
+
+ EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._mediaUploaded.bind(this));
+ }
+ }
+ Core.inherit(MediaManagerEditor, MediaManagerBase, {
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_addButtonEventListeners
+ */
+ _addButtonEventListeners: function() {
+ MediaManagerEditor._super.prototype._addButtonEventListeners.call(this);
+
+ if (!this._mediaManagerMediaList) return;
+
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ var insertIcon = elByClass('jsMediaInsertButton', listItem)[0];
+ if (insertIcon) {
+ insertIcon.classList.remove('jsMediaInsertButton');
+ insertIcon.addEventListener(WCF_CLICK_EVENT, this._openInsertDialog.bind(this));
+ }
+ }
+ },
+
+ /**
+ * Builds the dialog to setup inserting media files.
+ */
+ _buildInsertDialog: function() {
+ var thumbnailOptions = '';
+
+ var thumbnailSizes = this._getThumbnailSizes();
+ for (var i = 0, length = thumbnailSizes.length; i < length; i++) {
+ thumbnailOptions += '<option value="' + thumbnailSizes[i] + '">' + Language.get('wcf.media.insert.imageSize.' + thumbnailSizes[i]) + '</option>';
+ }
+ thumbnailOptions += '<option value="original">' + Language.get('wcf.media.insert.imageSize.original') + '</option>';
+
+ var dialog = '<div class="section">'
+ /*+ (this._mediaToInsert.size > 1 ? '<dl>'
+ + '<dt>' + Language.get('wcf.media.insert.type') + '</dt>'
+ + '<dd>'
+ + '<select name="insertType">'
+ + '<option value="separate">' + Language.get('wcf.media.insert.type.separate') + '</option>'
+ + '<option value="gallery">' + Language.get('wcf.media.insert.type.gallery') + '</option>'
+ + '</select>'
+ + '</dd>'
+ + '</dl>' : '')*/
+ + '<dl class="thumbnailSizeSelection">'
+ + '<dt>' + Language.get('wcf.media.insert.imageSize') + '</dt>'
+ + '<dd>'
+ + '<select name="thumbnailSize">'
+ + thumbnailOptions
+ + '</select>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button class="buttonPrimary">' + Language.get('wcf.global.button.insert') + '</button>'
+ + '</div>';
+
+ UiDialog.open({
+ _dialogSetup: (function() {
+ return {
+ id: this._getInsertDialogId(),
+ options: {
+ onClose: this._editorClose.bind(this),
+ onSetup: function(content) {
+ elByClass('buttonPrimary', content)[0].addEventListener(WCF_CLICK_EVENT, this._insertMedia.bind(this));
+
+ // toggle thumbnail size selection based on selected insert type
+ /*var insertType = elBySel('select[name=insertType]', content);
+ if (insertType !== null) {
+ var thumbnailSelection = elByClass('thumbnailSizeSelection', content)[0];
+ insertType.addEventListener('change', function(event) {
+ if (event.currentTarget.value === 'gallery') {
+ elHide(thumbnailSelection);
+ }
+ else {
+ elShow(thumbnailSelection);
+ }
+ });
+ }*/
+ var thumbnailSelection = elBySel('.thumbnailSizeSelection', content);
+ elShow(thumbnailSelection);
+ }.bind(this),
+ title: Language.get('wcf.media.insert')
+ },
+ source: dialog
+ };
+ }).bind(this)
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_click
+ */
+ _click: function(event) {
+ this._activeButton = event.currentTarget;
+
+ MediaManagerEditor._super.prototype._click.call(this, event);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_dialogShow
+ */
+ _dialogShow: function() {
+ MediaManagerEditor._super.prototype._dialogShow.call(this);
+
+ // check if data needs to be uploaded
+ if (this._uploadData) {
+ if (this._uploadData.file) {
+ this._upload.uploadFile(this._uploadData.file);
+ }
+ else {
+ this._uploadId = this._upload.uploadBlob(this._uploadData.blob);
+ }
+
+ this._uploadData = null;
+ }
+ },
+
+ /**
+ * Handles pasting and dragging and dropping files into the editor.
+ *
+ * @param {object} data data of the uploaded file
+ */
+ _editorUpload: function(data) {
+ this._uploadData = data;
+
+ UiDialog.open(this);
+ },
+
+ /**
+ * Returns the id of the insert dialog based on the media files to be inserted.
+ *
+ * @return {string} insert dialog id
+ */
+ _getInsertDialogId: function() {
+ var dialogId = 'mediaInsert';
+
+ this._mediaToInsert.forEach(function(media, mediaId) {
+ dialogId += '-' + mediaId;
+ });
+
+ return dialogId;
+ },
+
+ /**
+ * Returns the supported thumbnail sizes (excluding `original`) for all media images to be inserted.
+ *
+ * @return {string[]}
+ */
+ _getThumbnailSizes: function() {
+ var sizes = [];
+
+ var supportedSizes = ['small', 'medium', 'large'];
+ var size, supportSize;
+ for (var i = 0, length = supportedSizes.length; i < length; i++) {
+ size = supportedSizes[i];
+
+ supportSize = true;
+ this._mediaToInsert.forEach(function(media) {
+ if (!media[size + 'ThumbnailType']) {
+ supportSize = false;
+ }
+ });
+
+ if (supportSize) {
+ sizes.push(size);
+ }
+ }
+
+ return sizes;
+ },
+
+ /**
+ * Inserts media files into redactor.
+ *
+ * @param {Event?} event
+ * @param {string?} thumbnailSize
+ * @param {boolean?} closeEditor
+ */
+ _insertMedia: function(event, thumbnailSize, closeEditor) {
+ if (closeEditor === undefined) closeEditor = true;
+
+ var insertType = 'separate';
+
+ // update insert options with selected values if method is called by clicking on 'insert' button
+ // in dialog
+ if (event) {
+ UiDialog.close(this._getInsertDialogId());
+
+ var dialogContent = event.currentTarget.closest('.dialogContent');
+
+ /*if (this._mediaToInsert.size > 1) {
+ insertType = elBySel('select[name=insertType]', dialogContent).value;
+ }*/
+ thumbnailSize = elBySel('select[name=thumbnailSize]', dialogContent).value;
+ }
+
+ if (this._options.callbackInsert !== null) {
+ this._options.callbackInsert(this._mediaToInsert, insertType, thumbnailSize);
+ }
+ else {
+ if (insertType === 'separate') {
+ this._options.editor.buffer.set();
+
+ this._mediaToInsert.forEach(this._insertMediaItem.bind(this, thumbnailSize));
+ }
+ else {
+ this._insertMediaGallery();
+ }
+ }
+
+ if (this._mediaToInsertByClipboard) {
+ var mediaIds = [];
+ this._mediaToInsert.forEach(function(media) {
+ mediaIds.push(media.mediaID);
+ });
+
+ ControllerClipboard.unmark('com.woltlab.wcf.media', mediaIds);
+ }
+
+ this._mediaToInsert = new Dictionary();
+ this._mediaToInsertByClipboard = false;
+
+ // close manager dialog
+ if (closeEditor) {
+ UiDialog.close(this);
+ }
+ },
+
+ /**
+ * Inserts a series of uploaded images using a slider.
+ *
+ * @protected
+ */
+ _insertMediaGallery: function() {
+ var mediaIds = [];
+ this._mediaToInsert.forEach(function(item) {
+ mediaIds.push(item.mediaID);
+ });
+
+ this._options.editor.buffer.set();
+ this._options.editor.insert.text("[wsmg='" + mediaIds.join(',') + "'][/wsmg]");
+ },
+
+ /**
+ * Inserts a single media item.
+ *
+ * @param {string} thumbnailSize preferred image dimension, is ignored for non-images
+ * @param {Object} item media item data
+ * @protected
+ */
+ _insertMediaItem: function(thumbnailSize, item) {
+ if (item.isImage) {
+ var sizes = ['small', 'medium', 'large', 'original'];
+
+ // check if size is actually available
+ var available = '', size;
+ for (var i = 0; i < 4; i++) {
+ size = sizes[i];
+
+ if (item[size + 'ThumbnailHeight'] != 0) {
+ available = size;
+
+ if (thumbnailSize == size) {
+ break;
+ }
+ }
+ }
+
+ thumbnailSize = available;
+
+ if (!thumbnailSize) thumbnailSize = 'original';
+
+ var link = item.link;
+ if (thumbnailSize !== 'original') {
+ link = item[thumbnailSize + 'ThumbnailLink'];
+ }
+
+ this._options.editor.insert.html('<img src="' + link + '" class="woltlabSuiteMedia" data-media-id="' + item.mediaID + '" data-media-size="' + thumbnailSize + '">');
+ }
+ else {
+ this._options.editor.insert.text("[wsm='" + item.mediaID + "'][/wsm]");
+ }
+ },
+
+ /**
+ * Is called after media files are successfully uploaded to insert copied media.
+ *
+ * @param {object} data upload data
+ */
+ _mediaUploaded: function(data) {
+ if (this._uploadId !== null && this._upload === data.upload) {
+ if (this._uploadId === data.uploadId || (Array.isArray(this._uploadId) && this._uploadId.indexOf(data.uploadId) !== -1)) {
+ this._mediaToInsert = Dictionary.fromObject(data.media);
+ this._insertMedia(null, 'medium', false);
+
+ this._uploadId = null;
+ }
+ }
+ },
+
+ /**
+ * Handles clicking on the insert button.
+ *
+ * @param {Event} event insert button click event
+ */
+ _openInsertDialog: function(event) {
+ this.insertMedia([~~elData(event.currentTarget, 'object-id')]);
+ },
+
+ /**
+ * Is called to insert the media files with the given ids into an editor.
+ *
+ * @param {int[]} mediaIds
+ */
+ clipboardInsertMedia: function(mediaIds) {
+ this.insertMedia(mediaIds, true);
+ },
+
+ /**
+ * Prepares insertion of the media files with the given ids.
+ *
+ * @param {array<int>} mediaIds ids of the media files to be inserted
+ * @param {boolean?} insertedByClipboard is true if the media files are inserted by clipboard
+ */
+ insertMedia: function(mediaIds, insertedByClipboard) {
+ this._mediaToInsert = new Dictionary();
+ this._mediaToInsertByClipboard = insertedByClipboard || false;
+
+ // open the insert dialog if all media files are images
+ var imagesOnly = true, media;
+ for (var i = 0, length = mediaIds.length; i < length; i++) {
+ media = this._media.get(mediaIds[i]);
+ this._mediaToInsert.set(media.mediaID, media);
+
+ if (!media.isImage) {
+ imagesOnly = false;
+ }
+ }
+
+ if (imagesOnly) {
+ var thumbnailSizes = this._getThumbnailSizes();
+ if (thumbnailSizes.length) {
+ UiDialog.close(this);
+ var dialogId = this._getInsertDialogId();
+ if (UiDialog.getDialog(dialogId)) {
+ UiDialog.openStatic(dialogId);
+ }
+ else {
+ this._buildInsertDialog();
+ }
+ }
+ else {
+ this._insertMedia(undefined, 'original');
+ }
+ }
+ else {
+ this._insertMedia();
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#getMode
+ */
+ getMode: function() {
+ return 'editor';
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#setupMediaElement
+ */
+ setupMediaElement: function(media, mediaElement) {
+ MediaManagerEditor._super.prototype.setupMediaElement.call(this, media, mediaElement);
+
+ // add media insertion icon
+ var buttons = elBySel('nav.buttonGroupNavigation > ul', mediaElement);
+
+ var listItem = elCreate('li');
+ listItem.className = 'jsMediaInsertButton';
+ elData(listItem, 'object-id', media.mediaID);
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-plus jsTooltip" title="' + Language.get('wcf.media.button.insert') + '"></span> <span class="invisible">' + Language.get('wcf.media.button.insert') + '</span></a>';
+ }
+ });
+
+ return MediaManagerEditor;
+});
+
+/**
+ * Provides the media manager dialog for selecting media for input elements.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Media/Manager/Select
+ */
+define('WoltLabSuite/Core/Media/Manager/Select',['Core', 'Dom/Traverse', 'Dom/Util', 'Language', 'ObjectMap', 'Ui/Dialog', 'WoltLabSuite/Core/FileUtil', 'WoltLabSuite/Core/Media/Manager/Base'],
+ function(Core, DomTraverse, DomUtil, Language, ObjectMap, UiDialog, FileUtil, MediaManagerBase) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _addButtonEventListeners: function() {},
+ _chooseMedia: function() {},
+ _click: function() {},
+ getMode: function() {},
+ setupMediaElement: function() {},
+ _removeMedia: function() {},
+ _clipboardAction: function() {},
+ _dialogClose: function() {},
+ _dialogInit: function() {},
+ _dialogSetup: function() {},
+ _dialogShow: function() {},
+ _editMedia: function() {},
+ _editorClose: function() {},
+ _editorSuccess: function() {},
+ _removeClipboardCheckboxes: function() {},
+ _setMedia: function() {},
+ addMedia: function() {},
+ getDialog: function() {},
+ getOption: function() {},
+ removeMedia: function() {},
+ resetMedia: function() {},
+ setMedia: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function MediaManagerSelect(options) {
+ MediaManagerBase.call(this, options);
+
+ this._activeButton = null;
+ this._buttons = elByClass(this._options.buttonClass || 'jsMediaSelectButton');
+ this._storeElements = new ObjectMap();
+
+ for (var i = 0, length = this._buttons.length; i < length; i++) {
+ var button = this._buttons[i];
+
+ // only consider buttons with a proper store specified
+ var store = elData(button, 'store');
+ if (store) {
+ var storeElement = elById(store);
+ if (storeElement && storeElement.tagName === 'INPUT') {
+ this._buttons[i].addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+
+ this._storeElements.set(button, storeElement);
+
+ // add remove button
+ var removeButton = elCreate('p');
+ removeButton.className = 'button';
+ DomUtil.insertAfter(removeButton, button);
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-times';
+ removeButton.appendChild(icon);
+
+ if (!storeElement.value) elHide(removeButton);
+ removeButton.addEventListener(WCF_CLICK_EVENT, this._removeMedia.bind(this));
+ }
+ }
+ }
+ }
+ Core.inherit(MediaManagerSelect, MediaManagerBase, {
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_addButtonEventListeners
+ */
+ _addButtonEventListeners: function() {
+ MediaManagerSelect._super.prototype._addButtonEventListeners.call(this);
+
+ if (!this._mediaManagerMediaList) return;
+
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ var listItem = listItems[i];
+
+ var chooseIcon = elByClass('jsMediaSelectButton', listItem)[0];
+ if (chooseIcon) {
+ chooseIcon.classList.remove('jsMediaSelectButton');
+ chooseIcon.addEventListener(WCF_CLICK_EVENT, this._chooseMedia.bind(this));
+ }
+ }
+ },
+
+ /**
+ * Handles clicking on a media choose icon.
+ *
+ * @param {Event} event click event
+ */
+ _chooseMedia: function(event) {
+ if (this._activeButton === null) {
+ throw new Error("Media cannot be chosen if no button is active.");
+ }
+
+ var media = this._media.get(~~elData(event.currentTarget, 'object-id'));
+
+ // save selected media in store element
+ elById(elData(this._activeButton, 'store')).value = media.mediaID;
+
+ // display selected media
+ var display = elData(this._activeButton, 'display');
+ if (display) {
+ var displayElement = elById(display);
+ if (displayElement) {
+ if (media.isImage) {
+ displayElement.innerHTML = '<img src="' + (media.smallThumbnailLink ? media.smallThumbnailLink : media.link) + '" alt="' + (media.altText && media.altText[LANGUAGE_ID] ? media.altText[LANGUAGE_ID] : '') + '" />';
+ }
+ else {
+ var fileIcon = FileUtil.getIconNameByFilename(media.filename);
+ if (fileIcon) {
+ fileIcon = '-' + fileIcon;
+ }
+
+ displayElement.innerHTML = '<div class="box48" style="margin-bottom: 10px;">'
+ + '<span class="icon icon48 fa-file' + fileIcon + '-o"></span>'
+ + '<div class="containerHeadline">'
+ + '<h3>' + media.filename + '</h3>'
+ + '<p>' + media.formattedFilesize + '</p>'
+ + '</div>'
+ + '</div>';
+ }
+ }
+ }
+
+ // show remove button
+ elShow(this._activeButton.nextElementSibling);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#_click
+ */
+ _click: function(event) {
+ event.preventDefault();
+ this._activeButton = event.currentTarget;
+
+ MediaManagerSelect._super.prototype._click.call(this, event);
+
+ if (!this._mediaManagerMediaList) return;
+
+ var storeElement = this._storeElements.get(this._activeButton);
+ var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI'), listItem;
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ listItem = listItems[i];
+ if (storeElement.value && storeElement.value == elData(listItem, 'object-id')) {
+ listItem.classList.add('jsSelected');
+ }
+ else {
+ listItem.classList.remove('jsSelected');
+ }
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#getMode
+ */
+ getMode: function() {
+ return 'select';
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Media/Manager/Base#setupMediaElement
+ */
+ setupMediaElement: function(media, mediaElement) {
+ MediaManagerSelect._super.prototype.setupMediaElement.call(this, media, mediaElement);
+
+ // add media insertion icon
+ var buttons = elBySel('nav.buttonGroupNavigation > ul', mediaElement);
+
+ var listItem = elCreate('li');
+ listItem.className = 'jsMediaSelectButton';
+ elData(listItem, 'object-id', media.mediaID);
+ buttons.appendChild(listItem);
+
+ listItem.innerHTML = '<a><span class="icon icon16 fa-check jsTooltip" title="' + Language.get('wcf.media.button.select') + '"></span> <span class="invisible">' + Language.get('wcf.media.button.select') + '</span></a>';
+ },
+
+ /**
+ * Handles clicking on the remove button.
+ *
+ * @param {Event} event click event
+ */
+ _removeMedia: function(event) {
+ event.preventDefault();
+
+ var removeButton = event.currentTarget;
+ elHide(removeButton);
+
+ var button = removeButton.previousElementSibling;
+ elById(elData(button, 'store')).value = 0;
+ var display = elData(button, 'display');
+ if (display) {
+ var displayElement = elById(display);
+ if (displayElement) {
+ displayElement.innerHTML = '';
+ }
+ }
+ }
+ });
+
+ return MediaManagerSelect;
+});
+
+/**
+ * Provides suggestions using an input field, designed to work with `wcf\data\ISearchAction`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Search/Input
+ */
+define('WoltLabSuite/Core/Ui/Search/Input',['Ajax', 'Core', 'EventKey', 'Dom/Util', 'Ui/SimpleDropdown'], function(Ajax, Core, EventKey, DomUtil, UiSimpleDropdown) {
+ "use strict";
+
+ /**
+ * @param {Element} element target input[type="text"]
+ * @param {Object} options search options and settings
+ * @constructor
+ */
+ function UiSearchInput(element, options) { this.init(element, options); }
+ UiSearchInput.prototype = {
+ /**
+ * Initializes the search input field.
+ *
+ * @param {Element} element target input[type="text"]
+ * @param {Object} options search options and settings
+ */
+ init: function(element, options) {
+ this._element = element;
+ if (!(this._element instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element.");
+ }
+ else if (this._element.nodeName !== 'INPUT' || (this._element.type !== 'search' && this._element.type !== 'text')) {
+ throw new Error('Expected an input[type="text"].');
+ }
+
+ this._activeItem = null;
+ this._dropdownContainerId = '';
+ this._lastValue = '';
+ this._list = null;
+ this._request = null;
+ this._timerDelay = null;
+
+ this._options = Core.extend({
+ ajax: {
+ actionName: 'getSearchResultList',
+ className: '',
+ interfaceName: 'wcf\\data\\ISearchAction'
+ },
+ callbackDropdownInit: null,
+ callbackSelect: null,
+ delay: 500,
+ excludedSearchValues: [],
+ minLength: 3,
+ noResultPlaceholder: '',
+ preventSubmit: false
+ }, options);
+
+ // disable auto-complete as it collides with the suggestion dropdown
+ elAttr(this._element, 'autocomplete', 'off');
+
+ this._element.addEventListener('keydown', this._keydown.bind(this));
+ this._element.addEventListener('keyup', this._keyup.bind(this));
+ },
+
+ /**
+ * Adds an excluded search value.
+ *
+ * @param {string} value excluded value
+ */
+ addExcludedSearchValues: function (value) {
+ if (this._options.excludedSearchValues.indexOf(value) === -1) {
+ this._options.excludedSearchValues.push(value);
+ }
+ },
+
+ /**
+ * Removes a value from the excluded search values.
+ *
+ * @param {string} value excluded value
+ */
+ removeExcludedSearchValues: function (value) {
+ var index = this._options.excludedSearchValues.indexOf(value);
+ if (index !== -1) {
+ this._options.excludedSearchValues.splice(index, 1);
+ }
+ },
+
+ /**
+ * Handles the 'keydown' event.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _keydown: function(event) {
+ if ((this._activeItem !== null && UiSimpleDropdown.isOpen(this._dropdownContainerId)) || this._options.preventSubmit) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+ }
+ }
+
+ if (EventKey.ArrowUp(event) || EventKey.ArrowDown(event) || EventKey.Escape(event)) {
+ event.preventDefault();
+ }
+ },
+
+ /**
+ * Handles the 'keyup' event, provides keyboard navigation and executes search queries.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _keyup: function(event) {
+ // handle dropdown keyboard navigation
+ if (this._activeItem !== null) {
+ if (UiSimpleDropdown.isOpen(this._dropdownContainerId)) {
+ if (EventKey.ArrowUp(event)) {
+ event.preventDefault();
+
+ return this._keyboardPreviousItem();
+ }
+ else if (EventKey.ArrowDown(event)) {
+ event.preventDefault();
+
+ return this._keyboardNextItem();
+ }
+ else if (EventKey.Enter(event)) {
+ event.preventDefault();
+
+ return this._keyboardSelectItem();
+ }
+ }
+ else {
+ this._activeItem = null;
+ }
+ }
+
+ // close list on escape
+ if (EventKey.Escape(event)) {
+ UiSimpleDropdown.close(this._dropdownContainerId);
+
+ return;
+ }
+
+ var value = this._element.value.trim();
+ if (this._lastValue === value) {
+ // value did not change, e.g. previously it was "Test" and now it is "Test ",
+ // but the trailing whitespace has been ignored
+ return;
+ }
+
+ this._lastValue = value;
+
+ if (value.length < this._options.minLength) {
+ if (this._dropdownContainerId) {
+ UiSimpleDropdown.close(this._dropdownContainerId);
+ this._activeItem = null;
+ }
+
+ // value below threshold
+ return;
+ }
+
+ if (this._options.delay) {
+ if (this._timerDelay !== null) {
+ window.clearTimeout(this._timerDelay);
+ }
+
+ this._timerDelay = window.setTimeout((function() {
+ this._search(value);
+ }).bind(this), this._options.delay);
+ }
+ else {
+ this._search(value);
+ }
+ },
+
+ /**
+ * Queries the server with the provided search string.
+ *
+ * @param {string} value search string
+ * @protected
+ */
+ _search: function(value) {
+ if (this._request) {
+ this._request.abortPrevious();
+ }
+
+ this._request = Ajax.api(this, this._getParameters(value));
+ },
+
+ /**
+ * Returns additional AJAX parameters.
+ *
+ * @param {string} value search string
+ * @return {Object} additional AJAX parameters
+ * @protected
+ */
+ _getParameters: function(value) {
+ return {
+ parameters: {
+ data: {
+ excludedSearchValues: this._options.excludedSearchValues,
+ searchString: value
+ }
+ }
+ };
+ },
+
+ /**
+ * Selects the next dropdown item.
+ *
+ * @protected
+ */
+ _keyboardNextItem: function() {
+ this._activeItem.classList.remove('active');
+
+ if (this._activeItem.nextElementSibling) {
+ this._activeItem = this._activeItem.nextElementSibling;
+ }
+ else {
+ this._activeItem = this._list.children[0];
+ }
+
+ this._activeItem.classList.add('active');
+ },
+
+ /**
+ * Selects the previous dropdown item.
+ *
+ * @protected
+ */
+ _keyboardPreviousItem: function() {
+ this._activeItem.classList.remove('active');
+
+ if (this._activeItem.previousElementSibling) {
+ this._activeItem = this._activeItem.previousElementSibling;
+ }
+ else {
+ this._activeItem = this._list.children[this._list.childElementCount - 1];
+ }
+
+ this._activeItem.classList.add('active');
+ },
+
+ /**
+ * Selects the active item from the dropdown.
+ *
+ * @protected
+ */
+ _keyboardSelectItem: function() {
+ this._selectItem(this._activeItem);
+ },
+
+ /**
+ * Selects an item from the dropdown by clicking it.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _clickSelectItem: function(event) {
+ this._selectItem(event.currentTarget);
+ },
+
+ /**
+ * Selects an item.
+ *
+ * @param {Element} item selected item
+ * @protected
+ */
+ _selectItem: function(item) {
+ if (this._options.callbackSelect && this._options.callbackSelect(item) === false) {
+ this._element.value = '';
+ }
+ else {
+ this._element.value = elData(item, 'label');
+ }
+
+ this._activeItem = null;
+ UiSimpleDropdown.close(this._dropdownContainerId);
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _ajaxSuccess: function(data) {
+ var createdList = false;
+ if (this._list === null) {
+ this._list = elCreate('ul');
+ this._list.className = 'dropdownMenu';
+
+ createdList = true;
+
+ if (typeof this._options.callbackDropdownInit === 'function') {
+ this._options.callbackDropdownInit(this._list);
+ }
+ }
+ else {
+ // reset current list
+ this._list.innerHTML = '';
+ }
+
+ if (typeof data.returnValues === 'object') {
+ var callbackClick = this._clickSelectItem.bind(this), listItem;
+
+ for (var key in data.returnValues) {
+ if (data.returnValues.hasOwnProperty(key)) {
+ listItem = this._createListItem(data.returnValues[key]);
+
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ this._list.appendChild(listItem);
+ }
+ }
+ }
+
+ if (createdList) {
+ DomUtil.insertAfter(this._list, this._element);
+ UiSimpleDropdown.initFragment(this._element.parentNode, this._list);
+
+ this._dropdownContainerId = DomUtil.identify(this._element.parentNode);
+ }
+
+ if (this._dropdownContainerId) {
+ this._activeItem = null;
+
+ if (!this._list.childElementCount && this._handleEmptyResult() === false) {
+ UiSimpleDropdown.close(this._dropdownContainerId);
+ }
+ else {
+ UiSimpleDropdown.open(this._dropdownContainerId, true);
+
+ // mark first item as active
+ if (this._list.childElementCount && ~~elData(this._list.children[0], 'object-id')) {
+ this._activeItem = this._list.children[0];
+ this._activeItem.classList.add('active');
+ }
+ }
+ }
+ },
+
+ /**
+ * Handles an empty result set, return a boolean false to hide the dropdown.
+ *
+ * @return {boolean} false to close the dropdown
+ * @protected
+ */
+ _handleEmptyResult: function() {
+ if (!this._options.noResultPlaceholder) {
+ return false;
+ }
+
+ var listItem = elCreate('li');
+ listItem.className = 'dropdownText';
+
+ var span = elCreate('span');
+ span.textContent = this._options.noResultPlaceholder;
+ listItem.appendChild(span);
+
+ this._list.appendChild(listItem);
+
+ return true;
+ },
+
+ /**
+ * Creates an list item from response data.
+ *
+ * @param {Object} item response data
+ * @return {Element} list item
+ * @protected
+ */
+ _createListItem: function(item) {
+ var listItem = elCreate('li');
+ elData(listItem, 'object-id', item.objectID);
+ elData(listItem, 'label', item.label);
+
+ var span = elCreate('span');
+ span.textContent = item.label;
+ listItem.appendChild(span);
+
+ return listItem;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: this._options.ajax
+ };
+ }
+ };
+
+ return UiSearchInput;
+});
+
+/**
+ * Provides suggestions for users, optionally supporting groups.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Search/Input
+ * @see module:WoltLabSuite/Core/Ui/Search/Input
+ */
+define('WoltLabSuite/Core/Ui/User/Search/Input',['Core', 'WoltLabSuite/Core/Ui/Search/Input'], function(Core, UiSearchInput) {
+ "use strict";
+
+ /**
+ * @param {Element} element input element
+ * @param {Object=} options search options and settings
+ * @constructor
+ */
+ function UiUserSearchInput(element, options) { this.init(element, options); }
+ Core.inherit(UiUserSearchInput, UiSearchInput, {
+ init: function(element, options) {
+ var includeUserGroups = (Core.isPlainObject(options) && options.includeUserGroups === true);
+
+ options = Core.extend({
+ ajax: {
+ className: 'wcf\\data\\user\\UserAction',
+ parameters: {
+ data: {
+ includeUserGroups: (includeUserGroups ? 1 : 0)
+ }
+ }
+ }
+ }, options);
+
+ UiUserSearchInput._super.prototype.init.call(this, element, options);
+ },
+
+ _createListItem: function(item) {
+ var listItem = UiUserSearchInput._super.prototype._createListItem.call(this, item);
+ elData(listItem, 'type', item.type);
+
+ var box = elCreate('div');
+ box.className = 'box16';
+ box.innerHTML = (item.type === 'group') ? '<span class="icon icon16 fa-users"></span>' : item.icon;
+ box.appendChild(listItem.children[0]);
+ listItem.appendChild(box);
+
+ return listItem;
+ }
+ });
+
+ return UiUserSearchInput;
+});
+
+define('WoltLabSuite/Core/Ui/Acl/Simple',['Language', 'StringUtil', 'Dom/ChangeListener', 'WoltLabSuite/Core/Ui/User/Search/Input'], function(Language, StringUtil, DomChangeListener, UiUserSearchInput) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _build: function() {},
+ _select: function() {},
+ _removeItem: function() {}
+ };
+ return Fake;
+ }
+
+ function UiAclSimple(prefix, inputName) { this.init(prefix, inputName); }
+ UiAclSimple.prototype = {
+ init: function(prefix, inputName) {
+ this._prefix = prefix || '';
+ this._inputName = inputName || 'aclValues';
+
+ this._build();
+ },
+
+ _build: function () {
+ var container = elById(this._prefix + 'aclInputContainer');
+
+ elById(this._prefix + 'aclAllowAll').addEventListener('change', (function() {
+ elHide(container);
+ }));
+ elById(this._prefix + 'aclAllowAll_no').addEventListener('change', (function() {
+ elShow(container);
+ }));
+
+ this._list = elById(this._prefix + 'aclAccessList');
+ this._list.addEventListener(WCF_CLICK_EVENT, this._removeItem.bind(this));
+
+ var excludedSearchValues = [];
+ elBySelAll('.aclLabel', this._list, function(label) {
+ excludedSearchValues.push(label.textContent);
+ });
+
+ this._searchInput = new UiUserSearchInput(elById(this._prefix + 'aclSearchInput'), {
+ callbackSelect: this._select.bind(this),
+ includeUserGroups: true,
+ excludedSearchValues: excludedSearchValues,
+ preventSubmit: true,
+ });
+
+ this._aclListContainer = elById(this._prefix + 'aclListContainer');
+
+ DomChangeListener.trigger();
+ },
+
+ _select: function(listItem) {
+ var type = elData(listItem, 'type');
+ var label = elData(listItem, 'label');
+
+ var html = '<span class="icon icon16 fa-' + (type === 'group' ? 'users' : 'user') + '"></span>';
+ html += '<span class="aclLabel">' + StringUtil.escapeHTML(label) + '</span>';
+ html += '<span class="icon icon16 fa-times pointer jsTooltip" title="' + Language.get('wcf.global.button.delete') + '"></span>';
+ html += '<input type="hidden" name="' + this._inputName + '[' + type + '][]" value="' + elData(listItem, 'object-id') + '">';
+
+ var item = elCreate('li');
+ item.innerHTML = html;
+
+ var firstUser = elBySel('.fa-user', this._list);
+ if (firstUser === null) {
+ this._list.appendChild(item);
+ }
+ else {
+ this._list.insertBefore(item, firstUser.parentNode);
+ }
+
+ elShow(this._aclListContainer);
+
+ this._searchInput.addExcludedSearchValues(label);
+
+ DomChangeListener.trigger();
+
+ return false;
+ },
+
+ _removeItem: function (event) {
+ if (event.target.classList.contains('fa-times')) {
+ var label = elBySel('.aclLabel', event.target.parentNode);
+ this._searchInput.removeExcludedSearchValues(label.textContent);
+
+ elRemove(event.target.parentNode);
+
+ if (this._list.childElementCount === 0) {
+ elHide(this._aclListContainer);
+ }
+ }
+ }
+ };
+
+ return UiAclSimple;
+});
+
+/**
+ * Handles the 'mark as read' action for articles.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Article/MarkAllAsRead
+ */
+define('WoltLabSuite/Core/Ui/Article/MarkAllAsRead',['Ajax'], function(Ajax) {
+ "use strict";
+
+ return {
+ init: function() {
+ elBySelAll('.markAllAsReadButton', undefined, (function(button) {
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }).bind(this));
+ },
+
+ _click: function(event) {
+ event.preventDefault();
+
+ Ajax.api(this);
+ },
+
+ _ajaxSuccess: function() {
+ /* remove obsolete badges */
+ // main menu
+ var badge = elBySel('.mainMenu .active .badge');
+ if (badge) elRemove(badge);
+
+ // article list
+ elBySelAll('.articleList .newMessageBadge', undefined, elRemove);
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'markAllAsRead',
+ className: 'wcf\\data\\article\\ArticleAction'
+ }
+ };
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Article/Search',['Ajax', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function(Ajax, EventKey, Language, StringUtil, DomUtil, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ open: function() {},
+ _search: function() {},
+ _click: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callbackSelect, _resultContainer, _resultList, _searchInput = null;
+
+ return {
+ open: function(callbackSelect) {
+ _callbackSelect = callbackSelect;
+
+ UiDialog.open(this);
+ },
+
+ _search: function (event) {
+ event.preventDefault();
+
+ var inputContainer = _searchInput.parentNode;
+
+ var value = _searchInput.value.trim();
+ if (value.length < 3) {
+ elInnerError(inputContainer, Language.get('wcf.article.search.error.tooShort'));
+ return;
+ }
+ else {
+ elInnerError(inputContainer, false);
+ }
+
+ Ajax.api(this, {
+ parameters: {
+ searchString: value
+ }
+ });
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ _callbackSelect(elData(event.currentTarget, 'article-id'));
+
+ UiDialog.close(this);
+ },
+
+ _ajaxSuccess: function(data) {
+ var html = '', article;
+ //noinspection JSUnresolvedVariable
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ article = data.returnValues[i];
+
+ html += '<li>'
+ + '<div class="containerHeadline pointer" data-article-id="' + article.articleID + '">'
+ + '<h3>' + StringUtil.escapeHTML(article.name) + '</h3>'
+ + '<small>' + StringUtil.escapeHTML(article.displayLink) + '</small>'
+ + '</div>'
+ + '</li>';
+ }
+
+ _resultList.innerHTML = html;
+
+ window[html ? 'elShow' : 'elHide'](_resultContainer);
+
+ if (html) {
+ elBySelAll('.containerHeadline', _resultList, (function(item) {
+ item.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }).bind(this));
+ }
+ else {
+ elInnerError(_searchInput.parentNode, Language.get('wcf.article.search.error.noResults'));
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ actionName: 'search',
+ className: 'wcf\\data\\article\\ArticleAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiArticleSearch',
+ options: {
+ onSetup: (function() {
+ var callbackSearch = this._search.bind(this);
+
+ _searchInput = elById('wcfUiArticleSearchInput');
+ _searchInput.addEventListener('keydown', function(event) {
+ if (EventKey.Enter(event)) {
+ callbackSearch(event);
+ }
+ });
+
+ _searchInput.nextElementSibling.addEventListener(WCF_CLICK_EVENT, callbackSearch);
+
+ _resultContainer = elById('wcfUiArticleSearchResultContainer');
+ _resultList = elById('wcfUiArticleSearchResultList');
+ }).bind(this),
+ onShow: function() {
+ _searchInput.focus();
+ },
+ title: Language.get('wcf.article.search')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiArticleSearchInput">' + Language.get('wcf.article.search.name') + '</label></dt>'
+ + '<dd>'
+ + '<div class="inputAddon">'
+ + '<input type="text" id="wcfUiArticleSearchInput" class="long">'
+ + '<a href="#" class="inputSuffix"><span class="icon icon16 fa-search"></span></a>'
+ + '</div>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<section id="wcfUiArticleSearchResultContainer" class="section" style="display: none;">'
+ + '<header class="sectionHeader">'
+ + '<h2 class="sectionTitle">' + Language.get('wcf.article.search.results') + '</h2>'
+ + '</header>'
+ + '<ol id="wcfUiArticleSearchResultList" class="containerList"></ol>'
+ + '</section>'
+ };
+ }
+ };
+});
+
+/**
+ * Wrapper class to provide color picker support. Constructing a new object does not
+ * guarantee the picker to be ready at the time of call.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Color/Picker
+ */
+define('WoltLabSuite/Core/Ui/Color/Picker',['Core'], function (Core) {
+ "use strict";
+
+ var _marshal = function (element, options) {
+ if (typeof window.WCF === 'object' && typeof window.WCF.ColorPicker === 'function') {
+ _marshal = function (element, options) {
+ var picker = new window.WCF.ColorPicker(element);
+
+ if (typeof options.callbackSubmit === 'function') {
+ picker.setCallbackSubmit(options.callbackSubmit);
+ }
+
+ return picker;
+ };
+
+ return _marshal(element, options);
+ }
+ else {
+ if (_queue.length === 0) {
+ window.__wcf_bc_colorPickerInit = function () {
+ _queue.forEach(function (data) {
+ _marshal(data[0], data[1]);
+ });
+
+ window.__wcf_bc_colorPickerInit = undefined;
+ _queue = [];
+ };
+ }
+
+ _queue.push([element, options]);
+ }
+ };
+ var _queue = [];
+
+ /**
+ * @constructor
+ */
+ function UiColorPicker(element, options) { this.init(element, options); }
+ UiColorPicker.prototype = {
+ /**
+ * Initializes a new color picker instance. This is actually just a wrapper that does
+ * not guarantee the picker to be ready at the time of call.
+ *
+ * @param {Element} element input element
+ * @param {Object} options list of initialization options
+ */
+ init: function (element, options) {
+ if (!(element instanceof Element)) {
+ throw new TypeError("Expected a valid DOM element, use `UiColorPicker.fromSelector()` if you want to use a CSS selector.");
+ }
+
+ this._options = Core.extend({
+ callbackSubmit: null
+ }, options);
+
+ _marshal(element, options);
+ }
+ };
+
+ /**
+ * Initializes a color picker for all input elements matching the given selector.
+ *
+ * @param {string} selector CSS selector
+ */
+ UiColorPicker.fromSelector = function (selector) {
+ elBySelAll(selector, undefined, function (element) {
+ new UiColorPicker(element);
+ });
+ };
+
+ return UiColorPicker;
+});
+
+/**
+ * Handles the comment add feature.
+ *
+ * Warning: This implementation is also used for responses, but in a slightly
+ * modified version. Changes made to this class need to be verified
+ * against the response implementation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Add
+ */
+define('WoltLabSuite/Core/Ui/Comment/Add',[
+ 'Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Scroll', 'EventKey', 'User', 'WoltLabSuite/Core/Controller/Captcha'
+],
+function(
+ Ajax, Core, EventHandler, Language, DomChangeListener, DomUtil, DomTraverse, UiDialog, UiNotification, UiScroll, EventKey, User, ControllerCaptcha
+) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _submitGuestDialog: function() {},
+ _submit: function() {},
+ _getParameters: function () {},
+ _validate: function() {},
+ throwError: function() {},
+ _showLoadingOverlay: function() {},
+ _hideLoadingOverlay: function() {},
+ _reset: function() {},
+ _handleError: function() {},
+ _getEditor: function() {},
+ _insertMessage: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSetup: function() {},
+ _cancelGuestDialog: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentAdd(container) { this.init(container); }
+ UiCommentAdd.prototype = {
+ /**
+ * Initializes a new quick reply field.
+ *
+ * @param {Element} container container element
+ */
+ init: function(container) {
+ this._container = container;
+ this._content = elBySel('.jsOuterEditorContainer', this._container);
+ this._textarea = elBySel('.wysiwygTextarea', this._container);
+ this._editor = null;
+ this._loadingOverlay = null;
+
+ this._content.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ if (this._content.classList.contains('collapsed')) {
+ event.preventDefault();
+
+ this._content.classList.remove('collapsed');
+
+ this._focusEditor();
+ }
+ }).bind(this));
+
+ // handle submit button
+ var submitButton = elBySel('button[data-type="save"]', this._container);
+ submitButton.addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ },
+
+ /**
+ * Scrolls the editor into view and sets the caret to the end of the editor.
+ *
+ * @protected
+ */
+ _focusEditor: function () {
+ UiScroll.element(this._container, (function () {
+ window.jQuery(this._textarea).redactor('WoltLabCaret.endOfEditor');
+ }).bind(this));
+ },
+
+ /**
+ * Submits the guest dialog.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _submitGuestDialog: function(event) {
+ // only submit when enter key is pressed
+ if (event.type === 'keypress' && !EventKey.Enter(event)) {
+ return;
+ }
+
+ var usernameInput = elBySel('input[name=username]', event.currentTarget.closest('.dialogContent'));
+ if (usernameInput.value === '') {
+ elInnerError(usernameInput, Language.get('wcf.global.form.error.empty'));
+ usernameInput.closest('dl').classList.add('formError');
+
+ return;
+ }
+
+ var parameters = {
+ parameters: {
+ data: {
+ username: usernameInput.value
+ }
+ }
+ };
+
+ if (ControllerCaptcha.has('commentAdd')) {
+ var data = ControllerCaptcha.getData('commentAdd');
+ if (data instanceof Promise) {
+ data.then((function (data) {
+ parameters = Core.extend(parameters, data);
+ this._submit(undefined, parameters);
+ }).bind(this));
+ }
+ else {
+ parameters = Core.extend(parameters, data);
+ this._submit(undefined, parameters);
+ }
+ }
+ else {
+ this._submit(undefined, parameters);
+ }
+ },
+
+ /**
+ * Validates the message and submits it to the server.
+ *
+ * @param {Event?} event event object
+ * @param {Object?} additionalParameters additional parameters sent to the server
+ * @protected
+ */
+ _submit: function(event, additionalParameters) {
+ if (event) {
+ event.preventDefault();
+ }
+
+ if (!this._validate()) {
+ // validation failed, bail out
+ return;
+ }
+
+ this._showLoadingOverlay();
+
+ // build parameters
+ var parameters = this._getParameters();
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_text', parameters.data);
+
+ if (!User.userId && !additionalParameters) {
+ parameters.requireGuestDialog = true;
+ }
+
+ Ajax.api(this, Core.extend({
+ parameters: parameters
+ }, additionalParameters));
+ },
+
+ /**
+ * Returns the request parameters to add a comment.
+ *
+ * @return {{data: {message: string, objectID: number, objectTypeID: number}}}
+ * @protected
+ */
+ _getParameters: function () {
+ var commentList = this._container.closest('.commentList');
+
+ return {
+ data: {
+ message: this._getEditor().code.get(),
+ objectID: ~~elData(commentList, 'object-id'),
+ objectTypeID: ~~elData(commentList, 'object-type-id')
+ }
+ };
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function() {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._container, elRemove);
+
+ // check if editor contains actual content
+ if (this._getEditor().utils.isEmpty()) {
+ this.throwError(this._textarea, Language.get('wcf.global.form.error.empty'));
+ return false;
+ }
+
+ var data = {
+ api: this,
+ editor: this._getEditor(),
+ message: this._getEditor().code.get(),
+ valid: true
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_text', data);
+
+ return (data.valid !== false);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, (message === 'empty' ? Language.get('wcf.global.form.error.empty') : message));
+ },
+
+ /**
+ * Displays a loading spinner while the request is processed by the server.
+ *
+ * @protected
+ */
+ _showLoadingOverlay: function() {
+ if (this._loadingOverlay === null) {
+ this._loadingOverlay = elCreate('div');
+ this._loadingOverlay.className = 'commentLoadingOverlay';
+ this._loadingOverlay.innerHTML = '<span class="icon icon96 fa-spinner"></span>';
+ }
+
+ this._content.classList.add('loading');
+ this._content.appendChild(this._loadingOverlay);
+ },
+
+ /**
+ * Hides the loading spinner.
+ *
+ * @protected
+ */
+ _hideLoadingOverlay: function() {
+ this._content.classList.remove('loading');
+
+ var loadingOverlay = elBySel('.commentLoadingOverlay', this._content);
+ if (loadingOverlay !== null) {
+ loadingOverlay.parentNode.removeChild(loadingOverlay);
+ }
+ },
+
+ /**
+ * Resets the editor contents and notifies event listeners.
+ *
+ * @protected
+ */
+ _reset: function() {
+ this._getEditor().code.set('<p>\u200b</p>');
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'reset_text');
+
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+
+ this._content.classList.add('collapsed');
+ },
+
+ /**
+ * Handles errors occurred during server processing.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _handleError: function(data) {
+ //noinspection JSUnresolvedVariable
+ this.throwError(this._textarea, data.returnValues.errorType);
+ },
+
+ /**
+ * Returns the current editor instance.
+ *
+ * @return {Object} editor instance
+ * @protected
+ */
+ _getEditor: function() {
+ if (this._editor === null) {
+ if (typeof window.jQuery === 'function') {
+ this._editor = window.jQuery(this._textarea).data('redactor');
+ }
+ else {
+ throw new Error("Unable to access editor, jQuery has not been loaded yet.");
+ }
+ }
+
+ return this._editor;
+ },
+
+ /**
+ * Inserts the rendered message.
+ *
+ * @param {Object} data response data
+ * @return {Element} scroll target
+ * @protected
+ */
+ _insertMessage: function(data) {
+ // insert HTML
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.insertHtml(data.returnValues.template, this._container, 'after');
+
+ UiNotification.show(Language.get('wcf.global.success.add'));
+
+ DomChangeListener.trigger();
+
+ return this._container.nextElementSibling;
+ },
+
+ /**
+ * @param {{returnValues:{guestDialog:string}}} data
+ * @protected
+ */
+ _ajaxSuccess: function(data) {
+ if (!User.userId && data.returnValues.guestDialog) {
+ UiDialog.openStatic('jsDialogGuestComment', data.returnValues.guestDialog, {
+ closable: false,
+ onClose: function() {
+ if (ControllerCaptcha.has('commentAdd')) {
+ ControllerCaptcha.delete('commentAdd');
+ }
+ },
+ title: Language.get('wcf.global.confirmation.title')
+ });
+
+ var dialog = UiDialog.getDialog('jsDialogGuestComment');
+ elBySel('input[type=submit]', dialog.content).addEventListener(WCF_CLICK_EVENT, this._submitGuestDialog.bind(this));
+ elBySel('button[data-type="cancel"]', dialog.content).addEventListener(WCF_CLICK_EVENT, this._cancelGuestDialog.bind(this));
+ elBySel('input[type=text]', dialog.content).addEventListener('keypress', this._submitGuestDialog.bind(this));
+ }
+ else {
+ var scrollTarget = this._insertMessage(data);
+
+ if (!User.userId) {
+ UiDialog.close('jsDialogGuestComment');
+ }
+
+ this._reset();
+
+ this._hideLoadingOverlay();
+
+ window.setTimeout((function () {
+ UiScroll.element(scrollTarget);
+ }).bind(this), 100);
+ }
+ },
+
+ _ajaxFailure: function(data) {
+ this._hideLoadingOverlay();
+
+ //noinspection JSUnresolvedVariable
+ if (data === null || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+ return true;
+ }
+
+ this._handleError(data);
+
+ return false;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'addComment',
+ className: 'wcf\\data\\comment\\CommentAction'
+ },
+ silent: true
+ };
+ },
+
+ /**
+ * Cancels the guest dialog and restores the comment editor.
+ */
+ _cancelGuestDialog: function() {
+ UiDialog.close('jsDialogGuestComment');
+
+ this._hideLoadingOverlay();
+ }
+ };
+
+ return UiCommentAdd;
+});
+
+/**
+ * Provides editing support for comments.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Edit
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Comment/Edit',[
+ 'Ajax', 'Core', 'Dictionary', 'Environment',
+ 'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll'
+ ],
+ function(
+ Ajax, Core, Dictionary, Environment,
+ EventHandler, Language, List, DomChangeListener, DomTraverse,
+ DomUtil, UiNotification, UiReusableDropdown, UiScroll
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ _click: function() {},
+ _prepare: function() {},
+ _showEditor: function() {},
+ _restoreMessage: function() {},
+ _save: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showMessage: function() {},
+ _hideEditor: function() {},
+ _restoreEditor: function() {},
+ _destroyEditor: function() {},
+ _getEditorId: function() {},
+ _getObjectId: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentEdit(container) { this.init(container); }
+ UiCommentEdit.prototype = {
+ /**
+ * Initializes the comment edit manager.
+ *
+ * @param {Element} container container element
+ */
+ init: function(container) {
+ this._activeElement = null;
+ this._callbackClick = null;
+ this._comments = new List();
+ this._container = container;
+ this._editorContainer = null;
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Comment/Edit_' + DomUtil.identify(this._container), this.rebuild.bind(this));
+ },
+
+ /**
+ * Initializes each applicable message, should be called whenever new
+ * messages are being displayed.
+ */
+ rebuild: function() {
+ elBySelAll('.comment', this._container, (function (comment) {
+ if (this._comments.has(comment)) {
+ return;
+ }
+
+ if (elDataBool(comment, 'can-edit')) {
+ var button = elBySel('.jsCommentEditButton', comment);
+ if (button !== null) {
+ if (this._callbackClick === null) {
+ this._callbackClick = this._click.bind(this);
+ }
+
+ button.addEventListener(WCF_CLICK_EVENT, this._callbackClick);
+ }
+ }
+
+ this._comments.add(comment);
+ }).bind(this));
+ },
+
+ /**
+ * Handles clicks on the edit button.
+ *
+ * @param {?Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ if (this._activeElement === null) {
+ this._activeElement = event.currentTarget.closest('.comment');
+
+ this._prepare();
+
+ Ajax.api(this, {
+ actionName: 'beginEdit',
+ objectIDs: [this._getObjectId(this._activeElement)]
+ });
+ }
+ else {
+ UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+ }
+ },
+
+ /**
+ * Prepares the message for editor display.
+ *
+ * @protected
+ */
+ _prepare: function() {
+ this._editorContainer = elCreate('div');
+ this._editorContainer.className = 'commentEditorContainer';
+ this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
+
+ var content = elBySel('.commentContentContainer', this._activeElement);
+ content.insertBefore(this._editorContainer, content.firstChild);
+ },
+
+ /**
+ * Shows the message editor.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showEditor: function(data) {
+ var id = this._getEditorId();
+
+ var icon = elBySel('.icon', this._editorContainer);
+ elRemove(icon);
+
+ var editor = elCreate('div');
+ editor.className = 'editorContainer';
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(editor, data.returnValues.template);
+ this._editorContainer.appendChild(editor);
+
+ // bind buttons
+ var formSubmit = elBySel('.formSubmit', editor);
+
+ var buttonSave = elBySel('button[data-type="save"]', formSubmit);
+ buttonSave.addEventListener(WCF_CLICK_EVENT, this._save.bind(this));
+
+ var buttonCancel = elBySel('button[data-type="cancel"]', formSubmit);
+ buttonCancel.addEventListener(WCF_CLICK_EVENT, this._restoreMessage.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.redactor', 'submitEditor_' + id, (function(data) {
+ data.cancel = true;
+
+ this._save();
+ }).bind(this));
+
+ var editorElement = elById(id);
+ if (Environment.editor() === 'redactor') {
+ window.setTimeout((function() {
+ UiScroll.element(this._activeElement);
+ }).bind(this), 250);
+ }
+ else {
+ editorElement.focus();
+ }
+ },
+
+ /**
+ * Restores the message view.
+ *
+ * @protected
+ */
+ _restoreMessage: function() {
+ this._destroyEditor();
+
+ elRemove(this._editorContainer);
+
+ this._activeElement = null;
+ },
+
+ /**
+ * Saves the editor message.
+ *
+ * @protected
+ */
+ _save: function() {
+ var parameters = {
+ data: {
+ message: ''
+ }
+ };
+
+ var id = this._getEditorId();
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'getText_' + id, parameters.data);
+
+ if (!this._validate(parameters)) {
+ // validation failed
+ return;
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_' + id, parameters);
+
+ Ajax.api(this, {
+ actionName: 'save',
+ objectIDs: [this._getObjectId(this._activeElement)],
+ parameters: parameters
+ });
+
+ this._hideEditor();
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @param {Object} parameters request parameters
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function(parameters) {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._activeElement, elRemove);
+
+ // check if editor contains actual content
+ var editorElement = elById(this._getEditorId());
+ if (window.jQuery(editorElement).data('redactor').utils.isEmpty()) {
+ this.throwError(editorElement, Language.get('wcf.global.form.error.empty'));
+ return false;
+ }
+
+ var data = {
+ api: this,
+ parameters: parameters,
+ valid: true
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_' + this._getEditorId(), data);
+
+ return (data.valid !== false);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, message);
+ },
+
+ /**
+ * Shows the update message.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showMessage: function(data) {
+ // set new content
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.setInnerHtml(elBySel('.commentContent .userMessage', this._editorContainer.parentNode), data.returnValues.message);
+
+ this._restoreMessage();
+
+ UiNotification.show();
+ },
+
+ /**
+ * Hides the editor from view.
+ *
+ * @protected
+ */
+ _hideEditor: function() {
+ elHide(elBySel('.editorContainer', this._editorContainer));
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ this._editorContainer.appendChild(icon);
+ },
+
+ /**
+ * Restores the previously hidden editor.
+ *
+ * @protected
+ */
+ _restoreEditor: function() {
+ var icon = elBySel('.fa-spinner', this._editorContainer);
+ elRemove(icon);
+
+ var editorContainer = elBySel('.editorContainer', this._editorContainer);
+ if (editorContainer !== null) elShow(editorContainer);
+ },
+
+ /**
+ * Destroys the editor instance.
+ *
+ * @protected
+ */
+ _destroyEditor: function() {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveDestroy_' + this._getEditorId());
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'destroy_' + this._getEditorId());
+ },
+
+ /**
+ * Returns the unique editor id.
+ *
+ * @return {string} editor id
+ * @protected
+ */
+ _getEditorId: function() {
+ return 'commentEditor' + this._getObjectId(this._activeElement);
+ },
+
+ /**
+ * Returns the element's `data-object-id` value.
+ *
+ * @param {Element} element target element
+ * @return {int}
+ * @protected
+ */
+ _getObjectId: function(element) {
+ return ~~elData(element, 'object-id');
+ },
+
+ _ajaxFailure: function(data) {
+ var editor = elBySel('.redactor-layer', this._editorContainer);
+
+ // handle errors occurring on editor load
+ if (editor === null) {
+ this._restoreMessage();
+
+ return true;
+ }
+
+ this._restoreEditor();
+
+ //noinspection JSUnresolvedVariable
+ if (!data || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+ return true;
+ }
+
+ //noinspection JSUnresolvedVariable
+ elInnerError(editor, data.returnValues.errorType);
+
+ return false;
+ },
+
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'beginEdit':
+ this._showEditor(data);
+ break;
+
+ case 'save':
+ this._showMessage(data);
+ break;
+ }
+ },
+
+ _ajaxSetup: function() {
+ var objectTypeId = ~~elData(this._container, 'object-type-id');
+
+ return {
+ data: {
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: {
+ objectTypeID: objectTypeId
+ }
+ }
+ },
+ silent: true
+ };
+ }
+ };
+
+ return UiCommentEdit;
+});
+
+/**
+ * Simplified and consistent dropdown creation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Dropdown/Builder
+ */
+define('WoltLabSuite/Core/Ui/Dropdown/Builder',['Core', 'Ui/SimpleDropdown'], function (Core, UiSimpleDropdown) {
+ "use strict";
+
+ var _validIconSizes = [16, 24, 32, 48, 64, 96, 144];
+
+ function _validateList(list) {
+ if (!(list instanceof HTMLUListElement)) {
+ throw new TypeError('Expected a reference to an <ul> element.');
+ }
+
+ if (!list.classList.contains('dropdownMenu')) {
+ throw new Error('List does not appear to be a dropdown menu.');
+ }
+ }
+
+ function _buildItem(data) {
+ var item = elCreate('li');
+
+ // handle special `divider` type
+ if (data === 'divider') {
+ item.className = 'dropdownDivider';
+ return item;
+ }
+
+ if (typeof data.identifier === 'string') {
+ elData(item, 'identifier', data.identifier);
+ }
+
+ var link = elCreate('a');
+ link.href = (typeof data.href === 'string') ? data.href : '#';
+ if (typeof data.callback === 'function') {
+ link.addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+
+ data.callback(link);
+ });
+ }
+ else if (link.getAttribute('href') === '#') {
+ throw new Error('Expected either a `href` value or a `callback`.');
+ }
+
+ if (data.hasOwnProperty('attributes') && Core.isPlainObject(data.attributes)) {
+ for (var key in data.attributes) {
+ if (data.attributes.hasOwnProperty(key)) {
+ elData(link, key, data.attributes[key]);
+ }
+ }
+ }
+
+ item.appendChild(link);
+
+ if (typeof data.icon !== 'undefined' && Core.isPlainObject(data.icon)) {
+ if (typeof data.icon.name !== 'string') {
+ throw new TypeError('Expected a valid icon name.');
+ }
+
+ var size = 16;
+ if (typeof data.icon.size === 'number' && _validIconSizes.indexOf(~~data.icon.size) !== -1) {
+ size = ~~data.icon.size;
+ }
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon' + size + ' fa-' + data.icon.name;
+
+ link.appendChild(icon);
+ }
+
+ var label = (typeof data.label === 'string') ? data.label.trim() : '';
+ var labelHtml = (typeof data.labelHtml === 'string') ? data.labelHtml.trim() : '';
+ if (label === '' && labelHtml === '') {
+ throw new TypeError('Expected either a label or a `labelHtml`.');
+ }
+
+ var span = elCreate('span');
+ span[label ? 'textContent' : 'innerHTML'] = (label) ? label : labelHtml;
+ link.appendChild(document.createTextNode(' '));
+ link.appendChild(span);
+
+ return item;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Dropdown/Builder
+ */
+ return {
+ /**
+ * Creates a new dropdown menu, optionally pre-populated with the supplied list of
+ * dropdown items. The list element will be returned and must be manually injected
+ * into the DOM by the callee.
+ *
+ * @param {(Object|string)[]} items
+ * @param {string?} identifier
+ * @return {Element}
+ */
+ create: function (items, identifier) {
+ var list = elCreate('ul');
+ list.className = 'dropdownMenu';
+ if (typeof identifier === 'string') {
+ elData(list, 'identifier', identifier);
+ }
+
+ if (Array.isArray(items) && items.length > 0) {
+ this.appendItems(list, items);
+ }
+
+ return list;
+ },
+
+ /**
+ * Creates a new dropdown item that can be inserted into lists using regular DOM operations.
+ *
+ * @param {(Object|string)} item
+ * @return {Element}
+ */
+ buildItem: function (item) {
+ return _buildItem(item);
+ },
+
+ /**
+ * Appends a single item to the target list.
+ *
+ * @param {Element} list
+ * @param {(Object|string)} item
+ */
+ appendItem: function (list, item) {
+ _validateList(list);
+
+ list.appendChild(_buildItem(item));
+ },
+
+ /**
+ * Appends a list of items to the target list.
+ *
+ * @param {Element} list
+ * @param {(Object|string)[]} items
+ */
+ appendItems: function (list, items) {
+ _validateList(list);
+
+ if (!Array.isArray(items)) {
+ throw new TypeError('Expected an array of items.');
+ }
+
+ var length = items.length;
+ if (length === 0) {
+ throw new Error('Expected a non-empty list of items.');
+ }
+
+ if (length === 1) {
+ this.appendItem(list, items[0]);
+ }
+ else {
+ var fragment = document.createDocumentFragment();
+ for (var i = 0; i < length; i++) {
+ fragment.appendChild(_buildItem(items[i]));
+ }
+ list.appendChild(fragment);
+ }
+ },
+
+ /**
+ * Replaces the existing list items with the provided list of new items.
+ *
+ * @param {Element} list
+ * @param {(Object|string)[]} items
+ */
+ setItems: function (list, items) {
+ _validateList(list);
+
+ list.innerHTML = '';
+
+ this.appendItems(list, items);
+ },
+
+ /**
+ * Attaches the list to a button, visibility is from then on controlled through clicks
+ * on the provided button element. Internally calls `Ui/SimpleDropdown.initFragment()`
+ * to delegate the DOM management.
+ *
+ * @param {Element} list
+ * @param {Element} button
+ */
+ attach: function (list, button) {
+ _validateList(list);
+
+ UiSimpleDropdown.initFragment(button, list);
+
+ button.addEventListener(WCF_CLICK_EVENT, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ UiSimpleDropdown.toggleDropdown(button.id);
+ });
+ },
+
+ /**
+ * Helper method that returns the special string `"divider"` that causes a divider to
+ * be created.
+ *
+ * @return {string}
+ */
+ divider: function () {
+ return 'divider';
+ }
+ };
+});
+
+/**
+ * Delete files which are uploaded via AJAX.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/File/Delete
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/File/Delete',['Ajax', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util', 'Dom/Traverse', 'Dictionary'], function(Ajax, Core, DomChangeListener, Language, DomUtil, DomTraverse, Dictionary) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Delete(buttonContainerId, targetId, isSingleImagePreview, uploadHandler) {
+ this._isSingleImagePreview = isSingleImagePreview;
+ this._uploadHandler = uploadHandler;
+
+ this._buttonContainer = elById(buttonContainerId);
+ if (this._buttonContainer === null) {
+ throw new Error("Element id '" + buttonContainerId + "' is unknown.");
+ }
+
+ this._target = elById(targetId);
+ if (targetId === null) {
+ throw new Error("Element id '" + targetId + "' is unknown.");
+ }
+ this._containers = new Dictionary();
+
+ this._internalId = elData(this._target, 'internal-id');
+
+ if (!this._internalId) {
+ throw new Error("InternalId is unknown.");
+ }
+
+ this.rebuild();
+ }
+
+ Delete.prototype = {
+ /**
+ * Creates the upload button.
+ */
+ _createButtons: function() {
+ var element, elements = elBySelAll('li.uploadedFile', this._target), elementData, triggerChange = false, uniqueFileId;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ uniqueFileId = elData(element, 'unique-file-id');
+ if (this._containers.has(uniqueFileId)) {
+ continue;
+ }
+
+ elementData = {
+ uniqueFileId: uniqueFileId,
+ element: element
+ };
+
+ this._containers.set(uniqueFileId, elementData);
+ this._initDeleteButton(element, elementData);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Init the delete button for a specific element.
+ *
+ * @param {HTMLElement} element
+ * @param {string} elementData
+ */
+ _initDeleteButton: function(element, elementData) {
+ var buttonGroup = elBySel('.buttonGroup', element);
+
+ if (buttonGroup === null) {
+ throw new Error("Button group in '" + targetId + "' is unknown.");
+ }
+
+ var li = elCreate('li');
+ var span = elCreate('span');
+ span.classList = "button jsDeleteButton small";
+ span.textContent = Language.get('wcf.global.button.delete');
+ li.appendChild(span);
+ buttonGroup.appendChild(li);
+
+ li.addEventListener(WCF_CLICK_EVENT, this._delete.bind(this, elementData.uniqueFileId));
+ },
+
+ /**
+ * Delete a specific file with the given uniqueFileId.
+ *
+ * @param {string} uniqueFileId
+ */
+ _delete: function(uniqueFileId) {
+ Ajax.api(this, {
+ uniqueFileId: uniqueFileId,
+ internalId: this._internalId
+ });
+ },
+
+ /**
+ * Rebuilds the delete buttons for unknown files.
+ */
+ rebuild: function() {
+ if (this._isSingleImagePreview) {
+ var img = elBySel('img', this._target);
+
+ if (img !== null) {
+ var uniqueFileId = elData(img, 'unique-file-id');
+
+ if (!this._containers.has(uniqueFileId)) {
+ var elementData = {
+ uniqueFileId: uniqueFileId,
+ element: img
+ };
+
+ this._containers.set(uniqueFileId, elementData);
+
+ this._deleteButton = elCreate('p');
+ this._deleteButton.className = 'button deleteButton';
+
+ var span = elCreate('span');
+ span.textContent = Language.get('wcf.global.button.delete');
+ this._deleteButton.appendChild(span);
+
+ this._buttonContainer.appendChild(this._deleteButton);
+
+ this._deleteButton.addEventListener(WCF_CLICK_EVENT, this._delete.bind(this, elementData.uniqueFileId));
+ }
+ }
+ }
+ else {
+ this._createButtons();
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ elRemove(this._containers.get(data.uniqueFileId).element);
+
+ if (this._isSingleImagePreview) {
+ elRemove(this._deleteButton);
+ this._deleteButton = null;
+ }
+
+ this._uploadHandler.checkMaxFiles();
+ },
+
+ _ajaxSetup: function () {
+ return {
+ url: 'index.php?ajax-file-delete/&t=' + SECURITY_TOKEN
+ };
+ }
+ };
+
+ return Delete;
+});
+
+/**
+ * Uploads file via AJAX.
+ *
+ * @author Joshua Ruesweg, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/File/Upload
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/File/Upload',['Core', 'Language', 'Dom/Util', 'WoltLabSuite/Core/Ui/File/Delete', 'Upload'], function(Core, Language, DomUtil, DeleteHandler, CoreUpload) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Upload(buttonContainerId, targetId, options) {
+ options = options || {};
+
+ if (options.internalId === undefined) {
+ throw new Error("Missing internal id.");
+ }
+
+ // set default options
+ this._options = Core.extend({
+ // name if the upload field
+ name: '__files[]',
+ // is true if every file from a multi-file selection is uploaded in its own request
+ singleFileRequests: false,
+ // url for uploading file
+ url: 'index.php?ajax-file-upload/&t=' + SECURITY_TOKEN,
+ // image preview
+ imagePreview: false,
+ // max files
+ maxFiles: null
+ }, options);
+
+ this._options.multiple = this._options.maxFiles === null || this._options.maxFiles > 1;
+
+ this._options.url = this._options.url;
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
+ }
+
+ this._buttonContainer = elById(buttonContainerId);
+ if (this._buttonContainer === null) {
+ throw new Error("Element id '" + buttonContainerId + "' is unknown.");
+ }
+
+ this._target = elById(targetId);
+ if (targetId === null) {
+ throw new Error("Element id '" + targetId + "' is unknown.");
+ }
+
+ if (options.multiple && this._target.nodeName !== 'UL' && this._target.nodeName !== 'OL') {
+ throw new Error("Target element has to be list or table body if uploading multiple files is supported.");
+ }
+
+ this._fileElements = [];
+ this._internalFileId = 0;
+
+ // upload ids that belong to an upload of multiple files at once
+ this._multiFileUploadIds = [];
+
+ this._createButton();
+ this.checkMaxFiles();
+
+ this._deleteHandler = new DeleteHandler(buttonContainerId, targetId, this._options.imagePreview, this);
+ }
+
+ Core.inherit(Upload, CoreUpload, {
+ _createFileElement: function(file) {
+ var element = Upload._super.prototype._createFileElement.call(this, file);
+ element.classList.add('box64', 'uploadedFile');
+
+ var progress = elBySel('progress', element);
+
+ var icon = elCreate('span');
+ icon.classList = 'icon icon64 fa-spinner';
+
+ var fileName = element.textContent;
+ element.textContent = "";
+ element.append(icon);
+
+ var innerDiv = elCreate('div');
+ var fileNameP = elCreate('p');
+ fileNameP.textContent = fileName; // file.name
+
+ var smallProgress = elCreate('small');
+ smallProgress.appendChild(progress);
+
+ innerDiv.appendChild(fileNameP);
+ innerDiv.appendChild(smallProgress);
+
+ var div = elCreate('div');
+ div.appendChild(innerDiv);
+
+ var ul = elCreate('ul');
+ ul.classList = 'buttonGroup';
+ div.appendChild(ul);
+
+ // reset element textContent and replace with own element style
+ element.append(div);
+
+ return element;
+ },
+
+ _failure: function(uploadId, data, responseText, xhr, requestOptions) {
+ for (var i = 0, length = this._fileElements[uploadId].length; i < length; i++) {
+ this._fileElements[uploadId][i].classList.add('uploadFailed');
+
+ elBySel('small', this._fileElements[uploadId][i]).innerHTML = '';
+ var icon = elBySel('.icon', this._fileElements[uploadId][i]);
+ icon.classList.remove('fa-spinner');
+ icon.classList.add('fa-ban');
+
+ var innerError = elCreate('span');
+ innerError.classList = 'innerError';
+ innerError.textContent = Language.get('wcf.upload.error.uploadFailed');
+ DomUtil.insertAfter(innerError, elBySel('small', this._fileElements[uploadId][i]));
+ }
+
+ throw new Error("Upload failed: " + data.message);
+ },
+
+ _upload: function(event, file, blob) {
+ var innerError = elBySel('small.innerError:not(.innerFileError)', this._buttonContainer.parentNode);
+ if (innerError) elRemove(innerError);
+
+ return Upload._super.prototype._upload.call(this, event, file, blob);
+ },
+
+ _success: function(uploadId, data, responseText, xhr, requestOptions) {
+ for (var i = 0, length = this._fileElements[uploadId].length; i < length; i++) {
+ if (data['files'][i] !== undefined) {
+ if (this._options.imagePreview) {
+ if (data['files'][i].image === null) {
+ throw new Error("Expect image for uploaded file. None given.");
+ }
+
+ elRemove(this._fileElements[uploadId][i]);
+
+ if (elBySel('img.previewImage', this._target) !== null) {
+ elBySel('img.previewImage', this._target).setAttribute('src', data['files'][i].image);
+ }
+ else {
+ var image = elCreate('img');
+ image.classList.add('previewImage');
+ image.setAttribute('src', data['files'][i].image);
+ image.setAttribute('style', "max-width: 100%;");
+ elData(image, 'unique-file-id', data['files'][i].uniqueFileId);
+ this._target.appendChild(image);
+ }
+ }
+ else {
+ elData(this._fileElements[uploadId][i], 'unique-file-id', data['files'][i].uniqueFileId);
+ elBySel('small', this._fileElements[uploadId][i]).textContent = data['files'][i].filesize;
+ var icon = elBySel('.icon', this._fileElements[uploadId][i]);
+ icon.classList.remove('fa-spinner');
+ icon.classList.add('fa-' + data['files'][i].icon);
+ }
+ }
+ else if (data['error'][i] !== undefined) {
+ this._fileElements[uploadId][i].classList.add('uploadFailed');
+
+ elBySel('small', this._fileElements[uploadId][i]).innerHTML = '';
+ var icon = elBySel('.icon', this._fileElements[uploadId][i]);
+ icon.classList.remove('fa-spinner');
+ icon.classList.add('fa-ban');
+
+ if (elBySel('.innerError', this._fileElements[uploadId][i]) === null) {
+ var innerError = elCreate('span');
+ innerError.classList = 'innerError';
+ innerError.textContent = data['error'][i].errorMessage;
+ DomUtil.insertAfter(innerError, elBySel('small', this._fileElements[uploadId][i]));
+ }
+ else {
+ elBySel('.innerError', this._fileElements[uploadId][i]).textContent = data['error'][i].errorMessage;
+ }
+ }
+ else {
+ throw new Error('Unknown uploaded file for uploadId ' + uploadId + '.');
+ }
+ }
+
+ // create delete buttons
+ this._deleteHandler.rebuild();
+ this.checkMaxFiles();
+ },
+
+ _getFormData: function() {
+ return {
+ internalId: this._options.internalId
+ };
+ },
+
+ validateUpload: function(files) {
+ if (this._options.maxFiles === null || files.length + this.countFiles() <= this._options.maxFiles) {
+ return true;
+ }
+ else {
+ var innerError = elBySel('small.innerError:not(.innerFileError)', this._buttonContainer.parentNode);
+
+ if (innerError === null) {
+ innerError = elCreate('small');
+ innerError.classList = 'innerError';
+ DomUtil.insertAfter(innerError, this._buttonContainer);
+ }
+
+ innerError.textContent = Language.get('wcf.upload.error.reachedRemainingLimit', {
+ maxFiles: this._options.maxFiles - this.countFiles()
+ });
+
+ return false;
+ }
+ },
+
+ /**
+ * Returns the count of the uploaded images.
+ *
+ * @return {int}
+ */
+ countFiles: function() {
+ if (this._options.imagePreview) {
+ return elBySel('img', this._target) !== null ? 1 : 0;
+ }
+ else {
+ return this._target.childElementCount;
+ }
+ },
+
+ /**
+ * Checks the maximum number of files and enables or disables the upload button.
+ */
+ checkMaxFiles: function() {
+ if (this._options.maxFiles !== null && this.countFiles() >= this._options.maxFiles) {
+ elHide(this._button);
+ }
+ else {
+ elShow(this._button);
+ }
+ }
+ });
+
+ return Upload;
+});
+
+/**
+ * Provides a filter input for checkbox lists.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList/Filter
+ */
+define('WoltLabSuite/Core/Ui/ItemList/Filter',['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util', 'Ui/SimpleDropdown'], function (Core, EventKey, Language, List, StringUtil, DomUtil, UiSimpleDropdown) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _buildItems: function() {},
+ _prepareItem: function() {},
+ _keyup: function() {},
+ _toggleVisibility: function () {},
+ _setupVisibilityFilter: function () {},
+ _setVisibility: function () {}
+ };
+ return Fake;
+ }
+
+ /**
+ * Creates a new filter input.
+ *
+ * @param {string} elementId list element id
+ * @param {Object=} options options
+ * @constructor
+ */
+ function UiItemListFilter(elementId, options) { this.init(elementId, options); }
+ UiItemListFilter.prototype = {
+ /**
+ * Creates a new filter input.
+ *
+ * @param {string} elementId list element id
+ * @param {Object=} options options
+ */
+ init: function(elementId, options) {
+ this._value = '';
+
+ this._options = Core.extend({
+ callbackPrepareItem: undefined,
+ enableVisibilityFilter: true
+ }, options);
+
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, '" + elementId + "' does not match anything.");
+ }
+ else if (!element.classList.contains('scrollableCheckboxList') && typeof this._options.callbackPrepareItem !== 'function') {
+ throw new Error("Filter only works with elements with the CSS class 'scrollableCheckboxList'.");
+ }
+
+ elData(element, 'filter', 'showAll');
+
+ var container = elCreate('div');
+ container.className = 'itemListFilter';
+
+ element.parentNode.insertBefore(container, element);
+ container.appendChild(element);
+
+ var inputAddon = elCreate('div');
+ inputAddon.className = 'inputAddon';
+
+ var input = elCreate('input');
+ input.className = 'long';
+ input.type = 'text';
+ input.placeholder = Language.get('wcf.global.filter.placeholder');
+ input.addEventListener('keydown', function (event) {
+ if (EventKey.Enter(event)) {
+ event.preventDefault();
+ }
+ });
+ input.addEventListener('keyup', this._keyup.bind(this));
+
+ var clearButton = elCreate('a');
+ clearButton.href = '#';
+ clearButton.className = 'button inputSuffix jsTooltip';
+ clearButton.title = Language.get('wcf.global.filter.button.clear');
+ clearButton.innerHTML = '<span class="icon icon16 fa-times"></span>';
+ clearButton.addEventListener('click', (function(event) {
+ event.preventDefault();
+
+ this.reset();
+ }).bind(this));
+
+ inputAddon.appendChild(input);
+ inputAddon.appendChild(clearButton);
+
+ if (this._options.enableVisibilityFilter) {
+ var visibilityButton = elCreate('a');
+ visibilityButton.href = '#';
+ visibilityButton.className = 'button inputSuffix jsTooltip';
+ visibilityButton.title = Language.get('wcf.global.filter.button.visibility');
+ visibilityButton.innerHTML = '<span class="icon icon16 fa-eye"></span>';
+ visibilityButton.addEventListener(WCF_CLICK_EVENT, this._toggleVisibility.bind(this));
+ inputAddon.appendChild(visibilityButton);
+ }
+
+ container.appendChild(inputAddon);
+
+ this._container = container;
+ this._dropdown = null;
+ this._dropdownId = '';
+ this._element = element;
+ this._input = input;
+ this._items = null;
+ this._fragment = null;
+ },
+
+ /**
+ * Resets the filter.
+ */
+ reset: function () {
+ this._input.value = '';
+ this._keyup();
+ },
+
+ /**
+ * Builds the item list and rebuilds the items' DOM for easier manipulation.
+ *
+ * @protected
+ */
+ _buildItems: function() {
+ this._items = new List();
+
+ var callback = (typeof this._options.callbackPrepareItem === 'function') ? this._options.callbackPrepareItem : this._prepareItem.bind(this);
+ for (var i = 0, length = this._element.childElementCount; i < length; i++) {
+ this._items.add(callback(this._element.children[i]));
+ }
+ },
+
+ /**
+ * Processes an item and returns the meta data.
+ *
+ * @param {Element} item current item
+ * @return {{item: *, span: Element, text: string}}
+ * @protected
+ */
+ _prepareItem: function(item) {
+ var label = item.children[0];
+ var text = label.textContent.trim();
+
+ var checkbox = label.children[0];
+ while (checkbox.nextSibling) {
+ label.removeChild(checkbox.nextSibling);
+ }
+
+ label.appendChild(document.createTextNode(' '));
+
+ var span = elCreate('span');
+ span.textContent = text;
+ label.appendChild(span);
+
+ return {
+ item: item,
+ span: span,
+ text: text
+ };
+ },
+
+ /**
+ * Rebuilds the list on keyup, uses case-insensitive matching.
+ *
+ * @protected
+ */
+ _keyup: function() {
+ var value = this._input.value.trim();
+ if (this._value === value) {
+ return;
+ }
+
+ if (this._fragment === null) {
+ this._fragment = document.createDocumentFragment();
+
+ // set fixed height to avoid layout jumps
+ this._element.style.setProperty('height', this._element.offsetHeight + 'px', '');
+ }
+
+ // move list into fragment before editing items, increases performance
+ // by avoiding the browser to perform repaint/layout over and over again
+ this._fragment.appendChild(this._element);
+
+ if (this._items === null) {
+ this._buildItems();
+ }
+
+ var regexp = new RegExp('(' + StringUtil.escapeRegExp(value) + ')', 'i');
+ var hasVisibleItems = (value === '');
+ this._items.forEach(function (item) {
+ if (value === '') {
+ item.span.textContent = item.text;
+
+ elShow(item.item);
+ }
+ else {
+ if (regexp.test(item.text)) {
+ item.span.innerHTML = item.text.replace(regexp, '<u>$1</u>');
+
+ elShow(item.item);
+ hasVisibleItems = true;
+ }
+ else {
+ elHide(item.item);
+ }
+ }
+ });
+
+ this._container.insertBefore(this._fragment.firstChild, this._container.firstChild);
+ this._value = value;
+
+ elInnerError(this._container, (hasVisibleItems) ? false : Language.get('wcf.global.filter.error.noMatches'));
+ },
+
+ /**
+ * Toggles the visibility mode for marked items.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _toggleVisibility: function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var button = event.currentTarget;
+ if (this._dropdown === null) {
+ var dropdown = elCreate('ul');
+ dropdown.className = 'dropdownMenu';
+
+ ['activeOnly', 'highlightActive', 'showAll'].forEach((function (type) {
+ var link = elCreate('a');
+ elData(link, 'type', type);
+ link.href = '#';
+ link.textContent = Language.get('wcf.global.filter.visibility.' + type);
+ link.addEventListener(WCF_CLICK_EVENT, this._setVisibility.bind(this));
+
+ var li = elCreate('li');
+ li.appendChild(link);
+
+ if (type === 'showAll') {
+ li.className = 'active';
+
+ var divider = elCreate('li');
+ divider.className = 'dropdownDivider';
+ dropdown.appendChild(divider);
+ }
+
+ dropdown.appendChild(li);
+ }).bind(this));
+
+ UiSimpleDropdown.initFragment(button, dropdown);
+
+ // add `active` classes required for the visibility filter
+ this._setupVisibilityFilter();
+
+ this._dropdown = dropdown;
+ this._dropdownId = button.id;
+ }
+
+ UiSimpleDropdown.toggleDropdown(button.id, button);
+ },
+
+ /**
+ * Set-ups the visibility filter by assigning an active class to the
+ * list items that hold the checkboxes and observing the checkboxes
+ * for any changes.
+ *
+ * This process involves quite a few DOM changes and new event listeners,
+ * therefore we'll delay this until the filter has been accessed for
+ * the first time, because none of these changes matter before that.
+ *
+ * @protected
+ */
+ _setupVisibilityFilter: function () {
+ var nextSibling = this._element.nextSibling;
+ var parent = this._element.parentNode;
+ var scrollTop = this._element.scrollTop;
+
+ // mass-editing of DOM elements is slow while they're part of the document
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild(this._element);
+
+ elBySelAll('li', this._element, function(li) {
+ var checkbox = elBySel('input[type="checkbox"]', li);
+ if (checkbox) {
+ if (checkbox.checked) li.classList.add('active');
+
+ checkbox.addEventListener('change', function() {
+ li.classList[(checkbox.checked ? 'add' : 'remove')]('active');
+ });
+ }
+ else {
+ var radioButton = elBySel('input[type="radio"]', li);
+ if (radioButton) {
+ if (radioButton.checked) li.classList.add('active');
+
+ radioButton.addEventListener('change', function() {
+ elBySelAll('li', this._element, function(everyLi) {
+ everyLi.classList.remove('active');
+ });
+
+ li.classList[(radioButton.checked ? 'add' : 'remove')]('active');
+ }.bind(this));
+ }
+ }
+ }.bind(this));
+
+ // re-insert the modified DOM
+ parent.insertBefore(this._element, nextSibling);
+ this._element.scrollTop = scrollTop;
+ },
+
+ /**
+ * Sets the visibility of marked items.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _setVisibility: function (event) {
+ event.preventDefault();
+
+ var link = event.currentTarget;
+ var type = elData(link, 'type');
+
+ UiSimpleDropdown.close(this._dropdownId);
+
+ if (elData(this._element, 'filter') === type) {
+ // filter did not change
+ return;
+ }
+
+ elData(this._element, 'filter', type);
+
+ elBySel('.active', this._dropdown).classList.remove('active');
+ link.parentNode.classList.add('active');
+
+ var button = elById(this._dropdownId);
+ button.classList[(type === 'showAll' ? 'remove' : 'add')]('active');
+
+ var icon = elBySel('.icon', button);
+ icon.classList[(type === 'showAll' ? 'add' : 'remove')]('fa-eye');
+ icon.classList[(type === 'showAll' ? 'remove' : 'add')]('fa-eye-slash');
+ }
+ };
+
+ return UiItemListFilter;
+});
+
+/**
+ * Flexible UI element featuring both a list of items and an input field.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList/Static
+ */
+define('WoltLabSuite/Core/Ui/ItemList/Static',['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'Ui/SimpleDropdown'], function(Core, Dictionary, Language, DomTraverse, EventKey, UiSimpleDropdown) {
+ "use strict";
+
+ var _activeId = '';
+ var _data = new Dictionary();
+ var _didInit = false;
+
+ var _callbackKeyDown = null;
+ var _callbackKeyPress = null;
+ var _callbackKeyUp = null;
+ var _callbackPaste = null;
+ var _callbackRemoveItem = null;
+ var _callbackBlur = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/ItemList/Static
+ */
+ return {
+ /**
+ * Initializes an item list.
+ *
+ * The `values` argument must be empty or contain a list of strings or object, e.g.
+ * `['foo', 'bar']` or `[{ objectId: 1337, value: 'baz'}, {...}]`
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of existing values
+ * @param {Object} options option list
+ */
+ init: function(elementId, values, options) {
+ var element = elById(elementId);
+ if (element === null) {
+ throw new Error("Expected a valid element id, '" + elementId + "' is invalid.");
+ }
+
+ // remove data from previous instance
+ if (_data.has(elementId)) {
+ var tmp = _data.get(elementId);
+
+ for (var key in tmp) {
+ if (tmp.hasOwnProperty(key)) {
+ var el = tmp[key];
+ if (el instanceof Element && el.parentNode) {
+ elRemove(el);
+ }
+ }
+ }
+
+ UiSimpleDropdown.destroy(elementId);
+ _data.delete(elementId);
+ }
+
+ options = Core.extend({
+ // maximum number of items this list may contain, `-1` for infinite
+ maxItems: -1,
+ // maximum length of an item value, `-1` for infinite
+ maxLength: -1,
+
+ // initial value will be interpreted as comma separated value and submitted as such
+ isCSV: false,
+
+ // will be invoked whenever the items change, receives the element id first and list of values second
+ callbackChange: null,
+ // callback once the form is about to be submitted
+ callbackSubmit: null,
+ // value may contain the placeholder `{$objectId}`
+ submitFieldName: ''
+ }, options);
+
+ var form = DomTraverse.parentByTag(element, 'FORM');
+ if (form !== null) {
+ if (options.isCSV === false) {
+ if (!options.submitFieldName.length && typeof options.callbackSubmit !== 'function') {
+ throw new Error("Expected a valid function for option 'callbackSubmit', a non-empty value for option 'submitFieldName' or enabling the option 'submitFieldCSV'.");
+ }
+
+ form.addEventListener('submit', (function() {
+ var values = this.getValues(elementId);
+ if (options.submitFieldName.length) {
+ var input;
+ for (var i = 0, length = values.length; i < length; i++) {
+ input = elCreate('input');
+ input.type = 'hidden';
+ input.name = options.submitFieldName.replace(/{$objectId}/, values[i].objectId);
+ input.value = values[i].value;
+
+ form.appendChild(input);
+ }
+ }
+ else {
+ options.callbackSubmit(form, values);
+ }
+ }).bind(this));
+ }
+ }
+
+ this._setup();
+
+ var data = this._createUI(element, options);
+ _data.set(elementId, {
+ dropdownMenu: null,
+ element: data.element,
+ list: data.list,
+ listItem: data.element.parentNode,
+ options: options,
+ shadow: data.shadow
+ });
+
+ values = (data.values.length) ? data.values : values;
+ if (Array.isArray(values)) {
+ var value;
+ for (var i = 0, length = values.length; i < length; i++) {
+ value = values[i];
+ if (typeof value === 'string') {
+ value = { objectId: 0, value: value };
+ }
+
+ this._addItem(elementId, value);
+ }
+ }
+ },
+
+ /**
+ * Returns the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @return {Array} list of objects containing object id and value
+ */
+ getValues: function(elementId) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+ var values = [];
+ elBySelAll('.item > span', data.list, function(span) {
+ values.push({
+ objectId: ~~elData(span, 'object-id'),
+ value: span.textContent
+ });
+ });
+
+ return values;
+ },
+
+ /**
+ * Sets the list of current values.
+ *
+ * @param {string} elementId input element id
+ * @param {Array} values list of objects containing object id and value
+ */
+ setValues: function(elementId, values) {
+ if (!_data.has(elementId)) {
+ throw new Error("Element id '" + elementId + "' is unknown.");
+ }
+
+ var data = _data.get(elementId);
+
+ // remove all existing items first
+ var i, length;
+ var items = DomTraverse.childrenByClass(data.list, 'item');
+ for (i = 0, length = items.length; i < length; i++) {
+ this._removeItem(null, items[i], true);
+ }
+
+ // add new items
+ for (i = 0, length = values.length; i < length; i++) {
+ this._addItem(elementId, values[i]);
+ }
+ },
+
+ /**
+ * Binds static event listeners.
+ */
+ _setup: function() {
+ if (_didInit) {
+ return;
+ }
+
+ _didInit = true;
+
+ _callbackKeyDown = this._keyDown.bind(this);
+ _callbackKeyPress = this._keyPress.bind(this);
+ _callbackKeyUp = this._keyUp.bind(this);
+ _callbackPaste = this._paste.bind(this);
+ _callbackRemoveItem = this._removeItem.bind(this);
+ _callbackBlur = this._blur.bind(this);
+ },
+
+ /**
+ * Creates the DOM structure for target element. If `element` is a `<textarea>`
+ * it will be automatically replaced with an `<input>` element.
+ *
+ * @param {Element} element input element
+ * @param {Object} options option list
+ */
+ _createUI: function(element, options) {
+ var list = elCreate('ol');
+ list.className = 'inputItemList' + (element.disabled ? ' disabled' : '');
+ elData(list, 'element-id', element.id);
+ list.addEventListener(WCF_CLICK_EVENT, function(event) {
+ if (event.target === list) {
+ //noinspection JSUnresolvedFunction
+ element.focus();
+ }
+ });
+
+ var listItem = elCreate('li');
+ listItem.className = 'input';
+ list.appendChild(listItem);
+
+ element.addEventListener('keydown', _callbackKeyDown);
+ element.addEventListener('keypress', _callbackKeyPress);
+ element.addEventListener('keyup', _callbackKeyUp);
+ element.addEventListener('paste', _callbackPaste);
+ element.addEventListener('blur', _callbackBlur);
+
+ element.parentNode.insertBefore(list, element);
+ listItem.appendChild(element);
+
+ if (options.maxLength !== -1) {
+ elAttr(element, 'maxLength', options.maxLength);
+ }
+
+ var shadow = null, values = [];
+ if (options.isCSV) {
+ shadow = elCreate('input');
+ shadow.className = 'itemListInputShadow';
+ shadow.type = 'hidden';
+ //noinspection JSUnresolvedVariable
+ shadow.name = element.name;
+ element.removeAttribute('name');
+
+ list.parentNode.insertBefore(shadow, list);
+
+ //noinspection JSUnresolvedVariable
+ var value, tmp = element.value.split(',');
+ for (var i = 0, length = tmp.length; i < length; i++) {
+ value = tmp[i].trim();
+ if (value.length) {
+ values.push(value);
+ }
+ }
+
+ if (element.nodeName === 'TEXTAREA') {
+ var inputElement = elCreate('input');
+ inputElement.type = 'text';
+ element.parentNode.insertBefore(inputElement, element);
+ inputElement.id = element.id;
+
+ elRemove(element);
+ element = inputElement;
+ }
+ }
+
+ return {
+ element: element,
+ list: list,
+ shadow: shadow,
+ values: values
+ };
+ },
+
+ /**
+ * Enforces the maximum number of items.
+ *
+ * @param {string} elementId input element id
+ */
+ _handleLimit: function(elementId) {
+ var data = _data.get(elementId);
+ if (data.options.maxItems === -1) {
+ return;
+ }
+
+ if (data.list.childElementCount - 1 < data.options.maxItems) {
+ if (data.element.disabled) {
+ data.element.disabled = false;
+ data.element.removeAttribute('placeholder');
+ }
+ }
+ else if (!data.element.disabled) {
+ data.element.disabled = true;
+ elAttr(data.element, 'placeholder', Language.get('wcf.global.form.input.maxItems'));
+ }
+ },
+
+ /**
+ * Sets the active item list id and handles keyboard access to remove an existing item.
+ *
+ * @param {object} event event object
+ */
+ _keyDown: function(event) {
+ var input = event.currentTarget;
+ var lastItem = input.parentNode.previousElementSibling;
+
+ _activeId = input.id;
+
+ if (event.keyCode === 8) {
+ // 8 = [BACKSPACE]
+ if (input.value.length === 0) {
+ if (lastItem !== null) {
+ if (lastItem.classList.contains('active')) {
+ this._removeItem(null, lastItem);
+ }
+ else {
+ lastItem.classList.add('active');
+ }
+ }
+ }
+ }
+ else if (event.keyCode === 27) {
+ // 27 = [ESC]
+ if (lastItem !== null && lastItem.classList.contains('active')) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Handles the `[ENTER]` and `[,]` key to add an item to the list.
+ *
+ * @param {Event} event event object
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event) || EventKey.Comma(event)) {
+ event.preventDefault();
+
+ var value = event.currentTarget.value.trim();
+ if (value.length) {
+ this._addItem(event.currentTarget.id, { objectId: 0, value: value });
+ }
+ }
+ },
+
+ /**
+ * Splits comma-separated values being pasted into the input field.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _paste: function (event) {
+ var text = '';
+ if (typeof window.clipboardData === 'object') {
+ // IE11
+ text = window.clipboardData.getData('Text');
+ }
+ else {
+ text = event.clipboardData.getData('text/plain');
+ }
+
+ text.split(/,/).forEach((function(item) {
+ item = item.trim();
+ if (item.length !== 0) {
+ this._addItem(event.currentTarget.id, { objectId: 0, value: item });
+ }
+ }).bind(this));
+
+ event.preventDefault();
+ },
+
+ /**
+ * Handles the keyup event to unmark an item for deletion.
+ *
+ * @param {object} event event object
+ */
+ _keyUp: function(event) {
+ var input = event.currentTarget;
+
+ if (input.value.length > 0) {
+ var lastItem = input.parentNode.previousElementSibling;
+ if (lastItem !== null) {
+ lastItem.classList.remove('active');
+ }
+ }
+ },
+
+ /**
+ * Adds an item to the list.
+ *
+ * @param {string} elementId input element id
+ * @param {object} value item value
+ */
+ _addItem: function(elementId, value) {
+ var data = _data.get(elementId);
+
+ var listItem = elCreate('li');
+ listItem.className = 'item';
+
+ var content = elCreate('span');
+ content.className = 'content';
+ elData(content, 'object-id', value.objectId);
+ content.textContent = value.value;
+ listItem.appendChild(content);
+
+ if (!data.element.disabled) {
+ var button = elCreate('a');
+ button.className = 'icon icon16 fa-times';
+ button.addEventListener(WCF_CLICK_EVENT, _callbackRemoveItem);
+ listItem.appendChild(button);
+ }
+
+ data.list.insertBefore(listItem, data.listItem);
+ data.element.value = '';
+
+ if (!data.element.disabled) {
+ this._handleLimit(elementId);
+ }
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Removes an item from the list.
+ *
+ * @param {?object} event event object
+ * @param {Element?} item list item
+ * @param {boolean?} noFocus input element will not be focused if true
+ */
+ _removeItem: function(event, item, noFocus) {
+ item = (event === null) ? item : event.currentTarget.parentNode;
+
+ var parent = item.parentNode;
+ //noinspection JSCheckFunctionSignatures
+ var elementId = elData(parent, 'element-id');
+ var data = _data.get(elementId);
+
+ parent.removeChild(item);
+ if (!noFocus) data.element.focus();
+
+ this._handleLimit(elementId);
+ var values = this._syncShadow(data);
+
+ if (typeof data.options.callbackChange === 'function') {
+ if (values === null) values = this.getValues(elementId);
+ data.options.callbackChange(elementId, values);
+ }
+ },
+
+ /**
+ * Synchronizes the shadow input field with the current list item values.
+ *
+ * @param {object} data element data
+ */
+ _syncShadow: function(data) {
+ if (!data.options.isCSV) return null;
+
+ var value = '', values = this.getValues(data.element.id);
+ for (var i = 0, length = values.length; i < length; i++) {
+ value += (value.length ? ',' : '') + values[i].value;
+ }
+
+ data.shadow.value = value;
+
+ return values;
+ },
+
+ /**
+ * Handles the blur event.
+ *
+ * @param {object} event event object
+ */
+ _blur: function(event) {
+ var data = _data.get(event.currentTarget.id);
+
+ var currentTarget = event.currentTarget;
+ window.setTimeout(function() {
+ var value = currentTarget.value.trim();
+ if (value.length) {
+ this._addItem(currentTarget.id, { objectId: 0, value: value });
+ }
+ }.bind(this), 100);
+ }
+ };
+});
+
+/**
+ * Provides an item list for users and groups.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/ItemList/User
+ */
+define('WoltLabSuite/Core/Ui/ItemList/User',['WoltLabSuite/Core/Ui/ItemList'], function(UiItemList) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ getValues: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/ItemList/User
+ */
+ return {
+ _shadowGroups: null,
+
+ /**
+ * Initializes user suggestion support for an element.
+ *
+ * @param {string} elementId input element id
+ * @param {object} options option list
+ */
+ init: function(elementId, options) {
+ this._shadowGroups = null;
+
+ UiItemList.init(elementId, [], {
+ ajax: {
+ className: 'wcf\\data\\user\\UserAction',
+ parameters: {
+ data: {
+ includeUserGroups: ~~options.includeUserGroups,
+ restrictUserGroupIDs: (Array.isArray(options.restrictUserGroupIDs) ? options.restrictUserGroupIDs : [])
+ }
+ }
+ },
+ callbackChange: (typeof options.callbackChange === 'function' ? options.callbackChange : null),
+ callbackSyncShadow: options.csvPerType ? this._syncShadow.bind(this) : null,
+ callbackSetupValues: (typeof options.callbackSetupValues === 'function' ? options.callbackSetupValues : null),
+ excludedSearchValues: (Array.isArray(options.excludedSearchValues) ? options.excludedSearchValues : []),
+ isCSV: true,
+ maxItems: ~~options.maxItems || -1,
+ restricted: true
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Ui/ItemList::getValues()
+ */
+ getValues: function(elementId) {
+ return UiItemList.getValues(elementId);
+ },
+
+ _syncShadow: function(data) {
+ var values = this.getValues(data.element.id);
+ var users = [], groups = [];
+
+ values.forEach(function(value) {
+ if (value.type && value.type === 'group') groups.push(value.objectId);
+ else users.push(value.value);
+ });
+
+ data.shadow.value = users.join(',');
+ if (!this._shadowGroups) {
+ this._shadowGroups = elCreate('input');
+ this._shadowGroups.type = 'hidden';
+ this._shadowGroups.name = data.shadow.name + 'GroupIDs';
+ data.shadow.parentNode.insertBefore(this._shadowGroups, data.shadow);
+ }
+ this._shadowGroups.value = groups.join(',');
+
+ return values;
+ }
+ };
+});
+
+/**
+ * Object-based user list.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/List
+ */
+define('WoltLabSuite/Core/Ui/User/List',['Ajax', 'Core', 'Dictionary', 'Dom/Util', 'Ui/Dialog', 'WoltLabSuite/Core/Ui/Pagination'], function(Ajax, Core, Dictionary, DomUtil, UiDialog, UiPagination) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiUserList(options) { this.init(options); }
+ UiUserList.prototype = {
+ /**
+ * Initializes the user list.
+ *
+ * @param {object} options list of initialization options
+ */
+ init: function(options) {
+ this._cache = new Dictionary();
+ this._pageCount = 0;
+ this._pageNo = 1;
+
+ this._options = Core.extend({
+ className: '',
+ dialogTitle: '',
+ parameters: {}
+ }, options);
+ },
+
+ /**
+ * Opens the user list.
+ */
+ open: function() {
+ this._pageNo = 1;
+ this._showPage();
+ },
+
+ /**
+ * Shows the current or given page.
+ *
+ * @param {int=} pageNo page number
+ */
+ _showPage: function(pageNo) {
+ if (typeof pageNo === 'number') {
+ this._pageNo = ~~pageNo;
+ }
+
+ if (this._pageCount !== 0 && (this._pageNo < 1 || this._pageNo > this._pageCount)) {
+ throw new RangeError("pageNo must be between 1 and " + this._pageCount + " (" + this._pageNo + " given).");
+ }
+
+ if (this._cache.has(this._pageNo)) {
+ var dialog = UiDialog.open(this, this._cache.get(this._pageNo));
+
+ if (this._pageCount > 1) {
+ var element = elBySel('.jsPagination', dialog.content);
+ if (element !== null) {
+ new UiPagination(element, {
+ activePage: this._pageNo,
+ maxPage: this._pageCount,
+
+ callbackSwitch: this._showPage.bind(this)
+ });
+ }
+
+ // scroll to the list start
+ var container = dialog.content.parentNode;
+ if (container.scrollTop > 0) {
+ container.scrollTop = 0;
+ }
+ }
+ }
+ else {
+ this._options.parameters.pageNo = this._pageNo;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.pageCount !== undefined) {
+ this._pageCount = ~~data.returnValues.pageCount;
+ }
+
+ this._cache.set(this._pageNo, data.returnValues.template);
+ this._showPage();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getGroupedUserList',
+ className: this._options.className,
+ interfaceName: 'wcf\\data\\IGroupedUserListAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: DomUtil.getUniqueId(),
+ options: {
+ title: this._options.dialogTitle
+ },
+ source: null
+ };
+ }
+ };
+
+ return UiUserList;
+});
+
+/**
+ * Provides interface elements to use reactions.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Reaction/Handler
+ * @since 5.2
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Reaction/CountButtons',[
+ 'Ajax', 'Core', 'Dictionary', 'Language',
+ 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util',
+ 'Ui/Dialog'
+ ],
+ function(
+ Ajax, Core, Dictionary, Language,
+ ObjectMap, StringUtil, DomChangeListener, DomUtil,
+ UiDialog
+ )
+ {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function CountButtons(objectType, options) { this.init(objectType, options); }
+ CountButtons.prototype = {
+ /**
+ * Initializes the like handler.
+ *
+ * @param {string} objectType object type
+ * @param {object} options initialization options
+ */
+ init: function(objectType, options) {
+ if (options.containerSelector === '') {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/CountButtons] Expected a non-empty string for option 'containerSelector'.");
+ }
+
+ this._containers = new Dictionary();
+ this._objects = new Dictionary();
+ this._objectType = objectType;
+
+ this._options = Core.extend({
+ // selectors
+ summaryListSelector: '.reactionSummaryList',
+ containerSelector: '',
+ isSingleItem: false,
+
+ // optional parameters
+ parameters: {
+ data: {}
+ }
+ }, options);
+
+ this.initContainers(options, objectType);
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Reaction/CountButtons-' + objectType, this.initContainers.bind(this));
+ },
+
+ /**
+ * Initialises the containers.
+ */
+ initContainers: function() {
+ var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false, objectId;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._containers.has(DomUtil.identify(element))) {
+ continue;
+ }
+
+ elementData = {
+ reactButton: null,
+ summary: null,
+
+ objectId: ~~elData(element, 'object-id'),
+ element: element
+ };
+
+ this._containers.set(DomUtil.identify(element), elementData);
+ this._initReactionCountButtons(element, elementData);
+
+ if (!this._objects.has(~~elData(element, 'object-id'))) {
+ var objects = [];
+ }
+ else {
+ var objects = this._objects.get(~~elData(element, 'object-id'));
+ }
+
+ objects.push(elementData);
+
+ this._objects.set(~~elData(element, 'object-id'), objects);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Update the count buttons with the given data.
+ *
+ * @param {int} objectId
+ * @param {object} data
+ */
+ updateCountButtons: function(objectId, data) {
+ var triggerChange = false;
+ this._objects.get(objectId).forEach(function(elementData) {
+ var summaryList = elBySel(this._options.summaryListSelector, elementData.element);
+
+ var sortedElements = {}, elements = elBySelAll('li', summaryList);
+ for (var i = 0, length = elements.length; i < length; i++) {
+ if (data[elData(elements[i], 'reaction-type-id')] !== undefined) {
+ sortedElements[elData(elements[i], 'reaction-type-id')] = elements[i];
+ }
+ else {
+ // reaction has no longer reactions
+ elRemove(elements[i]);
+ }
+ }
+
+ Object.keys(data).forEach(function(key) {
+ if (sortedElements[key] !== undefined) {
+ var reactionCount = elBySel('.reactionCount', sortedElements[key]);
+ reactionCount.innerHTML = StringUtil.shortUnit(data[key]);
+ }
+ else if (REACTION_TYPES[key] !== undefined) {
+ // create element
+ var createdElement = elCreate('li');
+ createdElement.className = 'reactCountButton';
+ elData(createdElement, 'reaction-type-id', key);
+
+ var countSpan = elCreate('span');
+ countSpan.className = 'reactionCount';
+ countSpan.innerHTML = StringUtil.shortUnit(data[key]);
+ createdElement.appendChild(countSpan);
+
+ createdElement.innerHTML = createdElement.innerHTML + REACTION_TYPES[key].renderedIcon;
+
+ summaryList.appendChild(createdElement);
+
+ this._initReactionCountButton(createdElement, objectId);
+
+ triggerChange = true;
+ }
+ }, this);
+ }.bind(this));
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Initialized the reaction count buttons.
+ *
+ * @param {element} element
+ * @param {object} elementData
+ */
+ _initReactionCountButtons: function(element, elementData) {
+ if (this._options.isSingleItem) {
+ var summaryList = elBySel(this._options.summaryListSelector);
+ }
+ else {
+ var summaryList = elBySel(this._options.summaryListSelector, element);
+ }
+
+ if (summaryList !== null) {
+ var elements = elBySelAll('li', summaryList);
+ for (var i = 0, length = elements.length; i < length; i++) {
+ this._initReactionCountButton(elements[i], elementData.objectId);
+ }
+ }
+ },
+
+ /**
+ * Initialized a specific reaction count button for an object.
+ *
+ * @param {element} element
+ * @param {int} objectId
+ */
+ _initReactionCountButton: function(element, objectId) {
+ element.addEventListener(WCF_CLICK_EVENT, this._showReactionOverlay.bind(this, objectId));
+ },
+
+ /**
+ * Shows the reaction overly for a specific object.
+ *
+ * @param {int} objectId
+ */
+ _showReactionOverlay: function(objectId) {
+ this._currentObjectId = objectId;
+ this._showOverlay();
+ },
+
+ /**
+ * Shows a specific page of the current opened reaction overlay.
+ *
+ * @param {int} pageNo
+ */
+ _showOverlay: function() {
+ this._options.parameters.data.containerID = this._objectType + '-' + this._currentObjectId;
+ this._options.parameters.data.objectID = this._currentObjectId;
+ this._options.parameters.data.objectType = this._objectType;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ },
+
+ _ajaxSuccess: function(data) {
+ UiDialog.open(this, data.returnValues.template);
+ UiDialog.setTitle('userReactionOverlay-' + this._objectType, data.returnValues.title);
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getReactionDetails',
+ className: '\\wcf\\data\\reaction\\ReactionAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'userReactionOverlay-' + this._objectType,
+ options: {
+ title: ""
+ },
+ source: null
+ };
+ }
+ };
+
+ return CountButtons;
+ });
+
+/**
+ * Provides interface elements to use reactions.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Reaction/Handler
+ * @since 5.2
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Reaction/Handler',[
+ 'Ajax', 'Core', 'Dictionary', 'Language',
+ 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util',
+ 'Ui/Dialog', 'WoltLabSuite/Core/Ui/User/List', 'User', 'WoltLabSuite/Core/Ui/Reaction/CountButtons',
+ 'Ui/Alignment', 'Ui/CloseOverlay', 'Ui/Screen'
+ ],
+ function(
+ Ajax, Core, Dictionary, Language,
+ ObjectMap, StringUtil, DomChangeListener, DomUtil,
+ UiDialog, UiUserList, User, CountButtons,
+ UiAlignment, UiCloseOverlay, UiScreen
+ )
+ {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiReactionHandler(objectType, options) { this.init(objectType, options); }
+ UiReactionHandler.prototype = {
+ /**
+ * Initializes the reaction handler.
+ *
+ * @param {string} objectType object type
+ * @param {object} options initialization options
+ */
+ init: function(objectType, options) {
+ if (options.containerSelector === '') {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Handler] Expected a non-empty string for option 'containerSelector'.");
+ }
+
+ this._containers = new Dictionary();
+ this._details = new ObjectMap();
+ this._objectType = objectType;
+ this._cache = new Dictionary();
+ this._objects = new Dictionary();
+
+ this._popoverCurrentObjectId = 0;
+
+ this._popover = null;
+
+ this._options = Core.extend({
+ // selectors
+ buttonSelector: '.reactButton',
+ containerSelector: '',
+ isButtonGroupNavigation: false,
+ isSingleItem: false,
+
+ // other stuff
+ parameters: {
+ data: {}
+ }
+ }, options);
+
+ this.initReactButtons(options, objectType);
+
+ this.countButtons = new CountButtons(this._objectType, this._options);
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Reaction/Handler-' + objectType, this.initReactButtons.bind(this));
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Reaction/Handler', this._closePopover.bind(this));
+ },
+
+ /**
+ * Initializes all applicable react buttons with the given selector.
+ */
+ initReactButtons: function() {
+ var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false, objectId;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._containers.has(DomUtil.identify(element))) {
+ continue;
+ }
+
+ elementData = {
+ reactButton: null,
+ objectId: ~~elData(element, 'object-id'),
+ element: element
+ };
+
+ this._containers.set(DomUtil.identify(element), elementData);
+ this._initReactButton(element, elementData);
+
+ if (!this._objects.has(~~elData(element, 'object-id'))) {
+ var objects = [];
+ }
+ else {
+ var objects = this._objects.get(~~elData(element, 'object-id'));
+ }
+
+ objects.push(elementData);
+
+ this._objects.set(~~elData(element, 'object-id'), objects);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+
+ /**
+ * Initializes a specific react button.
+ */
+ _initReactButton: function(element, elementData) {
+ if (this._options.isSingleItem) {
+ elementData.reactButton = elBySel(this._options.buttonSelector);
+ }
+ else {
+ elementData.reactButton = elBySel(this._options.buttonSelector, element);
+ }
+
+ if (elementData.reactButton === null || elementData.reactButton.length === 0) {
+ // the element may have no react button
+ return;
+ }
+
+ if (Object.keys(REACTION_TYPES).length === 1) {
+ var reaction = REACTION_TYPES[Object.keys(REACTION_TYPES)[0]];
+ elementData.reactButton.title = reaction.title;
+ var textSpan = elBySel('.invisible', elementData.reactButton);
+ textSpan.innerText = reaction.title;
+ }
+
+ if (elementData.reactButton.closest('.messageFooterGroup > .jsMobileNavigation')) {
+ UiScreen.on('screen-sm-down', {
+ match: this._enableMobileView.bind(this, elementData.reactButton, elementData.objectId),
+ unmatch: this._disableMobileView.bind(this, elementData.reactButton, elementData.objectId),
+ setup: this._setupMobileView.bind(this, elementData.reactButton, elementData.objectId)
+ });
+ }
+
+ elementData.reactButton.addEventListener(WCF_CLICK_EVENT, this._toggleReactPopover.bind(this, elementData.objectId, elementData.reactButton));
+ },
+
+ /**
+ * Enables the mobile view for the reaction button.
+ *
+ * @param {Element} element
+ */
+ _enableMobileView: function(element) {
+ var messageFooterGroup = element.closest('.messageFooterGroup');
+
+ elShow(elBySel('.mobileReactButton', messageFooterGroup));
+ },
+
+ /**
+ * Disables the mobile view for the reaction button.
+ *
+ * @param {Element} element
+ */
+ _disableMobileView: function(element) {
+ var messageFooterGroup = element.closest('.messageFooterGroup');
+
+ elHide(elBySel('.mobileReactButton', messageFooterGroup));
+ },
+
+ /**
+ * Setup the mobile view for the reaction button.
+ *
+ * @param {Element} element
+ * @param {int} objectID
+ */
+ _setupMobileView: function(element, objectID) {
+ var messageFooterGroup = element.closest('.messageFooterGroup');
+
+ var button = elCreate('button');
+ button.classList = 'mobileReactButton';
+ button.innerHTML = element.innerHTML;
+
+ button.addEventListener(WCF_CLICK_EVENT, this._toggleReactPopover.bind(this, objectID, button));
+
+ messageFooterGroup.appendChild(button);
+ },
+
+ _updateReactButton: function(objectID, reactionTypeID) {
+ this._objects.get(objectID).forEach(function (elementData) {
+ if (reactionTypeID) {
+ elementData.reactButton.classList.add('active');
+ elData(elementData.reactButton, 'reaction-type-id', reactionTypeID);
+ }
+ else {
+ elData(elementData.reactButton, 'reaction-type-id', 0);
+ elementData.reactButton.classList.remove('active');
+ }
+ });
+ },
+
+ _markReactionAsActive: function() {
+ var reactionTypeID = elData(this._objects.get(this._popoverCurrentObjectId)[0].reactButton, 'reaction-type-id');
+
+ // clear old active state
+ var elements = elBySelAll('.reactionTypeButton.active', this._getPopover());
+ for (var i = 0, length = elements.length; i < length; i++) {
+ elements[i].classList.remove('active');
+ }
+
+ if (reactionTypeID != 0) {
+ elBySel('.reactionTypeButton[data-reaction-type-id="'+reactionTypeID+'"]', this._getPopover()).classList.add('active');
+ }
+ },
+
+ /**
+ * Toggle the visibility of the react popover.
+ *
+ * @param {int} objectId
+ * @param {Element} element
+ */
+ _toggleReactPopover: function(objectId, element, event) {
+ if (event !== null) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ if (Object.keys(REACTION_TYPES).length === 1) {
+ var reaction = REACTION_TYPES[Object.keys(REACTION_TYPES)[0]];
+ this._popoverCurrentObjectId = objectId;
+
+ this._react(reaction.reactionTypeID);
+ }
+ else {
+ if (this._popoverCurrentObjectId === 0 || this._popoverCurrentObjectId !== objectId) {
+ this._openReactPopover(objectId, element);
+ }
+ else {
+ this._closePopover(objectId, element);
+ }
+ }
+ },
+
+ /**
+ * Opens the react popover for a specific react button.
+ *
+ * @param {int} objectId objectId of the element
+ * @param {Element} element container element
+ */
+ _openReactPopover: function(objectId, element) {
+ // first close old popover, if exists
+ if (this._popoverCurrentObjectId !== 0) {
+ this._closePopover();
+ }
+
+ this._popoverCurrentObjectId = objectId;
+ this._markReactionAsActive();
+
+ UiAlignment.set(this._getPopover(), element, {
+ pointer: true,
+ horizontal: (this._options.isButtonGroupNavigation) ? 'left' :'center',
+ vertical: 'top'
+ });
+
+ if (this._options.isButtonGroupNavigation) {
+ // find nav element
+ var nav = element.closest('nav');
+ nav.style.opacity = "1";
+ }
+
+ this._getPopover().classList.remove('forceHide');
+ this._getPopover().classList.add('active');
+ },
+
+ /**
+ * Returns the react popover element.
+ *
+ * @returns {Element}
+ */
+ _getPopover: function() {
+ if (this._popover == null) {
+ this._popover = elCreate('div');
+ this._popover.className = 'reactionPopover forceHide';
+
+ var _popoverContent = elCreate('div');
+ _popoverContent.className = 'reactionPopoverContent';
+
+ var popoverContentHTML = elCreate('ul');
+
+ var sortedReactionTypes = this._getSortedReactionTypes();
+
+ for (var key in sortedReactionTypes) {
+ if (!sortedReactionTypes.hasOwnProperty(key)) continue;
+
+ var reactionType = sortedReactionTypes[key];
+
+ var reactionTypeItem = elCreate('li');
+ reactionTypeItem.className = 'reactionTypeButton jsTooltip';
+ elData(reactionTypeItem, 'reaction-type-id', reactionType.reactionTypeID);
+ elData(reactionTypeItem, 'title', reactionType.title);
+ reactionTypeItem.title = reactionType.title;
+
+ var reactionTypeItemSpan = elCreate('span');
+ reactionTypeItemSpan.classList = 'reactionTypeButtonTitle';
+ reactionTypeItemSpan.innerHTML = reactionType.title;
+
+ reactionTypeItem.innerHTML = reactionType.renderedIcon;
+
+ reactionTypeItem.appendChild(reactionTypeItemSpan);
+
+ reactionTypeItem.addEventListener(WCF_CLICK_EVENT, this._react.bind(this, reactionType.reactionTypeID));
+
+ popoverContentHTML.appendChild(reactionTypeItem);
+ }
+
+ _popoverContent.appendChild(popoverContentHTML);
+ this._popover.appendChild(_popoverContent);
+
+ var pointer = elCreate('span');
+ pointer.className = 'elementPointer';
+ pointer.appendChild(elCreate('span'));
+ this._popover.appendChild(pointer);
+
+ document.body.appendChild(this._popover);
+
+ DomChangeListener.trigger();
+ }
+
+ return this._popover;
+ },
+
+ /**
+ * Sort the reaction types by the showOrder field.
+ *
+ * @returns {Array} the reaction types sorted by showOrder
+ */
+ _getSortedReactionTypes: function() {
+ var sortedReactionTypes = [];
+
+ // convert our reaction type object to an array
+ for (var key in REACTION_TYPES) {
+ if (!REACTION_TYPES.hasOwnProperty(key)) continue;
+ sortedReactionTypes.push(REACTION_TYPES[key]);
+ }
+
+ // sort the array
+ sortedReactionTypes.sort(function (a, b) {
+ return a.showOrder - b.showOrder;
+ });
+
+ return sortedReactionTypes;
+ },
+
+ /**
+ * Closes the react popover.
+ */
+ _closePopover: function() {
+ if (this._popoverCurrentObjectId !== 0) {
+ this._getPopover().classList.remove('active');
+
+ if (this._options.isButtonGroupNavigation) {
+ this._objects.get(this._popoverCurrentObjectId).forEach(function (elementData) {
+ elementData.reactButton.closest('nav').style.cssText = "";
+ });
+ }
+
+ this._popoverCurrentObjectId = 0;
+ }
+ },
+
+ /**
+ * React with the given reactionTypeId on an object.
+ *
+ * @param {init} reactionTypeId
+ */
+ _react: function(reactionTypeId) {
+ this._options.parameters.reactionTypeID = reactionTypeId;
+ this._options.parameters.data.containerID = this._currentReactionTypeId;
+ this._options.parameters.data.objectID = this._popoverCurrentObjectId;
+ this._options.parameters.data.objectType = this._objectType;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+
+ this._closePopover();
+ },
+
+ _ajaxSuccess: function(data) {
+ this.countButtons.updateCountButtons(data.returnValues.objectID, data.returnValues.reactions);
+
+ // update react button status
+ this._updateReactButton(data.returnValues.objectID, data.returnValues.reactionTypeID);
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'react',
+ className: '\\wcf\\data\\reaction\\ReactionAction'
+ }
+ };
+ }
+ };
+
+ return UiReactionHandler;
+ });
+
+/**
+ * Provides interface elements to display and review likes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Like/Handler
+ * @deprecated 5.2 use ReactionHandler instead
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Like/Handler',[
+ 'Ajax', 'Core', 'Dictionary', 'Language',
+ 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util',
+ 'Ui/Dialog', 'WoltLabSuite/Core/Ui/User/List', 'User', 'WoltLabSuite/Core/Ui/Reaction/Handler'
+ ],
+ function(
+ Ajax, Core, Dictionary, Language,
+ ObjectMap, StringUtil, DomChangeListener, DomUtil,
+ UiDialog, UiUserList, User, UiReactionHandler
+ )
+{
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiLikeHandler(objectType, options) { this.init(objectType, options); }
+ UiLikeHandler.prototype = {
+ /**
+ * Initializes the like handler.
+ *
+ * @param {string} objectType object type
+ * @param {object} options initialization options
+ */
+ init: function(objectType, options) {
+ if (options.containerSelector === '') {
+ throw new Error("[WoltLabSuite/Core/Ui/Like/Handler] Expected a non-empty string for option 'containerSelector'.");
+ }
+
+ this._containers = new ObjectMap();
+ this._details = new ObjectMap();
+ this._objectType = objectType;
+ this._options = Core.extend({
+ // settings
+ badgeClassNames: '',
+ isSingleItem: false,
+ markListItemAsActive: false,
+ renderAsButton: true,
+ summaryPrepend: true,
+ summaryUseIcon: true,
+
+ // permissions
+ canDislike: false,
+ canLike: false,
+ canLikeOwnContent: false,
+ canViewSummary: false,
+
+ // selectors
+ badgeContainerSelector: '.messageHeader .messageStatus',
+ buttonAppendToSelector: '.messageFooter .messageFooterButtons',
+ buttonBeforeSelector: '',
+ containerSelector: '',
+ summarySelector: '.messageFooterGroup'
+ }, options);
+
+ this.initContainers(options, objectType);
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/Like/Handler-' + objectType, this.initContainers.bind(this));
+
+ new UiReactionHandler(this._objectType, {
+ containerSelector: this._options.containerSelector,
+ summaryListSelector: '.reactionSummaryList'
+ });
+ },
+
+ /**
+ * Initializes all applicable containers.
+ */
+ initContainers: function() {
+ var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._containers.has(element)) {
+ continue;
+ }
+
+ elementData = {
+ badge: null,
+ dislikeButton: null,
+ likeButton: null,
+ summary: null,
+
+ dislikes: ~~elData(element, 'like-dislikes'),
+ liked: ~~elData(element, 'like-liked'),
+ likes: ~~elData(element, 'like-likes'),
+ objectId: ~~elData(element, 'object-id'),
+ users: JSON.parse(elData(element, 'like-users'))
+ };
+
+ this._containers.set(element, elementData);
+ this._buildWidget(element, elementData);
+
+ triggerChange = true;
+ }
+
+ if (triggerChange) {
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * Creates the interface elements.
+ *
+ * @param {Element} element container element
+ * @param {object} elementData like data
+ */
+ _buildWidget: function(element, elementData) {
+ // build reaction summary list
+ var summaryList, listItem, badgeContainer, isSummaryPosition = true;
+ badgeContainer = (this._options.isSingleItem) ? elBySel(this._options.summarySelector) : elBySel(this._options.summarySelector, element);
+ if (badgeContainer === null) {
+ badgeContainer = (this._options.isSingleItem) ? elBySel(this._options.badgeContainerSelector) : elBySel(this._options.badgeContainerSelector, element);
+ isSummaryPosition = false;
+ }
+
+ if (badgeContainer !== null) {
+ summaryList = elCreate('ul');
+ summaryList.classList.add('reactionSummaryList');
+ if (isSummaryPosition) {
+ summaryList.classList.add('likesSummary');
+ }
+ else {
+ summaryList.classList.add('reactionSummaryListTiny');
+ }
+
+ for (var key in elementData.users) {
+ if (key === "reactionTypeID") continue;
+ if (!REACTION_TYPES.hasOwnProperty(key)) continue;
+
+ // create element
+ var createdElement = elCreate('li');
+ createdElement.className = 'reactCountButton';
+ elData(createdElement, 'reaction-type-id', key);
+
+ var countSpan = elCreate('span');
+ countSpan.className = 'reactionCount';
+ countSpan.innerHTML = StringUtil.shortUnit(elementData.users[key]);
+ createdElement.appendChild(countSpan);
+
+ createdElement.innerHTML = createdElement.innerHTML + REACTION_TYPES[key].renderedIcon;
+
+ summaryList.appendChild(createdElement);
+
+ }
+
+ if (isSummaryPosition) {
+ if (this._options.summaryPrepend) {
+ DomUtil.prepend(summaryList, badgeContainer);
+ }
+ else {
+ badgeContainer.appendChild(summaryList);
+ }
+ }
+ else {
+ if (badgeContainer.nodeName === 'OL' || badgeContainer.nodeName === 'UL') {
+ listItem = elCreate('li');
+ listItem.appendChild(summaryList);
+ badgeContainer.appendChild(listItem);
+ }
+ else {
+ badgeContainer.appendChild(summaryList);
+ }
+ }
+
+ elementData.badge = summaryList;
+ }
+
+ // build reaction button
+ if (this._options.canLike && (User.userId != elData(element, 'user-id') || this._options.canLikeOwnContent)) {
+ var appendTo = (this._options.buttonAppendToSelector) ? ((this._options.isSingleItem) ? elBySel(this._options.buttonAppendToSelector) : elBySel(this._options.buttonAppendToSelector, element)) : null;
+ var insertPosition = (this._options.buttonBeforeSelector) ? ((this._options.isSingleItem) ? elBySel(this._options.buttonBeforeSelector) : elBySel(this._options.buttonBeforeSelector, element)) : null;
+ if (insertPosition === null && appendTo === null) {
+ throw new Error("Unable to find insert location for like/dislike buttons.");
+ }
+ else {
+ elementData.likeButton = this._createButton(element, elementData.users.reactionTypeID, insertPosition, appendTo);
+ }
+ }
+ },
+
+ /**
+ * Creates a reaction button.
+ *
+ * @param {Element} element container element
+ * @param {int} reactionTypeID the reactionTypeID of the current state
+ * @param {Element?} insertBefore insert button before given element
+ * @param {Element?} appendTo append button to given element
+ * @return {Element} button element
+ */
+ _createButton: function(element, reactionTypeID, insertBefore, appendTo) {
+ var title = Language.get('wcf.reactions.react');
+
+ var listItem = elCreate('li');
+ listItem.className = 'wcfReactButton';
+
+ if (insertBefore) {
+ var jsMobileNavigation = insertBefore.parentElement.contains('jsMobileNavigation');
+ }
+ else {
+ var jsMobileNavigation = appendTo.classList.contains('jsMobileNavigation');
+ }
+
+ var button = elCreate('a');
+ button.className = 'jsTooltip reactButton';
+ if (this._options.renderAsButton) {
+ button.classList.add('button');
+
+ if (jsMobileNavigation) {
+ button.classList.add('ignoreMobileNavigation');
+ }
+ }
+
+ button.href = '#';
+ button.title = title;
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon16 fa-smile-o';
+
+ if (reactionTypeID === undefined || reactionTypeID == 0) {
+ elData(icon, 'reaction-type-id', 0);
+ }
+ else {
+ elData(button, 'reaction-type-id', reactionTypeID);
+ button.classList.add("active");
+ }
+
+ button.appendChild(icon);
+
+ var invisibleText = elCreate("span");
+ invisibleText.className = "invisible";
+ invisibleText.innerHTML = title;
+
+ button.appendChild(document.createTextNode(" "));
+ button.appendChild(invisibleText);
+
+ listItem.appendChild(button);
+
+ if (insertBefore) {
+ insertBefore.parentNode.insertBefore(listItem, insertBefore);
+ }
+ else {
+ appendTo.appendChild(listItem);
+ }
+
+ return button;
+ }
+ };
+
+ return UiLikeHandler;
+});
+
+/**
+ * Flexible message inline editor.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/InlineEditor
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Message/InlineEditor',[
+ 'Ajax', 'Core', 'Dictionary', 'Environment',
+ 'EventHandler', 'Language', 'ObjectMap', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll'
+ ],
+ function(
+ Ajax, Core, Dictionary, Environment,
+ EventHandler, Language, ObjectMap, DomChangeListener, DomTraverse,
+ DomUtil, UiNotification, UiReusableDropdown, UiScroll
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ _click: function() {},
+ _clickDropdown: function() {},
+ _dropdownBuild: function() {},
+ _dropdownToggle: function() {},
+ _dropdownGetItems: function() {},
+ _dropdownOpen: function() {},
+ _dropdownSelect: function() {},
+ _clickDropdownItem: function() {},
+ _prepare: function() {},
+ _showEditor: function() {},
+ _restoreMessage: function() {},
+ _save: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showMessage: function() {},
+ _hideEditor: function() {},
+ _restoreEditor: function() {},
+ _destroyEditor: function() {},
+ _getHash: function() {},
+ _updateHistory: function() {},
+ _getEditorId: function() {},
+ _getObjectId: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ legacyEdit: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiMessageInlineEditor(options) { this.init(options); }
+ UiMessageInlineEditor.prototype = {
+ /**
+ * Initializes the message inline editor.
+ *
+ * @param {Object} options list of configuration options
+ */
+ init: function(options) {
+ this._activeDropdownElement = null;
+ this._activeElement = null;
+ this._dropdownMenu = null;
+ this._elements = new ObjectMap();
+ this._options = Core.extend({
+ canEditInline: false,
+
+ className: '',
+ containerId: 0,
+ dropdownIdentifier: '',
+ editorPrefix: 'messageEditor',
+
+ messageSelector: '.jsMessage',
+
+ quoteManager: null
+ }, options);
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Message/InlineEdit_' + this._options.className, this.rebuild.bind(this));
+ },
+
+ /**
+ * Initializes each applicable message, should be called whenever new
+ * messages are being displayed.
+ */
+ rebuild: function() {
+ var button, canEdit, element, elements = elBySelAll(this._options.messageSelector);
+
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (this._elements.has(element)) {
+ continue;
+ }
+
+ button = elBySel('.jsMessageEditButton', element);
+ if (button !== null) {
+ canEdit = elDataBool(element, 'can-edit');
+
+ if (this._options.canEditInline || elDataBool(element, 'can-edit-inline')) {
+ button.addEventListener(WCF_CLICK_EVENT, this._clickDropdown.bind(this, element));
+ button.classList.add('jsDropdownEnabled');
+
+ if (canEdit) {
+ button.addEventListener('dblclick', this._click.bind(this, element));
+ }
+ }
+ else if (canEdit) {
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this, element));
+ }
+ }
+
+ var messageBody = elBySel('.messageBody', element);
+ var messageFooter = elBySel('.messageFooter', element);
+ var messageHeader = elBySel('.messageHeader', element);
+
+ this._elements.set(element, {
+ button: button,
+ messageBody: messageBody,
+ messageBodyEditor: null,
+ messageFooter: messageFooter,
+ messageFooterButtons: elBySel('.messageFooterButtons', messageFooter),
+ messageHeader: messageHeader,
+ messageText: elBySel('.messageText', messageBody)
+ });
+ }
+ },
+
+ /**
+ * Handles clicks on the edit button or the edit dropdown item.
+ *
+ * @param {Element} element message element
+ * @param {?Event} event event object
+ * @protected
+ */
+ _click: function(element, event) {
+ if (element === null) element = this._activeDropdownElement;
+ if (event) event.preventDefault();
+
+ if (this._activeElement === null) {
+ this._activeElement = element;
+
+ this._prepare();
+
+ Ajax.api(this, {
+ actionName: 'beginEdit',
+ parameters: {
+ containerID: this._options.containerId,
+ objectID: this._getObjectId(element)
+ }
+ });
+ }
+ else {
+ UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+ }
+ },
+
+ /**
+ * Creates and opens the dropdown on first usage.
+ *
+ * @param {Element} element message element
+ * @param {Object} event event object
+ * @protected
+ */
+ _clickDropdown: function(element, event) {
+ event.preventDefault();
+
+ var button = event.currentTarget;
+ if (button.classList.contains('dropdownToggle')) {
+ return;
+ }
+
+ button.classList.add('dropdownToggle');
+ button.parentNode.classList.add('dropdown');
+ (function(button, element) {
+ button.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ this._activeDropdownElement = element;
+ UiReusableDropdown.toggleDropdown(this._options.dropdownIdentifier, button);
+ }).bind(this));
+ }).bind(this)(button, element);
+
+ // build dropdown
+ if (this._dropdownMenu === null) {
+ this._dropdownMenu = elCreate('ul');
+ this._dropdownMenu.className = 'dropdownMenu';
+
+ var items = this._dropdownGetItems();
+
+ EventHandler.fire('com.woltlab.wcf.inlineEditor', 'dropdownInit_' + this._options.dropdownIdentifier, {
+ items: items
+ });
+
+ this._dropdownBuild(items);
+
+ UiReusableDropdown.init(this._options.dropdownIdentifier, this._dropdownMenu);
+ UiReusableDropdown.registerCallback(this._options.dropdownIdentifier, this._dropdownToggle.bind(this));
+ }
+
+ setTimeout(function() {
+ Core.triggerEvent(button, WCF_CLICK_EVENT);
+ }, 10);
+ },
+
+ /**
+ * Creates the dropdown menu on first usage.
+ *
+ * @param {Object} items list of dropdown items
+ * @protected
+ */
+ _dropdownBuild: function(items) {
+ var item, label, listItem;
+ var callbackClick = this._clickDropdownItem.bind(this);
+
+ for (var i = 0, length = items.length; i < length; i++) {
+ item = items[i];
+ listItem = elCreate('li');
+ elData(listItem, 'item', item.item);
+
+ if (item.item === 'divider') {
+ listItem.className = 'dropdownDivider';
+ }
+ else {
+ label = elCreate('span');
+ label.textContent = Language.get(item.label);
+ listItem.appendChild(label);
+
+ if (item.item === 'editItem') {
+ listItem.addEventListener(WCF_CLICK_EVENT, this._click.bind(this, null));
+ }
+ else {
+ listItem.addEventListener(WCF_CLICK_EVENT, callbackClick);
+ }
+ }
+
+ this._dropdownMenu.appendChild(listItem);
+ }
+ },
+
+ /**
+ * Callback for dropdown toggle.
+ *
+ * @param {int} containerId container id
+ * @param {string} action toggle action, either 'open' or 'close'
+ * @protected
+ */
+ _dropdownToggle: function(containerId, action) {
+ var elementData = this._elements.get(this._activeDropdownElement);
+ elementData.button.parentNode.classList[(action === 'open' ? 'add' : 'remove')]('dropdownOpen');
+ elementData.messageFooterButtons.classList[(action === 'open' ? 'add' : 'remove')]('forceVisible');
+
+ if (action === 'open') {
+ var visibility = this._dropdownOpen();
+
+ EventHandler.fire('com.woltlab.wcf.inlineEditor', 'dropdownOpen_' + this._options.dropdownIdentifier, {
+ element: this._activeDropdownElement,
+ visibility: visibility
+ });
+
+ var item, listItem, visiblePredecessor = false;
+ for (var i = 0; i < this._dropdownMenu.childElementCount; i++) {
+ listItem = this._dropdownMenu.children[i];
+ item = elData(listItem, 'item');
+
+ if (item === 'divider') {
+ if (visiblePredecessor) {
+ elShow(listItem);
+
+ visiblePredecessor = false;
+ }
+ else {
+ elHide(listItem);
+ }
+ }
+ else {
+ if (objOwns(visibility, item) && visibility[item] === false) {
+ elHide(listItem);
+
+ // check if previous item was a divider
+ if (i > 0 && i + 1 === this._dropdownMenu.childElementCount) {
+ if (elData(listItem.previousElementSibling, 'item') === 'divider') {
+ elHide(listItem.previousElementSibling);
+ }
+ }
+ }
+ else {
+ elShow(listItem);
+
+ visiblePredecessor = true;
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Returns the list of dropdown items for this type.
+ *
+ * @return {Array<Object>} list of objects containing the type name and label
+ * @protected
+ */
+ _dropdownGetItems: function() {},
+
+ /**
+ * Invoked once the dropdown for this type is shown, expects a list of type name and a boolean value
+ * to represent the visibility of each item. Items that do not appear in this list will be considered
+ * visible.
+ *
+ * @return {Object<string, boolean>}
+ * @protected
+ */
+ _dropdownOpen: function() {},
+
+ /**
+ * Invoked whenever the user selects an item from the dropdown menu, the selected item is passed as argument.
+ *
+ * @param {string} item selected dropdown item
+ * @protected
+ */
+ _dropdownSelect: function(item) {},
+
+ /**
+ * Handles clicks on a dropdown item.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _clickDropdownItem: function(event) {
+ event.preventDefault();
+
+ //noinspection JSCheckFunctionSignatures
+ var item = elData(event.currentTarget, 'item');
+ var data = {
+ cancel: false,
+ element: this._activeDropdownElement,
+ item: item
+ };
+ EventHandler.fire('com.woltlab.wcf.inlineEditor', 'dropdownItemClick_' + this._options.dropdownIdentifier, data);
+
+ if (data.cancel === true) {
+ event.preventDefault();
+ }
+ else {
+ this._dropdownSelect(item);
+ }
+ },
+
+ /**
+ * Prepares the message for editor display.
+ *
+ * @protected
+ */
+ _prepare: function() {
+ var data = this._elements.get(this._activeElement);
+
+ var messageBodyEditor = elCreate('div');
+ messageBodyEditor.className = 'messageBody editor';
+ data.messageBodyEditor = messageBodyEditor;
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ messageBodyEditor.appendChild(icon);
+
+ DomUtil.insertAfter(messageBodyEditor, data.messageBody);
+
+ elHide(data.messageBody);
+ },
+
+ /**
+ * Shows the message editor.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showEditor: function(data) {
+ var id = this._getEditorId();
+ var elementData = this._elements.get(this._activeElement);
+
+ this._activeElement.classList.add('jsInvalidQuoteTarget');
+ var icon = DomTraverse.childByClass(elementData.messageBodyEditor, 'icon');
+ elRemove(icon);
+
+ var messageBody = elementData.messageBodyEditor;
+ var editor = elCreate('div');
+ editor.className = 'editorContainer';
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(editor, data.returnValues.template);
+ messageBody.appendChild(editor);
+
+ // bind buttons
+ var formSubmit = elBySel('.formSubmit', editor);
+
+ var buttonSave = elBySel('button[data-type="save"]', formSubmit);
+ buttonSave.addEventListener(WCF_CLICK_EVENT, this._save.bind(this));
+
+ var buttonCancel = elBySel('button[data-type="cancel"]', formSubmit);
+ buttonCancel.addEventListener(WCF_CLICK_EVENT, this._restoreMessage.bind(this));
+
+ EventHandler.add('com.woltlab.wcf.redactor', 'submitEditor_' + id, (function(data) {
+ data.cancel = true;
+
+ this._save();
+ }).bind(this));
+
+ // hide message header and footer
+ elHide(elementData.messageHeader);
+ elHide(elementData.messageFooter);
+
+ var editorElement = elById(id);
+ if (Environment.editor() === 'redactor') {
+ window.setTimeout((function() {
+ if (this._options.quoteManager) {
+ this._options.quoteManager.setAlternativeEditor(id);
+ }
+
+ UiScroll.element(this._activeElement);
+ }).bind(this), 250);
+ }
+ else {
+ editorElement.focus();
+ }
+ },
+
+ /**
+ * Restores the message view.
+ *
+ * @protected
+ */
+ _restoreMessage: function() {
+ var elementData = this._elements.get(this._activeElement);
+
+ this._destroyEditor();
+
+ elRemove(elementData.messageBodyEditor);
+ elementData.messageBodyEditor = null;
+
+ elShow(elementData.messageBody);
+ elShow(elementData.messageFooter);
+ elShow(elementData.messageHeader);
+ this._activeElement.classList.remove('jsInvalidQuoteTarget');
+
+ this._activeElement = null;
+
+ if (this._options.quoteManager) {
+ this._options.quoteManager.clearAlternativeEditor();
+ }
+ },
+
+ /**
+ * Saves the editor message.
+ *
+ * @protected
+ */
+ _save: function() {
+ var parameters = {
+ containerID: this._options.containerId,
+ data: {
+ message: ''
+ },
+ objectID: this._getObjectId(this._activeElement),
+ removeQuoteIDs: (this._options.quoteManager) ? this._options.quoteManager.getQuotesMarkedForRemoval() : []
+ };
+
+ var id = this._getEditorId();
+
+ // add any available settings
+ var settingsContainer = elById('settings_' + id);
+ if (settingsContainer) {
+ elBySelAll('input, select, textarea', settingsContainer, function (element) {
+ if (element.nodeName === 'INPUT' && (element.type === 'checkbox' || element.type === 'radio')) {
+ if (!element.checked) {
+ return;
+ }
+ }
+
+ var name = element.name;
+ if (parameters.hasOwnProperty(name)) {
+ throw new Error("Variable overshadowing, key '" + name + "' is already present.");
+ }
+
+ parameters[name] = element.value.trim();
+ });
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'getText_' + id, parameters.data);
+
+ var validateResult = this._validate(parameters);
+
+ if (!(validateResult instanceof Promise)) {
+ if (validateResult === false) {
+ validateResult = Promise.reject();
+ }
+ else {
+ validateResult = Promise.resolve();
+ }
+ }
+
+ validateResult.then(function () {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_' + id, parameters);
+
+ Ajax.api(this, {
+ actionName: 'save',
+ parameters: parameters
+ });
+
+ this._hideEditor();
+ }.bind(this), function(e) {
+ console.log('Validation of post edit failed: '+ e);
+ });
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @param {Object} parameters request parameters
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function(parameters) {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._activeElement, elRemove);
+
+ var data = {
+ api: this,
+ parameters: parameters,
+ valid: true,
+ promises: []
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_' + this._getEditorId(), data);
+
+ data.promises.push(Promise[data.valid ? 'resolve' : 'reject']());
+
+ return Promise.all(data.promises);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, message);
+ },
+
+ /**
+ * Shows the update message.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showMessage: function(data) {
+ var activeElement = this._activeElement;
+ var editorId = this._getEditorId();
+ var elementData = this._elements.get(activeElement);
+ var attachmentLists = elBySelAll('.attachmentThumbnailList, .attachmentFileList', elementData.messageFooter);
+
+ // set new content
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(DomTraverse.childByClass(elementData.messageBody, 'messageText'), data.returnValues.message);
+
+ // handle attachment list
+ //noinspection JSUnresolvedVariable
+ if (typeof data.returnValues.attachmentList === 'string') {
+ for (var i = 0, length = attachmentLists.length; i < length; i++) {
+ elRemove(attachmentLists[i]);
+ }
+
+ var element = elCreate('div');
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(element, data.returnValues.attachmentList);
+
+ var node;
+ while (element.childNodes.length) {
+ node = element.childNodes[element.childNodes.length - 1];
+ elementData.messageFooter.insertBefore(node, elementData.messageFooter.firstChild);
+ }
+ }
+
+ // handle poll
+ //noinspection JSUnresolvedVariable
+ if (typeof data.returnValues.poll === 'string') {
+ // find current poll
+ var poll = elBySel('.pollContainer', elementData.messageBody);
+ if (poll !== null) {
+ // poll contain is wrapped inside `.jsInlineEditorHideContent`
+ elRemove(poll.parentNode);
+ }
+
+ var pollContainer = elCreate('div');
+ pollContainer.className = 'jsInlineEditorHideContent';
+ //noinspection JSUnresolvedVariable
+ DomUtil.setInnerHtml(pollContainer, data.returnValues.poll);
+
+ DomUtil.prepend(pollContainer, elementData.messageBody);
+ }
+
+ this._restoreMessage();
+
+ this._updateHistory(this._getHash(this._getObjectId(activeElement)));
+
+ EventHandler.fire('com.woltlab.wcf.redactor', 'autosaveDestroy_' + editorId);
+
+ UiNotification.show();
+
+ if (this._options.quoteManager) {
+ this._options.quoteManager.clearAlternativeEditor();
+ this._options.quoteManager.countQuotes();
+ }
+ },
+
+ /**
+ * Hides the editor from view.
+ *
+ * @protected
+ */
+ _hideEditor: function() {
+ var elementData = this._elements.get(this._activeElement);
+ elHide(DomTraverse.childByClass(elementData.messageBodyEditor, 'editorContainer'));
+
+ var icon = elCreate('span');
+ icon.className = 'icon icon48 fa-spinner';
+ elementData.messageBodyEditor.appendChild(icon);
+ },
+
+ /**
+ * Restores the previously hidden editor.
+ *
+ * @protected
+ */
+ _restoreEditor: function() {
+ var elementData = this._elements.get(this._activeElement);
+ var icon = elBySel('.fa-spinner', elementData.messageBodyEditor);
+ elRemove(icon);
+
+ var editorContainer = DomTraverse.childByClass(elementData.messageBodyEditor, 'editorContainer');
+ if (editorContainer !== null) elShow(editorContainer);
+ },
+
+ /**
+ * Destroys the editor instance.
+ *
+ * @protected
+ */
+ _destroyEditor: function() {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveDestroy_' + this._getEditorId());
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'destroy_' + this._getEditorId());
+ },
+
+ /**
+ * Returns the hash added to the url after successfully editing a message.
+ *
+ * @param {int} objectId message object id
+ * @return string
+ * @protected
+ */
+ _getHash: function(objectId) {
+ return '#message' + objectId;
+ },
+
+ /**
+ * Updates the history to avoid old content when going back in the browser
+ * history.
+ *
+ * @param {string} hash location hash
+ * @protected
+ */
+ _updateHistory: function(hash) {
+ window.location.hash = hash;
+ },
+
+ /**
+ * Returns the unique editor id.
+ *
+ * @return {string} editor id
+ * @protected
+ */
+ _getEditorId: function() {
+ return this._options.editorPrefix + this._getObjectId(this._activeElement);
+ },
+
+ /**
+ * Returns the element's `data-object-id` value.
+ *
+ * @param {Element} element target element
+ * @return {int}
+ * @protected
+ */
+ _getObjectId: function(element) {
+ return ~~elData(element, 'object-id');
+ },
+
+ _ajaxFailure: function(data) {
+ var elementData = this._elements.get(this._activeElement);
+ var editor = elBySel('.redactor-layer', elementData.messageBodyEditor);
+
+ // handle errors occurring on editor load
+ if (editor === null) {
+ this._restoreMessage();
+
+ return true;
+ }
+
+ this._restoreEditor();
+
+ //noinspection JSUnresolvedVariable
+ if (!data || data.returnValues === undefined || data.returnValues.realErrorMessage === undefined) {
+ return true;
+ }
+
+ //noinspection JSUnresolvedVariable
+ elInnerError(editor, data.returnValues.realErrorMessage);
+
+ return false;
+ },
+
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'beginEdit':
+ this._showEditor(data);
+ break;
+
+ case 'save':
+ this._showMessage(data);
+ break;
+ }
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: this._options.className,
+ interfaceName: 'wcf\\data\\IMessageInlineEditorAction'
+ },
+ silent: true
+ };
+ },
+
+ /** @deprecated 3.0 - used only for backward compatibility with `WCF.Message.InlineEditor` */
+ legacyEdit: function(containerId) {
+ this._click(elById(containerId), null);
+ }
+ };
+
+ return UiMessageInlineEditor;
+});
+
+/**
+ * Provides access and editing of message properties.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Manager
+ */
+define('WoltLabSuite/Core/Ui/Message/Manager',['Ajax', 'Core', 'Dictionary', 'Language', 'Dom/ChangeListener', 'Dom/Util'], function(Ajax, Core, Dictionary, Language, DomChangeListener, DomUtil) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ getPermission: function() {},
+ getPropertyValue: function() {},
+ update: function() {},
+ updateItems: function() {},
+ updateAllItems: function() {},
+ setNote: function() {},
+ _update: function() {},
+ _updateState: function() {},
+ _toggleMessageStatus: function() {},
+ _getAttributeName: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @param {Object} options initialization options
+ * @constructor
+ */
+ function UiMessageManager(options) { this.init(options); }
+ UiMessageManager.prototype = {
+ /**
+ * Initializes a new manager instance.
+ *
+ * @param {Object} options initialization options
+ */
+ init: function(options) {
+ this._elements = null;
+ this._options = Core.extend({
+ className: '',
+ selector: ''
+ }, options);
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Message/Manager' + this._options.className, this.rebuild.bind(this));
+ },
+
+ /**
+ * Rebuilds the list of observed messages. You should call this method whenever a
+ * message has been either added or removed from the document.
+ */
+ rebuild: function() {
+ this._elements = new Dictionary();
+
+ var element, elements = elBySelAll(this._options.selector);
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+
+ this._elements.set(elData(element, 'object-id'), element);
+ }
+ },
+
+ /**
+ * Returns a boolean value for the given permission. The permission should not start
+ * with "can" or "can-" as this is automatically assumed by this method.
+ *
+ * @param {int} objectId message object id
+ * @param {string} permission permission name without a leading "can" or "can-"
+ * @return {boolean} true if permission was set and is either 'true' or '1'
+ */
+ getPermission: function(objectId, permission) {
+ permission = 'can-' + this._getAttributeName(permission);
+ var element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ }
+
+ return elDataBool(element, permission);
+ },
+
+ /**
+ * Returns the given property value from a message, optionally supporting a boolean return value.
+ *
+ * @param {int} objectId message object id
+ * @param {string} propertyName attribute name
+ * @param {boolean} asBool attempt to interpret property value as boolean
+ * @return {(boolean|string)} raw property value or boolean if requested
+ */
+ getPropertyValue: function(objectId, propertyName, asBool) {
+ var element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ }
+
+ return window[(asBool ? 'elDataBool' : 'elData')](element, this._getAttributeName(propertyName));
+ },
+
+ /**
+ * Invokes a method for given message object id in order to alter its state or properties.
+ *
+ * @param {int} objectId message object id
+ * @param {string} actionName action name used for the ajax api
+ * @param {Object=} parameters optional list of parameters included with the ajax request
+ */
+ update: function(objectId, actionName, parameters) {
+ Ajax.api(this, {
+ actionName: actionName,
+ parameters: parameters || {},
+ objectIDs: [objectId]
+ });
+ },
+
+ /**
+ * Updates properties and states for given object ids. Keep in mind that this method does
+ * not support setting individual properties per message, instead all property changes
+ * are applied to all matching message objects.
+ *
+ * @param {Array<int>} objectIds list of message object ids
+ * @param {Object} data list of updated properties
+ */
+ updateItems: function(objectIds, data) {
+ if (!Array.isArray(objectIds)) {
+ objectIds = [objectIds];
+ }
+
+ var element;
+ for (var i = 0, length = objectIds.length; i < length; i++) {
+ element = this._elements.get(objectIds[i]);
+ if (element === undefined) {
+ continue;
+ }
+
+ for (var key in data) {
+ if (data.hasOwnProperty(key)) {
+ this._update(element, key, data[key]);
+ }
+ }
+ }
+ },
+
+ /**
+ * Bulk updates the properties and states for all observed messages at once.
+ *
+ * @param {Object} data list of updated properties
+ */
+ updateAllItems: function(data) {
+ var objectIds = [];
+ this._elements.forEach((function(element, objectId) {
+ objectIds.push(objectId);
+ }).bind(this));
+
+ this.updateItems(objectIds, data);
+ },
+
+ /**
+ * Sets or removes a message note identified by its unique CSS class.
+ *
+ * @param {int} objectId message object id
+ * @param {string} className unique CSS class
+ * @param {string} htmlContent HTML content
+ */
+ setNote: function (objectId, className, htmlContent) {
+ var element = this._elements.get(objectId);
+ if (element === undefined) {
+ throw new Error("Unknown object id '" + objectId + "' for selector '" + this._options.selector + "'");
+ }
+
+ var messageFooterNotes = elBySel('.messageFooterNotes', element);
+ var note = elBySel('.' + className, messageFooterNotes);
+ if (htmlContent) {
+ if (note === null) {
+ note = elCreate('p');
+ note.className = 'messageFooterNote ' + className;
+
+ messageFooterNotes.appendChild(note);
+ }
+
+ note.innerHTML = htmlContent;
+ }
+ else if (note !== null) {
+ elRemove(note);
+ }
+ },
+
+ /**
+ * Updates a single property of a message element.
+ *
+ * @param {Element} element message element
+ * @param {string} propertyName property name
+ * @param {?} propertyValue property value, will be implicitly converted to string
+ * @protected
+ */
+ _update: function(element, propertyName, propertyValue) {
+ elData(element, this._getAttributeName(propertyName), propertyValue);
+
+ // handle special properties
+ var propertyValueBoolean = (propertyValue == 1 || propertyValue === true || propertyValue === 'true');
+ this._updateState(element, propertyName, propertyValue, propertyValueBoolean);
+ },
+
+ /**
+ * Updates the message element's state based upon a property change.
+ *
+ * @param {Element} element message element
+ * @param {string} propertyName property name
+ * @param {?} propertyValue property value
+ * @param {boolean} propertyValueBoolean true if `propertyValue` equals either 'true' or '1'
+ * @protected
+ */
+ _updateState: function(element, propertyName, propertyValue, propertyValueBoolean) {
+ switch (propertyName) {
+ case 'isDeleted':
+ element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDeleted');
+ this._toggleMessageStatus(element, 'jsIconDeleted', 'wcf.message.status.deleted', 'red', propertyValueBoolean);
+
+ break;
+
+ case 'isDisabled':
+ element.classList[(propertyValueBoolean ? 'add' : 'remove')]('messageDisabled');
+ this._toggleMessageStatus(element, 'jsIconDisabled', 'wcf.message.status.disabled', 'green', propertyValueBoolean);
+
+ break;
+ }
+ },
+
+ /**
+ * Toggles the message status bade for provided element.
+ *
+ * @param {Element} element message element
+ * @param {string} className badge class name
+ * @param {string} phrase language phrase
+ * @param {string} badgeColor color css class
+ * @param {boolean} addBadge add or remove badge
+ * @protected
+ */
+ _toggleMessageStatus: function(element, className, phrase, badgeColor, addBadge) {
+ var messageStatus = elBySel('.messageStatus', element);
+ if (messageStatus === null) {
+ var messageHeaderMetaData = elBySel('.messageHeaderMetaData', element);
+ if (messageHeaderMetaData === null) {
+ // can't find appropriate location to insert badge
+ return;
+ }
+
+ messageStatus = elCreate('ul');
+ messageStatus.className = 'messageStatus';
+ DomUtil.insertAfter(messageStatus, messageHeaderMetaData);
+ }
+
+ var badge = elBySel('.' + className, messageStatus);
+
+ if (addBadge) {
+ if (badge !== null) {
+ // badge already exists
+ return;
+ }
+
+ badge = elCreate('span');
+ badge.className = 'badge label ' + badgeColor + ' ' + className;
+ badge.textContent = Language.get(phrase);
+
+ var listItem = elCreate('li');
+ listItem.appendChild(badge);
+ messageStatus.appendChild(listItem);
+ }
+ else {
+ if (badge === null) {
+ // badge does not exist
+ return;
+ }
+
+ elRemove(badge.parentNode);
+ }
+ },
+
+ /**
+ * Transforms camel-cased property names into their attribute equivalent.
+ *
+ * @param {string} propertyName camel-cased property name
+ * @return {string} equivalent attribute name
+ * @protected
+ */
+ _getAttributeName: function(propertyName) {
+ if (propertyName.indexOf('-') !== -1) {
+ return propertyName;
+ }
+
+ var attributeName = '';
+ var str, tmp = propertyName.split(/([A-Z][a-z]+)/);
+ for (var i = 0, length = tmp.length; i < length; i++) {
+ str = tmp[i];
+ if (str.length) {
+ if (attributeName.length) attributeName += '-';
+ attributeName += str.toLowerCase();
+ }
+ }
+
+ return attributeName;
+ },
+
+ _ajaxSuccess: function() {
+ throw new Error("Method _ajaxSuccess() must be implemented by deriving functions.");
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: this._options.className
+ }
+ };
+ }
+ };
+
+ return UiMessageManager;
+});
+/**
+ * Handles user interaction with the quick reply feature.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Reply
+ */
+define('WoltLabSuite/Core/Ui/Message/Reply',['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Scroll', 'EventKey', 'User', 'WoltLabSuite/Core/Controller/Captcha'],
+ function(Ajax, Core, EventHandler, Language, DomChangeListener, DomUtil, DomTraverse, UiDialog, UiNotification, UiScroll, EventKey, User, ControllerCaptcha) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _submitGuestDialog: function() {},
+ _submit: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showLoadingOverlay: function() {},
+ _hideLoadingOverlay: function() {},
+ _reset: function() {},
+ _handleError: function() {},
+ _getEditor: function() {},
+ _insertMessage: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiMessageReply(options) { this.init(options); }
+ UiMessageReply.prototype = {
+ /**
+ * Initializes a new quick reply field.
+ *
+ * @param {Object} options configuration options
+ */
+ init: function(options) {
+ this._options = Core.extend({
+ ajax: {
+ className: ''
+ },
+ quoteManager: null,
+ successMessage: 'wcf.global.success.add'
+ }, options);
+
+ this._container = elById('messageQuickReply');
+ this._content = elBySel('.messageContent', this._container);
+ this._textarea = elById('text');
+ this._editor = null;
+ this._guestDialogId = '';
+ this._loadingOverlay = null;
+
+ // prevent marking of text for quoting
+ elBySel('.message', this._container).classList.add('jsInvalidQuoteTarget');
+
+ // handle submit button
+ var submitCallback = this._submit.bind(this);
+ var submitButton = elBySel('button[data-type="save"]', this._container);
+ submitButton.addEventListener(WCF_CLICK_EVENT, submitCallback);
+
+ // bind reply button
+ var replyButtons = elBySelAll('.jsQuickReply');
+ for (var i = 0, length = replyButtons.length; i < length; i++) {
+ replyButtons[i].addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+
+ this._getEditor().WoltLabReply.showEditor();
+
+ UiScroll.element(this._container, (function() {
+ this._getEditor().WoltLabCaret.endOfEditor();
+ }).bind(this));
+ }).bind(this));
+ }
+ },
+
+ /**
+ * Submits the guest dialog.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _submitGuestDialog: function(event) {
+ // only submit when enter key is pressed
+ if (event.type === 'keypress' && !EventKey.Enter(event)) {
+ return;
+ }
+
+ var usernameInput = elBySel('input[name=username]', event.currentTarget.closest('.dialogContent'));
+ if (usernameInput.value === '') {
+ elInnerError(usernameInput, Language.get('wcf.global.form.error.empty'));
+ usernameInput.closest('dl').classList.add('formError');
+
+ return;
+ }
+
+ var parameters = {
+ parameters: {
+ data: {
+ username: usernameInput.value
+ }
+ }
+ };
+
+ //noinspection JSCheckFunctionSignatures
+ var captchaId = elData(event.currentTarget, 'captcha-id');
+ if (ControllerCaptcha.has(captchaId)) {
+ var data = ControllerCaptcha.getData(captchaId);
+ if (data instanceof Promise) {
+ data.then((function (data) {
+ parameters = Core.extend(parameters, data);
+ this._submit(undefined, parameters);
+ }).bind(this));
+ }
+ else {
+ parameters = Core.extend(parameters, ControllerCaptcha.getData(captchaId));
+ this._submit(undefined, parameters);
+ }
+ }
+ else {
+ this._submit(undefined, parameters);
+ }
+ },
+
+ /**
+ * Validates the message and submits it to the server.
+ *
+ * @param {Event?} event event object
+ * @param {Object?} additionalParameters additional parameters sent to the server
+ * @protected
+ */
+ _submit: function(event, additionalParameters) {
+ if (event) {
+ event.preventDefault();
+ }
+
+ // Ignore requests to submit the message while a previous request is still pending.
+ if (this._content.classList.contains('loading')) {
+ if (!this._guestDialogId || !UiDialog.isOpen(this._guestDialogId)) {
+ return;
+ }
+ }
+
+ if (!this._validate()) {
+ // validation failed, bail out
+ return;
+ }
+
+ this._showLoadingOverlay();
+
+ // build parameters
+ var parameters = DomUtil.getDataAttributes(this._container, 'data-', true, true);
+ parameters.data = { message: this._getEditor().code.get() };
+ parameters.removeQuoteIDs = (this._options.quoteManager) ? this._options.quoteManager.getQuotesMarkedForRemoval() : [];
+
+ // add any available settings
+ var settingsContainer = elById('settings_text');
+ if (settingsContainer) {
+ elBySelAll('input, select, textarea', settingsContainer, function (element) {
+ if (element.nodeName === 'INPUT' && (element.type === 'checkbox' || element.type === 'radio')) {
+ if (!element.checked) {
+ return;
+ }
+ }
+
+ var name = element.name;
+ if (parameters.hasOwnProperty(name)) {
+ throw new Error("Variable overshadowing, key '" + name + "' is already present.");
+ }
+
+ parameters[name] = element.value.trim();
+ });
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_text', parameters.data);
+
+ if (!User.userId && !additionalParameters) {
+ parameters.requireGuestDialog = true;
+ }
+
+ Ajax.api(this, Core.extend({
+ parameters: parameters
+ }, additionalParameters));
+ },
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ *
+ * @return {boolean} validation result
+ * @protected
+ */
+ _validate: function() {
+ // remove all existing error elements
+ elBySelAll('.innerError', this._container, elRemove);
+
+ // check if editor contains actual content
+ if (this._getEditor().utils.isEmpty()) {
+ this.throwError(this._textarea, Language.get('wcf.global.form.error.empty'));
+ return false;
+ }
+
+ var data = {
+ api: this,
+ editor: this._getEditor(),
+ message: this._getEditor().code.get(),
+ valid: true
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_text', data);
+
+ return (data.valid !== false);
+ },
+
+ /**
+ * Throws an error by adding an inline error to target element.
+ *
+ * @param {Element} element erroneous element
+ * @param {string} message error message
+ */
+ throwError: function(element, message) {
+ elInnerError(element, (message === 'empty' ? Language.get('wcf.global.form.error.empty') : message));
+ },
+
+ /**
+ * Displays a loading spinner while the request is processed by the server.
+ *
+ * @protected
+ */
+ _showLoadingOverlay: function() {
+ if (this._loadingOverlay === null) {
+ this._loadingOverlay = elCreate('div');
+ this._loadingOverlay.className = 'messageContentLoadingOverlay';
+ this._loadingOverlay.innerHTML = '<span class="icon icon96 fa-spinner"></span>';
+ }
+
+ this._content.classList.add('loading');
+ this._content.appendChild(this._loadingOverlay);
+ },
+
+ /**
+ * Hides the loading spinner.
+ *
+ * @protected
+ */
+ _hideLoadingOverlay: function() {
+ this._content.classList.remove('loading');
+
+ var loadingOverlay = elBySel('.messageContentLoadingOverlay', this._content);
+ if (loadingOverlay !== null) {
+ loadingOverlay.parentNode.removeChild(loadingOverlay);
+ }
+ },
+
+ /**
+ * Resets the editor contents and notifies event listeners.
+ *
+ * @protected
+ */
+ _reset: function() {
+ this._getEditor().code.set('<p>\u200b</p>');
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'reset_text');
+ },
+
+ /**
+ * Handles errors occurred during server processing.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _handleError: function(data) {
+ var parameters = {
+ api: this,
+ cancel: false,
+ returnValues: data.returnValues
+ };
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'handleError_text', parameters);
+
+ if (parameters.cancel !== true) {
+ //noinspection JSUnresolvedVariable
+ this.throwError(this._textarea, data.returnValues.realErrorMessage);
+ }
+ },
+
+ /**
+ * Returns the current editor instance.
+ *
+ * @return {Object} editor instance
+ * @protected
+ */
+ _getEditor: function() {
+ if (this._editor === null) {
+ if (typeof window.jQuery === 'function') {
+ this._editor = window.jQuery(this._textarea).data('redactor');
+ }
+ else {
+ throw new Error("Unable to access editor, jQuery has not been loaded yet.");
+ }
+ }
+
+ return this._editor;
+ },
+
+ /**
+ * Inserts the rendered message into the post list, unless the post is on the next
+ * page in which case a redirect will be performed instead.
+ *
+ * @param {Object} data response data
+ * @protected
+ */
+ _insertMessage: function(data) {
+ this._getEditor().WoltLabAutosave.reset();
+
+ // redirect to new page
+ //noinspection JSUnresolvedVariable
+ if (data.returnValues.url) {
+ //noinspection JSUnresolvedVariable
+ if (window.location == data.returnValues.url) {
+ window.location.reload();
+ }
+ window.location = data.returnValues.url;
+ }
+ else {
+ //noinspection JSUnresolvedVariable
+ if (data.returnValues.template) {
+ var elementId;
+
+ // insert HTML
+ if (elData(this._container, 'sort-order') === 'DESC') {
+ //noinspection JSUnresolvedVariable
+ DomUtil.insertHtml(data.returnValues.template, this._container, 'after');
+ elementId = DomUtil.identify(this._container.nextElementSibling);
+ }
+ else {
+ var insertBefore = this._container;
+ if (insertBefore.previousElementSibling && insertBefore.previousElementSibling.classList.contains('messageListPagination')) {
+ insertBefore = insertBefore.previousElementSibling;
+ }
+
+ //noinspection JSUnresolvedVariable
+ DomUtil.insertHtml(data.returnValues.template, insertBefore, 'before');
+ elementId = DomUtil.identify(insertBefore.previousElementSibling);
+ }
+
+ // update last post time
+ //noinspection JSUnresolvedVariable
+ elData(this._container, 'last-post-time', data.returnValues.lastPostTime);
+
+ window.history.replaceState(undefined, '', '#' + elementId);
+ UiScroll.element(elById(elementId));
+ }
+
+ UiNotification.show(Language.get(this._options.successMessage));
+
+ if (this._options.quoteManager) {
+ this._options.quoteManager.countQuotes();
+ }
+
+ DomChangeListener.trigger();
+ }
+ },
+
+ /**
+ * @param {{returnValues:{guestDialog:string,guestDialogID:string}}} data
+ * @protected
+ */
+ _ajaxSuccess: function(data) {
+ if (!User.userId && !data.returnValues.guestDialogID) {
+ throw new Error("Missing 'guestDialogID' return value for guest.");
+ }
+
+ if (!User.userId && data.returnValues.guestDialog) {
+ UiDialog.openStatic(data.returnValues.guestDialogID, data.returnValues.guestDialog, {
+ closable: false,
+ onClose: function() {
+ if (ControllerCaptcha.has(data.returnValues.guestDialogID)) {
+ ControllerCaptcha.delete(data.returnValues.guestDialogID);
+ }
+ },
+ title: Language.get('wcf.global.confirmation.title')
+ });
+
+ var dialog = UiDialog.getDialog(data.returnValues.guestDialogID);
+ elBySel('input[type=submit]', dialog.content).addEventListener(WCF_CLICK_EVENT, this._submitGuestDialog.bind(this));
+ elBySel('input[type=text]', dialog.content).addEventListener('keypress', this._submitGuestDialog.bind(this));
+
+ this._guestDialogId = data.returnValues.guestDialogID;
+ }
+ else {
+ this._insertMessage(data);
+
+ if (!User.userId) {
+ UiDialog.close(data.returnValues.guestDialogID);
+ }
+
+ this._reset();
+
+ this._hideLoadingOverlay();
+ }
+ },
+
+ _ajaxFailure: function(data) {
+ this._hideLoadingOverlay();
+
+ //noinspection JSUnresolvedVariable
+ if (data === null || data.returnValues === undefined || data.returnValues.realErrorMessage === undefined) {
+ return true;
+ }
+
+ this._handleError(data);
+
+ return false;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'quickReply',
+ className: this._options.ajax.className,
+ interfaceName: 'wcf\\data\\IMessageQuickReplyAction'
+ },
+ silent: true
+ };
+ }
+ };
+
+ return UiMessageReply;
+});
+
+/**
+ * Provides buttons to share a page through multiple social community sites.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Message/Share
+ */
+define('WoltLabSuite/Core/Ui/Message/Share',['EventHandler', 'StringUtil'], function(EventHandler, StringUtil) {
+ "use strict";
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Message/Share
+ */
+ return {
+ _pageDescription: '',
+ _pageUrl: '',
+
+ init: function() {
+ var title = elBySel('meta[property="og:title"]');
+ if (title !== null) this._pageDescription = encodeURIComponent(title.content);
+ var url = elBySel('meta[property="og:url"]');
+ if (url !== null) this._pageUrl = encodeURIComponent(url.content);
+
+ elBySelAll('.jsMessageShareButtons', null, (function(container) {
+ container.classList.remove('jsMessageShareButtons');
+
+ var pageUrl = encodeURIComponent(StringUtil.unescapeHTML(elData(container, 'url') || ''));
+ if (!pageUrl) {
+ pageUrl = this._pageUrl;
+ }
+
+ var providers = {
+ facebook: {
+ link: elBySel('.jsShareFacebook', container),
+ share: (function(event) { this._share('facebook', 'https://www.facebook.com/sharer.php?u={pageURL}&t={text}', true, pageUrl); }).bind(this)
+ },
+ google: {
+ link: elBySel('.jsShareGoogle', container),
+ share: (function(event) { this._share('google', 'https://plus.google.com/share?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ reddit: {
+ link: elBySel('.jsShareReddit', container),
+ share: (function(event) { this._share('reddit', 'https://ssl.reddit.com/submit?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ twitter: {
+ link: elBySel('.jsShareTwitter', container),
+ share: (function(event) { this._share('twitter', 'https://twitter.com/share?url={pageURL}&text={text}', false, pageUrl); }).bind(this)
+ },
+ linkedIn: {
+ link: elBySel('.jsShareLinkedIn', container),
+ share: (function(event) { this._share('linkedIn', 'https://www.linkedin.com/cws/share?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ pinterest: {
+ link: elBySel('.jsSharePinterest', container),
+ share: (function(event) { this._share('pinterest', 'https://www.pinterest.com/pin/create/link/?url={pageURL}&description={text}', false, pageUrl); }).bind(this)
+ },
+ xing: {
+ link: elBySel('.jsShareXing', container),
+ share: (function(event) { this._share('xing', 'https://www.xing.com/social_plugins/share?url={pageURL}', false, pageUrl); }).bind(this)
+ },
+ whatsApp: {
+ link: elBySel('.jsShareWhatsApp', container),
+ share: (function() {
+ window.location.href = 'whatsapp://send?text=' + this._pageDescription + '%20' + pageUrl;
+ }).bind(this)
+ }
+ };
+
+ EventHandler.fire('com.woltlab.wcf.message.share', 'shareProvider', {
+ container: container,
+ providers: providers,
+ pageDescription: this._pageDescription,
+ pageUrl: this._pageUrl
+ });
+
+ for (var provider in providers) {
+ if (providers.hasOwnProperty(provider)) {
+ if (providers[provider].link !== null) {
+ providers[provider].link.addEventListener(WCF_CLICK_EVENT, providers[provider].share);
+ }
+ }
+ }
+ }).bind(this));
+ },
+
+ _share: function(objectName, url, appendUrl, pageUrl) {
+ // fallback for plugins
+ if (!pageUrl) {
+ pageUrl = this._pageUrl;
+ }
+
+ window.open(
+ url.replace(/\{pageURL}/, pageUrl).replace(/\{text}/, this._pageDescription + (appendUrl ? "%20" + pageUrl : "")),
+ objectName,
+ 'height=600,width=600'
+ );
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Page/Search',['Ajax', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function(Ajax, EventKey, Language, StringUtil, DomUtil, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ open: function() {},
+ _search: function() {},
+ _click: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callbackSelect, _resultContainer, _resultList, _searchInput = null;
+
+ return {
+ open: function(callbackSelect) {
+ _callbackSelect = callbackSelect;
+
+ UiDialog.open(this);
+ },
+
+ _search: function (event) {
+ event.preventDefault();
+
+ var inputContainer = _searchInput.parentNode;
+
+ var value = _searchInput.value.trim();
+ if (value.length < 3) {
+ elInnerError(inputContainer, Language.get('wcf.page.search.error.tooShort'));
+ return;
+ }
+ else {
+ elInnerError(inputContainer, false);
+ }
+
+ Ajax.api(this, {
+ parameters: {
+ searchString: value
+ }
+ });
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ var page = event.currentTarget;
+ var pageTitle = elBySel('h3', page).textContent.replace(/['"]/g, '');
+
+ _callbackSelect(elData(page, 'page-id') + '#' + pageTitle);
+
+ UiDialog.close(this);
+ },
+
+ _ajaxSuccess: function(data) {
+ var html = '', page;
+ //noinspection JSUnresolvedVariable
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ page = data.returnValues[i];
+
+ html += '<li>'
+ + '<div class="containerHeadline pointer" data-page-id="' + page.pageID + '">'
+ + '<h3>' + StringUtil.escapeHTML(page.name) + '</h3>'
+ + '<small>' + StringUtil.escapeHTML(page.displayLink) + '</small>'
+ + '</div>'
+ + '</li>';
+ }
+
+ _resultList.innerHTML = html;
+
+ window[html ? 'elShow' : 'elHide'](_resultContainer);
+
+ if (html) {
+ elBySelAll('.containerHeadline', _resultList, (function(item) {
+ item.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }).bind(this));
+ }
+ else {
+ elInnerError(_searchInput.parentNode, Language.get('wcf.page.search.error.noResults'));
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ actionName: 'search',
+ className: 'wcf\\data\\page\\PageAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiPageSearch',
+ options: {
+ onSetup: (function() {
+ var callbackSearch = this._search.bind(this);
+
+ _searchInput = elById('wcfUiPageSearchInput');
+ _searchInput.addEventListener('keydown', function(event) {
+ if (EventKey.Enter(event)) {
+ callbackSearch(event);
+ }
+ });
+
+ _searchInput.nextElementSibling.addEventListener(WCF_CLICK_EVENT, callbackSearch);
+
+ _resultContainer = elById('wcfUiPageSearchResultContainer');
+ _resultList = elById('wcfUiPageSearchResultList');
+ }).bind(this),
+ onShow: function() {
+ _searchInput.focus();
+ },
+ title: Language.get('wcf.page.search')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiPageSearchInput">' + Language.get('wcf.page.search.name') + '</label></dt>'
+ + '<dd>'
+ + '<div class="inputAddon">'
+ + '<input type="text" id="wcfUiPageSearchInput" class="long">'
+ + '<a href="#" class="inputSuffix"><span class="icon icon16 fa-search"></span></a>'
+ + '</div>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<section id="wcfUiPageSearchResultContainer" class="section" style="display: none;">'
+ + '<header class="sectionHeader">'
+ + '<h2 class="sectionTitle">' + Language.get('wcf.page.search.results') + '</h2>'
+ + '</header>'
+ + '<ol id="wcfUiPageSearchResultList" class="containerList"></ol>'
+ + '</section>'
+ };
+ }
+ };
+});
+
+/**
+ * Sortable lists with optimized handling per device sizes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Sortable/List
+ */
+define('WoltLabSuite/Core/Ui/Sortable/List',['Core', 'Ui/Screen'], function (Core, UiScreen) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _enable: function() {},
+ _disable: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiSortableList(options) { this.init(options); }
+ UiSortableList.prototype = {
+ /**
+ * Initializes the sortable list controller.
+ *
+ * @param {Object} options initialization options for `WCF.Sortable.List`
+ */
+ init: function (options) {
+ this._options = Core.extend({
+ containerId: '',
+ className: '',
+ offset: 0,
+ options: {},
+ isSimpleSorting: false,
+ additionalParameters: {}
+ }, options);
+
+ UiScreen.on('screen-sm-md', {
+ match: this._enable.bind(this, true),
+ unmatch: this._disable.bind(this),
+ setup: this._enable.bind(this, true)
+ });
+
+ UiScreen.on('screen-lg', {
+ match: this._enable.bind(this, false),
+ unmatch: this._disable.bind(this),
+ setup: this._enable.bind(this, false)
+ });
+ },
+
+ /**
+ * Enables sorting with an optional sort handle.
+ *
+ * @param {boolean} hasHandle true if sort can only be started with the sort handle
+ * @protected
+ */
+ _enable: function (hasHandle) {
+ var options = this._options.options;
+ if (hasHandle) options.handle = '.sortableNodeHandle';
+
+ new window.WCF.Sortable.List(
+ this._options.containerId,
+ this._options.className,
+ this._options.offset,
+ options,
+ this._options.isSimpleSorting,
+ this._options.additionalParameters
+ );
+ },
+
+ /**
+ * Disables sorting for registered containers.
+ *
+ * @protected
+ */
+ _disable: function () {
+ window.jQuery('#' + this._options.containerId + ' .sortableList')[(this._options.isSimpleSorting ? 'sortable' : 'nestedSortable')]('destroy');
+ }
+ };
+
+ return UiSortableList;
+});
+/**
+ * Handles the data to create and edit a poll in a form created via form builder.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Poll/Editor
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/Poll/Editor',[
+ 'Core',
+ 'Dom/Util',
+ 'EventHandler',
+ 'EventKey',
+ 'Language',
+ 'WoltLabSuite/Core/Date/Picker',
+ 'WoltLabSuite/Core/Ui/Sortable/List'
+], function(
+ Core,
+ DomUtil,
+ EventHandler,
+ EventKey,
+ Language,
+ DatePicker,
+ UiSortableList
+) {
+ "use strict";
+
+ function UiPollEditor(containerId, pollOptions, wysiwygId, options) {
+ this.init(containerId, pollOptions, wysiwygId, options);
+ }
+ UiPollEditor.prototype = {
+ /**
+ * Initializes the poll editor.
+ *
+ * @param {string} containerId id of the poll options container
+ * @param {object[]} pollOptions existing poll options
+ * @param {string} wysiwygId id of the related wysiwyg editor
+ * @param {object} options additional poll options
+ */
+ init: function(containerId, pollOptions, wysiwygId, options) {
+ this._container = elById(containerId);
+ if (this._container === null) {
+ throw new Error("Unknown poll editor container with id '" + containerId + "'.");
+ }
+
+ this._wysiwygId = wysiwygId;
+ if (wysiwygId !== '' && elById(wysiwygId) === null) {
+ throw new Error("Unknown wysiwyg field with id '" + wysiwygId + "'.");
+ }
+
+ this.questionField = elById(this._wysiwygId + 'Poll_question');
+
+ var optionLists = elByClass('sortableList', this._container);
+ if (optionLists.length === 0) {
+ throw new Error("Cannot find poll options list for container with id '" + containerId + "'.");
+ }
+ this.optionList = optionLists[0];
+
+ this.endTimeField = elById(this._wysiwygId + 'Poll_endTime');
+ this.maxVotesField = elById(this._wysiwygId + 'Poll_maxVotes');
+ this.isChangeableYesField = elById(this._wysiwygId + 'Poll_isChangeable');
+ this.isChangeableNoField = elById(this._wysiwygId + 'Poll_isChangeable_no');
+ this.isPublicYesField = elById(this._wysiwygId + 'Poll_isPublic');
+ this.isPublicNoField = elById(this._wysiwygId + 'Poll_isPublic_no');
+ this.resultsRequireVoteYesField = elById(this._wysiwygId + 'Poll_resultsRequireVote');
+ this.resultsRequireVoteNoField = elById(this._wysiwygId + 'Poll_resultsRequireVote_no');
+ this.sortByVotesYesField = elById(this._wysiwygId + 'Poll_sortByVotes');
+ this.sortByVotesNoField = elById(this._wysiwygId + 'Poll_sortByVotes_no');
+
+ this._optionCount = 0;
+ this._options = Core.extend({
+ isAjax: false,
+ maxOptions: 20
+ }, options);
+
+ this._createOptionList(pollOptions || []);
+
+ new UiSortableList({
+ containerId: containerId,
+ options: {
+ toleranceElement: '> div'
+ }
+ });
+
+ if (this._options.isAjax) {
+ var events = ['handleError', 'reset', 'submit', 'validate'];
+ for (var i = 0, length = events.length; i < length; i++) {
+ var event = events[i];
+
+ EventHandler.add(
+ 'com.woltlab.wcf.redactor2',
+ event + '_' + this._wysiwygId,
+ this['_' + event].bind(this)
+ );
+ }
+ }
+ else {
+ var form = this._container.closest('form');
+ if (form === null) {
+ throw new Error("Cannot find form for container with id '" + containerId + "'.");
+ }
+
+ form.addEventListener('submit', this._submit.bind(this));
+ }
+ },
+
+ /**
+ * Adds an option based on below the option for which the `Add Option` button has
+ * been clicked.
+ *
+ * @param {Event} event icon click event
+ */
+ _addOption: function(event) {
+ event.preventDefault();
+
+ if (this._optionCount === this._options.maxOptions) {
+ return false;
+ }
+
+ this._createOption(
+ undefined,
+ undefined,
+ event.currentTarget.closest('li')
+ );
+ },
+
+ /**
+ * Creates a new option based on the given data or an empty option if no option data
+ * is given.
+ *
+ * @param {string} optionValue value of the option
+ * @param {integer} optionId id of the option
+ * @param {Element?} insertAfter optional element after which the new option is added
+ * @private
+ */
+ _createOption: function(optionValue, optionId, insertAfter) {
+ optionValue = optionValue || '';
+ optionId = ~~optionId || 0;
+
+ var listItem = elCreate('LI');
+ listItem.className = 'sortableNode';
+ elData(listItem, 'option-id', optionId);
+
+ if (insertAfter) {
+ DomUtil.insertAfter(listItem, insertAfter);
+ }
+ else {
+ this.optionList.appendChild(listItem);
+ }
+
+ var pollOptionInput = elCreate('div');
+ pollOptionInput.className = 'pollOptionInput';
+ listItem.appendChild(pollOptionInput);
+
+ var sortHandle = elCreate('span');
+ sortHandle.className = 'icon icon16 fa-arrows sortableNodeHandle';
+ pollOptionInput.appendChild(sortHandle);
+
+ // buttons
+ var addButton = elCreate('a');
+ elAttr(addButton, 'role', 'button');
+ elAttr(addButton, 'href', '#');
+ addButton.className = 'icon icon16 fa-plus jsTooltip jsAddOption pointer';
+ elAttr(addButton, 'title', Language.get('wcf.poll.button.addOption'));
+ addButton.addEventListener('click', this._addOption.bind(this));
+ pollOptionInput.appendChild(addButton);
+
+ var deleteButton = elCreate('a');
+ elAttr(deleteButton, 'role', 'button');
+ elAttr(deleteButton, 'href', '#');
+ deleteButton.className = 'icon icon16 fa-times jsTooltip jsDeleteOption pointer';
+ elAttr(deleteButton, 'title', Language.get('wcf.poll.button.removeOption'));
+ deleteButton.addEventListener('click', this._removeOption.bind(this));
+ pollOptionInput.appendChild(deleteButton);
+
+ // input field
+ var optionInput = elCreate('input');
+ elAttr(optionInput, 'type', 'text');
+ optionInput.value = optionValue;
+ elAttr(optionInput, 'maxlength', 255);
+ optionInput.addEventListener('keydown', this._optionInputKeyDown.bind(this));
+ optionInput.addEventListener('click', function() {
+ // work-around for some weird focus issue on iOS/Android
+ if (document.activeElement !== this) {
+ this.focus();
+ }
+ });
+ pollOptionInput.appendChild(optionInput);
+
+ if (insertAfter !== null) {
+ optionInput.focus();
+ }
+
+ this._optionCount++;
+ if (this._optionCount === this._options.maxOptions) {
+ elBySelAll('span.jsAddOption', this.optionList, function(icon) {
+ icon.classList.remove('pointer');
+ icon.classList.add('disabled');
+ });
+ }
+ },
+
+ /**
+ * Adds the given poll option to the option list.
+ *
+ * @param {object[]} pollOptions data of the added options
+ */
+ _createOptionList: function(pollOptions) {
+ for (var i = 0, length = pollOptions.length; i < length; i++) {
+ var option = pollOptions[i];
+ this._createOption(option.optionValue, option.optionID);
+ }
+
+ // add empty option field to add new options
+ if (this._optionCount < this._options.maxOptions) {
+ this._createOption();
+ }
+ },
+
+ /**
+ * Handles errors when the data is saved via AJAX.
+ *
+ * @param {object} data request response data
+ */
+ _handleError: function (data) {
+ switch (data.returnValues.fieldName) {
+ case this._wysiwygId + 'Poll_endTime':
+ case this._wysiwygId + 'Poll_maxVotes':
+ var fieldName = data.returnValues.fieldName.replace(this._wysiwygId + 'Poll_', '');
+
+ var small = elCreate('small');
+ small.className = 'innerError';
+ small.innerHTML = Language.get('wcf.poll.' + fieldName + '.error.' + data.returnValues.errorType);
+
+ var element = elById(data.returnValues.fieldName);
+ var errorParent = element.closest('dd');
+
+ DomUtil.prepend(small, element.nextSibling);
+
+ data.cancel = true;
+ break;
+ }
+ },
+
+ /**
+ * Adds an empty poll option after the current option when clicking enter.
+ *
+ * @param {Event} event key event
+ */
+ _optionInputKeyDown: function(event) {
+ // ignore every key except for [Enter]
+ if (!EventKey.Enter(event)) {
+ return;
+ }
+
+ Core.triggerEvent(elByClass('jsAddOption', event.currentTarget.parentNode)[0], 'click');
+
+ event.preventDefault();
+ },
+
+ /**
+ * Removes a poll option after clicking on the `Remove Option` button.
+ *
+ * @param {Event} event click event
+ */
+ _removeOption: function (event) {
+ event.preventDefault();
+
+ elRemove(event.currentTarget.closest('li'));
+
+ this._optionCount--;
+
+ elBySelAll('span.jsAddOption', this.optionList, function(icon) {
+ icon.classList.add('pointer');
+ icon.classList.remove('disabled');
+ });
+
+ if (this.optionList.length === 0) {
+ this._createOption();
+ }
+ },
+
+ /**
+ * Resets all poll-related form fields.
+ */
+ _reset: function() {
+ this.questionField.value = '';
+
+ this._optionCount = 0;
+ this.optionList.innerHtml = '';
+ this._createOption();
+
+ DatePicker.clear(this.endTimeField);
+
+ this.maxVotesField.value = 1;
+ this.isChangeableYesField.checked = false;
+ this.isChangeableNoField.checked = true;
+ this.isPublicYesField = false;
+ this.isPublicNoField = true;
+ this.resultsRequireVoteYesField = false;
+ this.resultsRequireVoteNoField = true;
+ this.sortByVotesYesField = false;
+ this.sortByVotesNoField = true;
+
+ EventHandler.fire(
+ 'com.woltlab.wcf.poll.editor',
+ 'reset',
+ {
+ pollEditor: this
+ }
+ );
+ },
+
+ /**
+ * Is called if the form is submitted or before the AJAX request is sent.
+ *
+ * @param {Event?} event form submit event
+ */
+ _submit: function(event) {
+ var options = [];
+ for (var i = 0, length = this.optionList.children.length; i < length; i++) {
+ var listItem = this.optionList.children[i];
+ var optionValue = elBySel('input[type=text]', listItem).value.trim();
+
+ if (optionValue !== '') {
+ options.push(elData(listItem, 'option-id') + '_' + optionValue);
+ }
+ }
+
+ if (this._options.isAjax) {
+ event.poll = {};
+
+ event.poll[this.questionField.id] = this.questionField.value;
+ event.poll[this._wysiwygId + 'Poll_options'] = options;
+ event.poll[this.endTimeField.id] = this.endTimeField.value;
+ event.poll[this.maxVotesField.id] = this.maxVotesField.value;
+ event.poll[this.isChangeableYesField.id] = !!this.isChangeableYesField.checked;
+ event.poll[this.isPublicYesField.id] = !!this.isPublicYesField.checked;
+ event.poll[this.resultsRequireVoteYesField.id] = !!this.resultsRequireVoteYesField.checked;
+ event.poll[this.sortByVotesYesField.id] = !!this.sortByVotesYesField.checked;
+
+ EventHandler.fire(
+ 'com.woltlab.wcf.poll.editor',
+ 'submit',
+ {
+ event: event,
+ pollEditor: this
+ }
+ );
+ }
+ else {
+ var form = this._container.closest('form');
+
+ for (var i = 0, length = options.length; i < length; i++) {
+ var input = elCreate('input');
+ elAttr(input, 'type', 'hidden');
+ elAttr(input, 'name', this._wysiwygId + 'Poll_options[' + i + ']');
+ input.value = options[i];
+ form.appendChild(input);
+ }
+ }
+ },
+
+ /**
+ * Is called to validate the poll data.
+ *
+ * @param {object} data event data
+ */
+ _validate: function(data) {
+ if (this.questionField.value.trim() === '') {
+ return;
+ }
+
+ var nonEmptyOptionCount = 0;
+ for (var i = 0, length = this.optionList.children.length; i < length; i++) {
+ var optionInput = elBySel('input[type=text]', this.optionList.children[i]);
+ if (optionInput.value.trim() !== '') {
+ nonEmptyOptionCount++;
+ }
+ }
+
+ if (nonEmptyOptionCount === 0) {
+ data.api.throwError(this._container, Language.get('wcf.global.form.error.empty'));
+ data.valid = false;
+ }
+ else {
+ var maxVotes = ~~this.maxVotesField.value;
+
+ if (maxVotes && maxVotes > nonEmptyOptionCount) {
+ data.api.throwError(this.maxVotesField.parentNode, Language.get('wcf.poll.maxVotes.error.invalid'));
+ data.valid = false;
+ }
+ else {
+ EventHandler.fire(
+ 'com.woltlab.wcf.poll.editor',
+ 'validate',
+ {
+ data: data,
+ pollEditor: this
+ }
+ );
+ }
+ }
+ }
+ };
+
+ return UiPollEditor;
+});
+
+/**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Article
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Article',['WoltLabSuite/Core/Ui/Article/Search'], function(UiArticleSearch) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _click: function() {},
+ _insert: function() {}
+ };
+ return Fake;
+ }
+
+ function UiRedactorArticle(editor, button) { this.init(editor, button); }
+ UiRedactorArticle.prototype = {
+ init: function (editor, button) {
+ this._editor = editor;
+
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ UiArticleSearch.open(this._insert.bind(this));
+ },
+
+ _insert: function (articleId) {
+ this._editor.buffer.set();
+
+ this._editor.insert.text("[wsa='" + articleId + "'][/wsa]");
+ }
+ };
+
+ return UiRedactorArticle;
+});
+
+/**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Metacode
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Metacode',['EventHandler', 'Dom/Util'], function(EventHandler, DomUtil) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ convert: function() {},
+ convertFromHtml: function() {},
+ _getOpeningTag: function() {},
+ _getClosingTag: function() {},
+ _getFirstParagraph: function() {},
+ _getLastParagraph: function() {},
+ _parseAttributes: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Redactor/Metacode
+ */
+ return {
+ /**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @param {Element} element textarea element
+ */
+ convert: function(element) {
+ element.textContent = this.convertFromHtml(element.textContent);
+ },
+
+ convertFromHtml: function (editorId, html) {
+ var div = elCreate('div');
+ div.innerHTML = html;
+
+ var attributes, data, metacode, metacodes = elByTag('woltlab-metacode', div), name, tagClose, tagOpen;
+ while (metacodes.length) {
+ metacode = metacodes[0];
+ name = elData(metacode, 'name');
+ attributes = this._parseAttributes(elData(metacode, 'attributes'));
+
+ data = {
+ attributes: attributes,
+ cancel: false,
+ metacode: metacode
+ };
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'metacode_' + name + '_' + editorId, data);
+ if (data.cancel === true) {
+ continue;
+ }
+
+ tagOpen = this._getOpeningTag(name, attributes);
+ tagClose = this._getClosingTag(name);
+
+ if (metacode.parentNode === div) {
+ DomUtil.prepend(tagOpen, this._getFirstParagraph(metacode));
+ this._getLastParagraph(metacode).appendChild(tagClose);
+ }
+ else {
+ DomUtil.prepend(tagOpen, metacode);
+ metacode.appendChild(tagClose);
+ }
+
+ DomUtil.unwrapChildNodes(metacode);
+ }
+
+ // convert `<kbd>…</kbd>` to `[tt]…[/tt]`
+ var inlineCode, inlineCodes = elByTag('kbd', div);
+ while (inlineCodes.length) {
+ inlineCode = inlineCodes[0];
+
+ inlineCode.insertBefore(document.createTextNode('[tt]'), inlineCode.firstChild);
+ inlineCode.appendChild(document.createTextNode('[/tt]'));
+
+ DomUtil.unwrapChildNodes(inlineCode);
+ }
+
+ return div.innerHTML;
+ },
+
+ /**
+ * Returns a text node representing the opening bbcode tag.
+ *
+ * @param {string} name bbcode tag
+ * @param {Array} attributes list of attributes
+ * @returns {Text} text node containing the opening bbcode tag
+ * @protected
+ */
+ _getOpeningTag: function(name, attributes) {
+ var buffer = '[' + name;
+ if (attributes.length) {
+ buffer += '=';
+
+ for (var i = 0, length = attributes.length; i < length; i++) {
+ if (i > 0) buffer += ",";
+ buffer += "'" + attributes[i] + "'";
+ }
+ }
+
+ return document.createTextNode(buffer + ']');
+ },
+
+ /**
+ * Returns a text node representing the closing bbcode tag.
+ *
+ * @param {string} name bbcode tag
+ * @returns {Text} text node containing the closing bbcode tag
+ * @protected
+ */
+ _getClosingTag: function(name) {
+ return document.createTextNode('[/' + name + ']');
+ },
+
+ /**
+ * Returns the first paragraph of provided element. If there are no children or
+ * the first child is not a paragraph, a new paragraph is created and inserted
+ * as first child.
+ *
+ * @param {Element} element metacode element
+ * @returns {Element} paragraph that is the first child of provided element
+ * @protected
+ */
+ _getFirstParagraph: function (element) {
+ var firstChild, paragraph;
+
+ if (element.childElementCount === 0) {
+ paragraph = elCreate('p');
+ element.appendChild(paragraph);
+ }
+ else {
+ firstChild = element.children[0];
+
+ if (firstChild.nodeName === 'P') {
+ paragraph = firstChild;
+ }
+ else {
+ paragraph = elCreate('p');
+ element.insertBefore(paragraph, firstChild);
+ }
+ }
+
+ return paragraph;
+ },
+
+ /**
+ * Returns the last paragraph of provided element. If there are no children or
+ * the last child is not a paragraph, a new paragraph is created and inserted
+ * as last child.
+ *
+ * @param {Element} element metacode element
+ * @returns {Element} paragraph that is the last child of provided element
+ * @protected
+ */
+ _getLastParagraph: function (element) {
+ var count = element.childElementCount, lastChild, paragraph;
+
+ if (count === 0) {
+ paragraph = elCreate('p');
+ element.appendChild(paragraph);
+ }
+ else {
+ lastChild = element.children[count - 1];
+
+ if (lastChild.nodeName === 'P') {
+ paragraph = lastChild;
+ }
+ else {
+ paragraph = elCreate('p');
+ element.appendChild(paragraph);
+ }
+ }
+
+ return paragraph;
+ },
+
+ /**
+ * Parses the attributes string.
+ *
+ * @param {string} attributes base64- and JSON-encoded attributes
+ * @return {Array} list of parsed attributes
+ * @protected
+ */
+ _parseAttributes: function(attributes) {
+ try {
+ attributes = JSON.parse(atob(attributes));
+ }
+ catch (e) { /* invalid base64 data or invalid json */ }
+
+ if (!Array.isArray(attributes)) {
+ return [];
+ }
+
+ var attribute, parsedAttributes = [];
+ for (var i = 0, length = attributes.length; i < length; i++) {
+ attribute = attributes[i];
+
+ if (typeof attribute === 'string') {
+ attribute = attribute.replace(/^'(.*)'$/, '$1');
+ }
+
+ parsedAttributes.push(attribute);
+ }
+
+ return parsedAttributes;
+ }
+ };
+});
+
+/**
+ * Manages the autosave process storing the current editor message in the local
+ * storage to recover it on browser crash or accidental navigation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Autosave
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Autosave',['Core', 'Devtools', 'EventHandler', 'Language', 'Dom/Traverse', './Metacode'], function(Core, Devtools, EventHandler, Language, DomTraverse, UiRedactorMetacode) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ getInitialValue: function() {},
+ getMetaData: function () {},
+ watch: function() {},
+ destroy: function() {},
+ clear: function() {},
+ createOverlay: function() {},
+ hideOverlay: function() {},
+ _saveToStorage: function() {},
+ _cleanup: function() {}
+ };
+ return Fake;
+ }
+
+ // time between save requests in seconds
+ var _frequency = 15;
+
+ /**
+ * @param {Element} element textarea element
+ * @constructor
+ */
+ function UiRedactorAutosave(element) { this.init(element); }
+ UiRedactorAutosave.prototype = {
+ /**
+ * Initializes the autosave handler and removes outdated messages from storage.
+ *
+ * @param {Element} element textarea element
+ */
+ init: function (element) {
+ this._container = null;
+ this._metaData = {};
+ this._editor = null;
+ this._element = element;
+ this._isActive = true;
+ this._isPending = false;
+ this._key = Core.getStoragePrefix() + elData(this._element, 'autosave');
+ this._lastMessage = '';
+ this._originalMessage = '';
+ this._overlay = null;
+ this._restored = false;
+ this._timer = null;
+
+ this._cleanup();
+
+ // remove attribute to prevent Redactor's built-in autosave to kick in
+ this._element.removeAttribute('data-autosave');
+
+ var form = DomTraverse.parentByTag(this._element, 'FORM');
+ if (form !== null) {
+ form.addEventListener('submit', this.destroy.bind(this));
+ }
+
+ // export meta data
+ EventHandler.add('com.woltlab.wcf.redactor2', 'getMetaData_' + this._element.id, (function (data) {
+ for (var key in this._metaData) {
+ if (this._metaData.hasOwnProperty(key)) {
+ data[key] = this._metaData[key];
+ }
+ }
+ }).bind(this));
+
+ // clear editor content on reset
+ EventHandler.add('com.woltlab.wcf.redactor2', 'reset_' + this._element.id, this.hideOverlay.bind(this));
+
+ document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
+ },
+
+ _onVisibilityChange: function () {
+ if (document.hidden) {
+ this._isActive = false;
+ this._isPending = true;
+ }
+ else {
+ this._isActive = true;
+ this._isPending = false;
+ }
+ },
+
+ /**
+ * Returns the initial value for the textarea, used to inject message
+ * from storage into the editor before initialization.
+ *
+ * @return {string} message content
+ */
+ getInitialValue: function() {
+ //noinspection JSUnresolvedVariable
+ if (window.ENABLE_DEVELOPER_TOOLS && Devtools._internal_.editorAutosave() === false) {
+ //noinspection JSUnresolvedVariable
+ return this._element.value;
+ }
+
+ var value = '';
+ try {
+ value = window.localStorage.getItem(this._key);
+ }
+ catch (e) {
+ window.console.warn("Unable to access local storage: " + e.message);
+ }
+
+ try {
+ value = JSON.parse(value);
+ }
+ catch (e) {
+ value = '';
+ }
+
+ // Check if the storage is outdated.
+ if (value !== null && typeof value === 'object' && value.content) {
+ var lastEditTime = ~~elData(this._element, 'autosave-last-edit-time');
+ if (lastEditTime * 1000 <= value.timestamp) {
+ // Compare the stored version with the editor content, but only use the `innerText` property
+ // in order to ignore differences in whitespace, e. g. caused by indentation of HTML tags.
+ var div1 = elCreate('div');
+ div1.innerHTML = this._element.value;
+ var div2 = elCreate('div');
+ div2.innerHTML = value.content;
+
+ if (div1.innerText.trim() !== div2.innerText.trim()) {
+ //noinspection JSUnresolvedVariable
+ this._originalMessage = this._element.value;
+ this._restored = true;
+
+ this._metaData = value.meta || {};
+
+ return value.content;
+ }
+ }
+ }
+
+ //noinspection JSUnresolvedVariable
+ return this._element.value;
+ },
+
+ /**
+ * Returns the stored meta data.
+ *
+ * @return {Object}
+ */
+ getMetaData: function () {
+ return this._metaData;
+ },
+
+ /**
+ * Enables periodical save of editor contents to local storage.
+ *
+ * @param {$.Redactor} editor redactor instance
+ */
+ watch: function(editor) {
+ this._editor = editor;
+
+ if (this._timer !== null) {
+ throw new Error("Autosave timer is already active.");
+ }
+
+ this._timer = window.setInterval(this._saveToStorage.bind(this), _frequency * 1000);
+
+ this._saveToStorage();
+
+ this._isPending = false;
+ },
+
+ /**
+ * Disables autosave handler, for use on editor destruction.
+ */
+ destroy: function () {
+ this.clear();
+
+ this._editor = null;
+
+ window.clearInterval(this._timer);
+ this._timer = null;
+ this._isPending = false;
+ },
+
+ /**
+ * Removed the stored message, for use after a message has been submitted.
+ */
+ clear: function () {
+ this._metaData = {};
+ this._lastMessage = '';
+
+ try {
+ window.localStorage.removeItem(this._key);
+ }
+ catch (e) {
+ window.console.warn("Unable to remove from local storage: " + e.message);
+ }
+ },
+
+ /**
+ * Creates the autosave controls, used to keep or discard the restored draft.
+ */
+ createOverlay: function () {
+ if (!this._restored) {
+ return;
+ }
+
+ var container = elCreate('div');
+ container.className = 'redactorAutosaveRestored active';
+
+ var title = elCreate('span');
+ title.textContent = Language.get('wcf.editor.autosave.restored');
+ container.appendChild(title);
+
+ var button = elCreate('a');
+ button.className = 'jsTooltip';
+ button.href = '#';
+ button.title = Language.get('wcf.editor.autosave.keep');
+ button.innerHTML = '<span class="icon icon16 fa-check green"></span>';
+ button.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ this.hideOverlay();
+ }).bind(this));
+ container.appendChild(button);
+
+ button = elCreate('a');
+ button.className = 'jsTooltip';
+ button.href = '#';
+ button.title = Language.get('wcf.editor.autosave.discard');
+ button.innerHTML = '<span class="icon icon16 fa-times red"></span>';
+ button.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ // remove from storage
+ this.clear();
+
+ // set code
+ var content = UiRedactorMetacode.convertFromHtml(this._editor.core.element()[0].id, this._originalMessage);
+ this._editor.code.start(content);
+
+ // set value
+ this._editor.core.textarea().val(this._editor.clean.onSync(this._editor.$editor.html()));
+
+ this.hideOverlay();
+ }).bind(this));
+ container.appendChild(button);
+
+ this._editor.core.box()[0].appendChild(container);
+
+ var callback = (function () {
+ this._editor.core.editor()[0].removeEventListener(WCF_CLICK_EVENT, callback);
+
+ this.hideOverlay();
+ }).bind(this);
+ this._editor.core.editor()[0].addEventListener(WCF_CLICK_EVENT, callback);
+
+ this._container = container;
+ },
+
+ /**
+ * Hides the autosave controls.
+ */
+ hideOverlay: function () {
+ if (this._container !== null) {
+ this._container.classList.remove('active');
+
+ window.setTimeout((function () {
+ if (this._container !== null) {
+ elRemove(this._container);
+ }
+
+ this._container = null;
+ this._originalMessage = '';
+ }).bind(this), 1000);
+ }
+ },
+
+ /**
+ * Saves the current message to storage unless there was no change.
+ *
+ * @protected
+ */
+ _saveToStorage: function() {
+ if (!this._isActive) {
+ if (!this._isPending) return;
+
+ // save one last time before suspending
+ this._isPending = false;
+ }
+
+ //noinspection JSUnresolvedVariable
+ if (window.ENABLE_DEVELOPER_TOOLS && Devtools._internal_.editorAutosave() === false) {
+ //noinspection JSUnresolvedVariable
+ return;
+ }
+
+ var content = this._editor.code.get();
+ if (this._editor.utils.isEmpty(content)) {
+ content = '';
+ }
+
+ if (this._lastMessage === content) {
+ // break if content hasn't changed
+ return;
+ }
+
+ if (content === '') {
+ return this.clear();
+ }
+
+ try {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveMetaData_' + this._element.id, this._metaData);
+
+ window.localStorage.setItem(this._key, JSON.stringify({
+ content: content,
+ meta: this._metaData,
+ timestamp: Date.now()
+ }));
+
+ this._lastMessage = content;
+ }
+ catch (e) {
+ window.console.warn("Unable to write to local storage: " + e.message);
+ }
+ },
+
+ /**
+ * Removes stored messages older than one week.
+ *
+ * @protected
+ */
+ _cleanup: function () {
+ var oneWeekAgo = Date.now() - (7 * 24 * 3600 * 1000), removeKeys = [];
+ var i, key, length, value;
+ for (i = 0, length = window.localStorage.length; i < length; i++) {
+ key = window.localStorage.key(i);
+
+ // check if key matches our prefix
+ if (key.indexOf(Core.getStoragePrefix()) !== 0) {
+ continue;
+ }
+
+ try {
+ value = window.localStorage.getItem(key);
+ }
+ catch (e) {
+ window.console.warn("Unable to access local storage: " + e.message);
+ }
+
+ try {
+ value = JSON.parse(value);
+ }
+ catch (e) {
+ value = { timestamp: 0 };
+ }
+
+ if (!value || value.timestamp < oneWeekAgo) {
+ removeKeys.push(key);
+ }
+ }
+
+ for (i = 0, length = removeKeys.length; i < length; i++) {
+ try {
+ window.localStorage.removeItem(removeKeys[i]);
+ }
+ catch (e) {
+ window.console.warn("Unable to remove from local storage: " + e.message);
+ }
+ }
+ }
+ };
+
+ return UiRedactorAutosave;
+});
+
+/**
+ * Helper class to deal with clickable block headers using the pseudo
+ * `::before` element.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/PseudoHeader
+ */
+define('WoltLabSuite/Core/Ui/Redactor/PseudoHeader',[], function() {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ getHeight: function() {}
+ };
+ return Fake;
+ }
+
+ return {
+ /**
+ * Returns the height within a click should be treated as a click
+ * within the block element's title. This method expects that the
+ * `::before` element is used and that removing the attribute
+ * `data-title` does cause the title to collapse.
+ *
+ * @param {Element} element block element
+ * @return {int} clickable height spanning from the top border down to the bottom of the title
+ */
+ getHeight: function (element) {
+ var height = ~~window.getComputedStyle(element).paddingTop.replace(/px$/, '');
+
+ var styles = window.getComputedStyle(element, '::before');
+ height += ~~styles.paddingTop.replace(/px$/, '');
+ height += ~~styles.paddingBottom.replace(/px$/, '');
+
+ var titleHeight = ~~styles.height.replace(/px$/, '');
+ if (titleHeight === 0) {
+ // firefox returns garbage for pseudo element height
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=925694
+
+ titleHeight = element.scrollHeight;
+ element.classList.add('redactorCalcHeight');
+ titleHeight -= element.scrollHeight;
+ element.classList.remove('redactorCalcHeight');
+ }
+
+ height += titleHeight;
+
+ return height;
+ }
+ }
+});
+
+/**
+ * Manages code blocks.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Code
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Code',['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './PseudoHeader', 'prism/prism-meta'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorPseudoHeader, PrismMeta) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _bbcodeCode: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {},
+ _dialogSubmit: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @constructor
+ */
+ function UiRedactorCode(editor) { this.init(editor); }
+ UiRedactorCode.prototype = {
+ /**
+ * Initializes the source code management.
+ *
+ * @param {Object} editor editor instance
+ */
+ init: function(editor) {
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+ this._pre = null;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'bbcode_code_' + this._elementId, this._bbcodeCode.bind(this));
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ // support for active button marking
+ this._editor.opts.activeButtonsStates.pre = 'code';
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+ },
+
+ /**
+ * Intercepts the insertion of `[code]` tags and uses a native `<pre>` instead.
+ *
+ * @param {Object} data event data
+ * @protected
+ */
+ _bbcodeCode: function(data) {
+ data.cancel = true;
+
+ var pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE' && pre.classList.contains('woltlabHtml')) {
+ return;
+ }
+
+ this._editor.button.toggle({}, 'pre', 'func', 'block.format');
+
+ pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE' && !pre.classList.contains('woltlabHtml')) {
+ if (pre.childElementCount === 1 && pre.children[0].nodeName === 'BR') {
+ // drop superfluous linebreak
+ pre.removeChild(pre.children[0]);
+ }
+
+ this._setTitle(pre);
+
+ pre.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(pre);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ elBySelAll('pre:not(.woltlabHtml)', this._editor.$editor[0], (function(pre) {
+ pre.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(pre);
+ }).bind(this));
+ },
+
+ /**
+ * Opens the dialog overlay to edit the code's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var pre = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(pre);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(pre);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._pre = pre;
+
+ UiDialog.open(this);
+ }
+ },
+
+ /**
+ * Saves the changes to the code's properties.
+ *
+ * @protected
+ */
+ _dialogSubmit: function() {
+ var id = 'redactor-code-' + this._elementId;
+
+ ['file', 'highlighter', 'line'].forEach((function (attr) {
+ elData(this._pre, attr, elById(id + '-' + attr).value);
+ }).bind(this));
+
+ this._setTitle(this._pre);
+ this._editor.caret.after(this._pre);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * Sets or updates the code's header title.
+ *
+ * @param {Element} pre code element
+ * @protected
+ */
+ _setTitle: function(pre) {
+ var file = elData(pre, 'file'),
+ highlighter = elData(pre, 'highlighter');
+
+ //noinspection JSUnresolvedVariable
+ highlighter = (this._editor.opts.woltlab.highlighters.hasOwnProperty(highlighter)) ? this._editor.opts.woltlab.highlighters[highlighter] : '';
+
+ var title = Language.get('wcf.editor.code.title', {
+ file: file,
+ highlighter: highlighter
+ });
+
+ if (elData(pre, 'title') !== title) {
+ elData(pre, 'title', title);
+ }
+ },
+
+ _delete: function (event) {
+ event.preventDefault();
+
+ var caretEnd = this._pre.nextElementSibling || this._pre.previousElementSibling;
+ if (caretEnd === null && this._pre.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._pre.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._pre);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var id = 'redactor-code-' + this._elementId,
+ idButtonDelete = id + '-button-delete',
+ idButtonSave = id + '-button-save',
+ idFile = id + '-file',
+ idHighlighter = id + '-highlighter',
+ idLine = id + '-line';
+
+ return {
+ id: id,
+ options: {
+ onClose: (function () {
+ this._editor.selection.restore();
+
+ UiDialog.destroy(this);
+ }).bind(this),
+
+ onSetup: (function() {
+ elById(idButtonDelete).addEventListener(WCF_CLICK_EVENT, this._delete.bind(this));
+
+ // set highlighters
+ var highlighters = '<option value="">' + Language.get('wcf.editor.code.highlighter.detect') + '</option>';
+ highlighters += '<option value="plain">' + Language.get('wcf.editor.code.highlighter.plain') + '</option>';
+
+ //noinspection JSUnresolvedVariable
+ var values = this._editor.opts.woltlab.highlighters.map(function (highlighter) {
+ return [highlighter, PrismMeta[highlighter].title];
+ });
+
+ // sort by label
+ values.sort(function(a, b) {
+ if (a[1] < b[1]) {
+ return -1;
+ }
+ else if (a[1] > b[1]) {
+ return 1;
+ }
+
+ return 0;
+ });
+
+ values.forEach((function(value) {
+ highlighters += '<option value="' + value[0] + '">' + StringUtil.escapeHTML(value[1]) + '</option>';
+ }).bind(this));
+
+ elById(idHighlighter).innerHTML = highlighters;
+ }).bind(this),
+
+ onShow: (function() {
+ elById(idHighlighter).value = elData(this._pre, 'highlighter');
+ var line = elData(this._pre, 'line');
+ elById(idLine).value = (line === '') ? 1 : ~~line;
+ elById(idFile).value = elData(this._pre, 'file');
+ }).bind(this),
+
+ title: Language.get('wcf.editor.code.edit')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="' + idHighlighter + '">' + Language.get('wcf.editor.code.highlighter') + '</label></dt>'
+ + '<dd>'
+ + '<select id="' + idHighlighter + '"></select>'
+ + '<small>' + Language.get('wcf.editor.code.highlighter.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="' + idLine + '">' + Language.get('wcf.editor.code.line') + '</label></dt>'
+ + '<dd>'
+ + '<input type="number" id="' + idLine + '" min="0" value="1" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.code.line.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="' + idFile + '">' + Language.get('wcf.editor.code.file') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idFile + '" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.code.file.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button id="' + idButtonSave + '" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.save') + '</button>'
+ + '<button id="' + idButtonDelete + '">' + Language.get('wcf.global.button.delete') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+
+ return UiRedactorCode;
+});
+
+/**
+ * Provides helper methods to add and remove format elements. These methods should in
+ * theory work with non-editor elements but has not been tested and any usage outside
+ * the editor is not recommended.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Format
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Format',['Dom/Util'], function(DomUtil) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ format: function() {},
+ removeFormat: function() {},
+ _handleParentNodes: function() {},
+ _getLastMatchingParent: function() {},
+ _isBoundaryElement: function() {},
+ _getSelectionMarker: function() {}
+ };
+ return Fake;
+ }
+
+ var _isValidSelection = function(editorElement) {
+ var element = window.getSelection().anchorNode;
+ while (element) {
+ if (element === editorElement) {
+ return true;
+ }
+
+ element = element.parentNode;
+ }
+
+ return false;
+ };
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Redactor/Format
+ */
+ return {
+ /**
+ * Applies format elements to the selected text.
+ *
+ * @param {Element} editorElement editor element
+ * @param {string} property CSS property name
+ * @param {string} value CSS property value
+ */
+ format: function(editorElement, property, value) {
+ var selection = window.getSelection();
+ if (!selection.rangeCount) {
+ // no active selection
+ return;
+ }
+
+ if (!_isValidSelection(editorElement)) {
+ console.error("Invalid selection, range exists outside of the editor:", selection.anchorNode);
+ return;
+ }
+
+ var range = selection.getRangeAt(0);
+ var markerStart = null, markerEnd = null, tmpElement = null;
+ if (range.collapsed) {
+ tmpElement = elCreate('strike');
+ tmpElement.textContent = '\u200B';
+ range.insertNode(tmpElement);
+
+ range = document.createRange();
+ range.selectNodeContents(tmpElement);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ else {
+ // removing existing format causes the selection to vanish,
+ // these markers are used to restore it afterwards
+ markerStart = elCreate('mark');
+ markerEnd = elCreate('mark');
+
+ var tmpRange = range.cloneRange();
+ tmpRange.collapse(true);
+ tmpRange.insertNode(markerStart);
+
+ tmpRange = range.cloneRange();
+ tmpRange.collapse(false);
+ tmpRange.insertNode(markerEnd);
+
+ range = document.createRange();
+ range.setStartAfter(markerStart);
+ range.setEndBefore(markerEnd);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ // remove existing format before applying new one
+ this.removeFormat(editorElement, property);
+
+ range = document.createRange();
+ range.setStartAfter(markerStart);
+ range.setEndBefore(markerEnd);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ var selectionMarker = ['strike', 'strikethrough'];
+ if (tmpElement === null) {
+ selectionMarker = this._getSelectionMarker(editorElement, selection);
+
+ document.execCommand(selectionMarker[1]);
+ }
+
+ var elements = elBySelAll(selectionMarker[0], editorElement), formatElement, selectElements = [], strike;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ strike = elements[i];
+
+ formatElement = elCreate('span');
+ // we're bypassing `style.setPropertyValue()` on purpose here,
+ // as it prevents browsers from mangling the value
+ elAttr(formatElement, 'style', property + ': ' + value);
+
+ DomUtil.replaceElement(strike, formatElement);
+ selectElements.push(formatElement);
+ }
+
+ var count = selectElements.length;
+ if (count) {
+ var firstSelectedElement = selectElements[0];
+ var lastSelectedElement = selectElements[count - 1];
+
+ // check if parent is of the same format
+ // and contains only the selected nodes
+ if (tmpElement === null && (firstSelectedElement.parentNode === lastSelectedElement.parentNode)) {
+ var parent = firstSelectedElement.parentNode;
+ if (parent.nodeName === 'SPAN' && parent.style.getPropertyValue(property) !== '') {
+ if (this._isBoundaryElement(firstSelectedElement, parent, 'previous') && this._isBoundaryElement(lastSelectedElement, parent, 'next')) {
+ DomUtil.unwrapChildNodes(parent);
+ }
+ }
+ }
+
+ range = document.createRange();
+ range.setStart(firstSelectedElement, 0);
+ range.setEnd(lastSelectedElement, lastSelectedElement.childNodes.length);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ if (markerStart !== null) {
+ elRemove(markerStart);
+ elRemove(markerEnd);
+ }
+ },
+
+ /**
+ * Removes a format element from the current selection.
+ *
+ * The removal uses a few techniques to remove the target element(s) without harming
+ * nesting nor any other formatting present. The steps taken are described below:
+ *
+ * 1. The browser will wrap all parts of the selection into <strike> tags
+ *
+ * This isn't the most efficient way to isolate each selected node, but is the
+ * most reliable way to accomplish this because the browser will insert them
+ * exactly where the range spans without harming the node nesting.
+ *
+ * Basically it is a trade-off between efficiency and reliability, the performance
+ * is still excellent but could be better at the expense of an increased complexity,
+ * which simply doesn't exactly pay off.
+ *
+ * 2. Iterate over each inserted <strike> and isolate all relevant ancestors
+ *
+ * Format tags can appear both as a child of the <strike> as well as once or multiple
+ * times as an ancestor.
+ *
+ * It uses ranges to select the contents before the <strike> element up to the start
+ * of the last matching ancestor and cuts out the nodes. The browser will ensure that
+ * the resulting fragment will include all relevant ancestors that were present before.
+ *
+ * The example below will use the fictional <bar> elements as the tag to remove, the
+ * pipe ("|") is used to denote the outer node boundaries.
+ *
+ * Before:
+ * |<bar>This is <foo>a <strike>simple <bar>example</bar></strike></foo></bar>|
+ * After:
+ * |<bar>This is <foo>a </foo></bar>|<bar><foo>simple <bar>example</bar></strike></foo></bar>|
+ *
+ * As a result we can now remove <bar> both inside the <strike> element as well as
+ * the outer <bar> without harming the effect of <bar> for the preceding siblings.
+ *
+ * This process is repeated for siblings appearing after the <strike> element too, it
+ * works as described above but flipped. This is an expensive operation and will only
+ * take place if there are any matching ancestors that need to be considered.
+ *
+ * Inspired by http://stackoverflow.com/a/12899461
+ *
+ * 3. Remove all matching ancestors, child elements and last the <strike> element itself
+ *
+ * Depending on the amount of nested matching nodes, this process will move a lot of
+ * nodes around. Removing the <bar> element will require all its child nodes to be moved
+ * in front of <bar>, they will actually become a sibling of <bar>. Afterwards the
+ * (now empty) <bar> element can be safely removed without losing any nodes.
+ *
+ *
+ * One last hint: This method will not check if the selection at some point contains at
+ * least one target element, it assumes that the user will not take any action that invokes
+ * this method for no reason (unless they want to waste CPU cycles, in that case they're
+ * welcome).
+ *
+ * This is especially important for developers as this method shouldn't be called for
+ * no good reason. Even though it is super fast, it still comes with expensive DOM operations
+ * and especially low-end devices (such as cheap smartphones) might not exactly like executing
+ * this method on large documents.
+ *
+ * If you fell the need to invoke this method anyway, go ahead. I'm a comment, not a cop.
+ *
+ * @param {Element} editorElement editor element
+ * @param {string} property CSS property that should be removed
+ */
+ removeFormat: function(editorElement, property) {
+ var selection = window.getSelection();
+ if (!selection.rangeCount) {
+ return;
+ }
+ else if (!_isValidSelection(editorElement)) {
+ console.error("Invalid selection, range exists outside of the editor:", selection.anchorNode);
+ return;
+ }
+
+ // Removing a span from an empty selection in an empty line containing a `<br>` causes a selection
+ // shift where the caret is moved into the span again. Unlike inline changes to the formatting, any
+ // removal of the format in an empty line should remove it from its entirely, instead of just around
+ // the caret position.
+ var range = selection.getRangeAt(0);
+ var helperTextNode = null;
+ if (range.collapsed) {
+ var container = range.startContainer;
+ var tree = [container];
+ while (true) {
+ var parent = container.parentNode;
+ if (parent === editorElement || parent.nodeName === 'TD') {
+ break;
+ }
+
+ container = parent;
+ tree.push(container);
+ }
+
+ if (this._isEmpty(container.innerHTML)) {
+ var marker = document.createElement('woltlab-format-marker');
+ range.insertNode(marker);
+
+ // Find the offending span and remove it entirely.
+ tree.forEach(function (element) {
+ if (element.nodeName === 'SPAN') {
+ if (element.style.getPropertyValue(property)) {
+ DomUtil.unwrapChildNodes(element);
+ }
+ }
+ });
+
+ // Firefox messes up the selection if the ancestor element was removed and there is
+ // an adjacent `<br>` present. Instead of keeping the caret in front of the <br>, it
+ // is implicitly moved behind it.
+ range = document.createRange();
+ range.selectNode(marker);
+ range.collapse(true);
+
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ elRemove(marker);
+
+ return;
+ }
+
+ // Fill up the range with a zero length whitespace to give the browser
+ // something to strike through. If the range is completely empty, the
+ // "strike" is remembered by the browser, but not actually inserted into
+ // the DOM, causing the next keystroke to magically insert it.
+ helperTextNode = document.createTextNode('\u200B');
+ range.insertNode(helperTextNode);
+ }
+
+ var strikeElements = elByTag('strike', editorElement);
+
+ // remove any <strike> element first, all though there shouldn't be any at all
+ while (strikeElements.length) {
+ DomUtil.unwrapChildNodes(strikeElements[0]);
+ }
+
+ var selectionMarker = this._getSelectionMarker(editorElement, window.getSelection());
+
+ document.execCommand(selectionMarker[1]);
+ if (selectionMarker[0] !== 'strike') {
+ strikeElements = elByTag(selectionMarker[0], editorElement);
+ }
+
+ var lastMatchingParent, strikeElement;
+ while (strikeElements.length) {
+ strikeElement = strikeElements[0];
+ lastMatchingParent = this._getLastMatchingParent(strikeElement, editorElement, property);
+
+ if (lastMatchingParent !== null) {
+ this._handleParentNodes(strikeElement, lastMatchingParent, property);
+ }
+
+ // remove offending elements from child nodes
+ elBySelAll('span', strikeElement, function (span) {
+ if (span.style.getPropertyValue(property)) {
+ DomUtil.unwrapChildNodes(span);
+ }
+ });
+
+ // remove strike element itself
+ DomUtil.unwrapChildNodes(strikeElement);
+ }
+
+ // search for tags that are still floating around, but are completely empty
+ elBySelAll('span', editorElement, function (element) {
+ if (element.parentNode && !element.textContent.length && element.style.getPropertyValue(property) !== '') {
+ if (element.childElementCount === 1 && element.children[0].nodeName === 'MARK') {
+ element.parentNode.insertBefore(element.children[0], element);
+ }
+
+ if (element.childElementCount === 0) {
+ elRemove(element);
+ }
+ }
+ });
+
+ if (helperTextNode !== null) {
+ window.jQuery(editorElement).redactor('caret.after', range.parentNode);
+ elRemove(helperTextNode);
+ }
+ },
+
+ /**
+ * Slices relevant parent nodes and removes matching ancestors.
+ *
+ * @param {Element} strikeElement strike element representing the text selection
+ * @param {Element} lastMatchingParent last matching ancestor element
+ * @param {string} property CSS property that should be removed
+ * @protected
+ */
+ _handleParentNodes: function(strikeElement, lastMatchingParent, property) {
+ var range;
+
+ // selection does not begin at parent node start, slice all relevant parent
+ // nodes to ensure that selection is then at the beginning while preserving
+ // all proper ancestor elements
+ //
+ // before: (the pipe represents the node boundary)
+ // |otherContent <-- selection -->
+ // after:
+ // |otherContent| |<-- selection -->
+ if (!DomUtil.isAtNodeStart(strikeElement, lastMatchingParent)) {
+ range = document.createRange();
+ range.setStartBefore(lastMatchingParent);
+ range.setEndBefore(strikeElement);
+
+ var fragment = range.extractContents();
+ lastMatchingParent.parentNode.insertBefore(fragment, lastMatchingParent);
+ }
+
+ // selection does not end at parent node end, slice all relevant parent nodes
+ // to ensure that selection is then at the end while preserving all proper
+ // ancestor elements
+ //
+ // before: (the pipe represents the node boundary)
+ // <-- selection --> otherContent|
+ // after:
+ // <-- selection -->| |otherContent|
+ if (!DomUtil.isAtNodeEnd(strikeElement, lastMatchingParent)) {
+ range = document.createRange();
+ range.setStartAfter(strikeElement);
+ range.setEndAfter(lastMatchingParent);
+
+ fragment = range.extractContents();
+ lastMatchingParent.parentNode.insertBefore(fragment, lastMatchingParent.nextSibling);
+ }
+
+ // the strike element is now some kind of isolated, meaning we can now safely
+ // remove all offending parent nodes without influencing formatting of any content
+ // before or after the element
+ elBySelAll('span', lastMatchingParent, function (span) {
+ if (span.style.getPropertyValue(property)) {
+ DomUtil.unwrapChildNodes(span);
+ }
+ });
+
+ // finally remove the parent itself
+ DomUtil.unwrapChildNodes(lastMatchingParent);
+ },
+
+ /**
+ * Finds the last matching ancestor until it reaches the editor element.
+ *
+ * @param {Element} strikeElement strike element representing the text selection
+ * @param {Element} editorElement editor element
+ * @param {string} property CSS property that should be removed
+ * @returns {(Element|null)} last matching ancestor element or null if there is none
+ * @protected
+ */
+ _getLastMatchingParent: function(strikeElement, editorElement, property) {
+ var parent = strikeElement.parentNode, match = null;
+ while (parent !== editorElement) {
+ if (parent.nodeName === 'SPAN' && parent.style.getPropertyValue(property) !== '') {
+ match = parent;
+ }
+
+ parent = parent.parentNode;
+ }
+
+ return match;
+ },
+
+ /**
+ * Returns true if provided element is the first or last element
+ * of its parent, ignoring empty text nodes appearing between the
+ * element and the boundary.
+ *
+ * @param {Element} element target element
+ * @param {Element} parent parent element
+ * @param {string} type traversal direction, can be either `next` or `previous`
+ * @return {boolean} true if element is the non-empty boundary element
+ * @protected
+ */
+ _isBoundaryElement: function (element, parent, type) {
+ var node = element;
+ while (node = node[type + 'Sibling']) {
+ if (node.nodeType !== Node.TEXT_NODE || node.textContent.replace(/\u200B/, '') !== '') {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Returns a custom selection marker element, can be either `strike`, `sub` or `sup`. Using other kind
+ * of formattings is not possible due to the inconsistent behavior across browsers.
+ *
+ * @param {Element} editorElement editor element
+ * @param {Selection} selection selection object
+ * @return {string[]} tag name and command name
+ * @protected
+ */
+ _getSelectionMarker: function (editorElement, selection) {
+ var hasNode, node, tag, tags = ['DEL', 'SUB', 'SUP'];
+ for (var i = 0, length = tags.length; i < length; i++) {
+ tag = tags[i];
+
+ node = elClosest(selection.anchorNode);
+ hasNode = (elBySel(tag.toLowerCase(), node) !== null);
+
+ if (!hasNode) {
+ while (node && node !== editorElement) {
+ if (node.nodeName === tag) {
+ hasNode = true;
+ break;
+ }
+
+ node = node.parentNode;
+ }
+ }
+
+ if (hasNode) {
+ tag = undefined;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (tag === 'DEL' || tag === undefined) {
+ return ['strike', 'strikethrough'];
+ }
+
+ return [tag.toLowerCase(), tag.toLowerCase() + 'script'];
+ },
+
+ /**
+ * Slightly modified version of Redactor's `utils.isEmpty()`.
+ *
+ * @param {string} html
+ * @returns {boolean}
+ * @protected
+ */
+ _isEmpty: function(html) {
+ html = html.replace(/[\u200B-\u200D\uFEFF]/g, '');
+ html = html.replace(/ /gi, '');
+ html = html.replace(/<\/?br\s?\/?>/g, '');
+ html = html.replace(/\s/g, '');
+ html = html.replace(/^<p>[^\W\w\D\d]*?<\/p>$/i, '');
+ html = html.replace(/<iframe(.*?[^>])>$/i, 'iframe');
+ html = html.replace(/<source(.*?[^>])>$/i, 'source');
+
+ // remove empty tags
+ html = html.replace(/<[^\/>][^>]*><\/[^>]+>/gi, '');
+ html = html.replace(/<[^\/>][^>]*><\/[^>]+>/gi, '');
+
+ return html.trim() === '';
+ }
+ };
+});
+
+/**
+ * Manages html code blocks.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Html
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Html',['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './PseudoHeader'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorPseudoHeader) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _bbcodeCode: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _save: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @constructor
+ */
+ function UiRedactorHtml(editor) { this.init(editor); }
+ UiRedactorHtml.prototype = {
+ /**
+ * Initializes the source code management.
+ *
+ * @param {Object} editor editor instance
+ */
+ init: function(editor) {
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+ this._pre = null;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'bbcode_woltlabHtml_' + this._elementId, this._bbcodeCode.bind(this));
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ // support for active button marking
+ this._editor.opts.activeButtonsStates['woltlab-html'] = 'woltlabHtml';
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+ },
+
+ /**
+ * Intercepts the insertion of `[woltlabHtml]` tags and uses a native `<pre>` instead.
+ *
+ * @param {Object} data event data
+ * @protected
+ */
+ _bbcodeCode: function(data) {
+ data.cancel = true;
+
+ var pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE' && !pre.classList.contains('woltlabHtml')) {
+ return;
+ }
+
+ this._editor.button.toggle({}, 'pre', 'func', 'block.format');
+
+ pre = this._editor.selection.block();
+ if (pre && pre.nodeName === 'PRE') {
+ pre.classList.add('woltlabHtml');
+
+ if (pre.childElementCount === 1 && pre.children[0].nodeName === 'BR') {
+ // drop superfluous linebreak
+ pre.removeChild(pre.children[0]);
+ }
+
+ this._setTitle(pre);
+
+ pre.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(pre);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ elBySelAll('pre.woltlabHtml', this._editor.$editor[0], (function(pre) {
+ pre.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(pre);
+ }).bind(this));
+ },
+
+ /**
+ * Opens the dialog overlay to edit the code's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var pre = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(pre);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(pre);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._pre = pre;
+
+ console.warn("should edit");
+ }
+ },
+
+ /**
+ * Sets or updates the code's header title.
+ *
+ * @param {Element} pre code element
+ * @protected
+ */
+ _setTitle: function(pre) {
+ ['title', 'description'].forEach(function(title) {
+ var phrase = Language.get('wcf.editor.html.' + title);
+
+ if (elData(pre, title) !== phrase) {
+ elData(pre, title, phrase);
+ }
+ });
+ },
+
+ _delete: function (event) {
+ console.warn("should delete");
+ event.preventDefault();
+
+ var caretEnd = this._pre.nextElementSibling || this._pre.previousElementSibling;
+ if (caretEnd === null && this._pre.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._pre.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._pre);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ }
+ };
+
+ return UiRedactorHtml;
+});
+define('WoltLabSuite/Core/Ui/Redactor/Link',['Core', 'EventKey', 'Language', 'Ui/Dialog'], function(Core, EventKey, Language, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ showDialog: function() {},
+ _submit: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _boundListener = false;
+ var _callback = null;
+
+ return {
+ showDialog: function(options) {
+ UiDialog.open(this);
+
+ UiDialog.setTitle(this, Language.get('wcf.editor.link.' + (options.insert ? 'add' : 'edit')));
+
+ var submitButton = elById('redactor-modal-button-action');
+ submitButton.textContent = Language.get('wcf.global.button.' + (options.insert ? 'insert' : 'save'));
+
+ _callback = options.submitCallback;
+
+ if (!_boundListener) {
+ _boundListener = true;
+
+ submitButton.addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ }
+ },
+
+ _submit: function() {
+ if (_callback()) {
+ UiDialog.close(this);
+ }
+ else {
+ var url = elById('redactor-link-url');
+ elInnerError(url, Language.get((url.value.trim() === '' ? 'wcf.global.form.error.empty' : 'wcf.editor.link.error.invalid')));
+ }
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'redactorDialogLink',
+ options: {
+ onClose: function() {
+ var url = elById('redactor-link-url');
+ var small = (url.nextElementSibling && url.nextElementSibling.nodeName === 'SMALL') ? url.nextElementSibling : null;
+ if (small !== null) {
+ elRemove(small);
+ }
+ },
+ onSetup: function (content) {
+ var submitButton = elBySel('.formSubmit > .buttonPrimary', content);
+
+ if (submitButton !== null) {
+ elBySelAll('input[type="url"], input[type="text"]', content, function (input) {
+ input.addEventListener('keyup', function (event) {
+ if (EventKey.Enter(event)) {
+ Core.triggerEvent(submitButton, 'click');
+ }
+ });
+ });
+ }
+ },
+ onShow: function () {
+ elById('redactor-link-url').focus();
+ }
+ },
+ source: '<dl>'
+ + '<dt><label for="redactor-link-url">' + Language.get('wcf.editor.link.url') + '</label></dt>'
+ + '<dd><input type="url" id="redactor-link-url" class="long"></dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="redactor-link-url-text">' + Language.get('wcf.editor.link.text') + '</label></dt>'
+ + '<dd><input type="text" id="redactor-link-url-text" class="long"></dd>'
+ + '</dl>'
+ + '<div class="formSubmit">'
+ + '<button id="redactor-modal-button-action" class="buttonPrimary"></button>'
+ + '</div>'
+ };
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Redactor/Mention',['Ajax', 'Environment', 'StringUtil', 'Ui/CloseOverlay'], function(Ajax, Environment, StringUtil, UiCloseOverlay) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _keyDown: function() {},
+ _keyUp: function() {},
+ _getTextLineInFrontOfCaret: function() {},
+ _getDropdownMenuPosition: function() {},
+ _setUsername: function() {},
+ _selectMention: function() {},
+ _updateDropdownPosition: function() {},
+ _selectItem: function() {},
+ _hideDropdown: function() {},
+ _ajaxSetup: function() {},
+ _ajaxSuccess: function() {}
+ };
+ return Fake;
+ }
+
+ var _dropdownContainer = null;
+
+ function UiRedactorMention(redactor) { this.init(redactor); }
+ UiRedactorMention.prototype = {
+ init: function(redactor) {
+ this._active = false;
+ this._dropdownActive = false;
+ this._dropdownMenu = null;
+ this._itemIndex = 0;
+ this._lineHeight = null;
+ this._mentionStart = '';
+ this._redactor = redactor;
+ this._timer = null;
+
+ redactor.WoltLabEvent.register('keydown', this._keyDown.bind(this));
+ redactor.WoltLabEvent.register('keyup', this._keyUp.bind(this));
+
+ UiCloseOverlay.add('UiRedactorMention-' + redactor.core.element()[0].id, this._hideDropdown.bind(this));
+ },
+
+ _keyDown: function(data) {
+ if (!this._dropdownActive) {
+ return;
+ }
+
+ /** @var Event event */
+ var event = data.event;
+
+ switch (event.which) {
+ // enter
+ case 13:
+ this._setUsername(null, this._dropdownMenu.children[this._itemIndex].children[0]);
+ break;
+
+ // arrow up
+ case 38:
+ this._selectItem(-1);
+ break;
+
+ // arrow down
+ case 40:
+ this._selectItem(1);
+ break;
+
+ default:
+ this._hideDropdown();
+ return;
+ break;
+ }
+
+ event.preventDefault();
+ data.cancel = true;
+ },
+
+ _keyUp: function(data) {
+ /** @var Event event */
+ var event = data.event;
+
+ // ignore return key
+ if (event.which === 13) {
+ this._active = false;
+
+ return;
+ }
+
+ if (this._dropdownActive) {
+ data.cancel = true;
+
+ // ignore arrow up/down
+ if (event.which === 38 || event.which === 40) {
+ return;
+ }
+ }
+
+ var text = this._getTextLineInFrontOfCaret();
+ if (text.length > 0 && text.length < 25) {
+ var match = text.match(/@([^,]{3,})$/);
+ if (match) {
+ // if mentioning is at text begin or there's a whitespace character
+ // before the '@', everything is fine
+ if (!match.index || text[match.index - 1].match(/\s/)) {
+ this._mentionStart = match[1];
+
+ if (this._timer !== null) {
+ window.clearTimeout(this._timer);
+ this._timer = null;
+ }
+
+ this._timer = window.setTimeout((function() {
+ Ajax.api(this, {
+ parameters: {
+ data: {
+ searchString: this._mentionStart
+ }
+ }
+ });
+
+ this._timer = null;
+ }).bind(this), 500);
+ }
+ }
+ else {
+ this._hideDropdown();
+ }
+ }
+ else {
+ this._hideDropdown();
+ }
+ },
+
+ _getTextLineInFrontOfCaret: function() {
+ var data = this._selectMention(false);
+ if (data !== null) {
+ return data.range.cloneContents().textContent.replace(/\u200B/g, '').replace(/\u00A0/g, ' ').trim();
+ }
+
+ return '';
+ },
+
+ _getDropdownMenuPosition: function() {
+ var data = this._selectMention();
+ if (data === null) {
+ return null;
+ }
+
+ this._redactor.selection.save();
+
+ data.selection.removeAllRanges();
+ data.selection.addRange(data.range);
+
+ // get the offsets of the bounding box of current text selection
+ var rect = data.selection.getRangeAt(0).getBoundingClientRect();
+ var offsets = {
+ top: Math.round(rect.bottom) + (window.scrollY || window.pageYOffset),
+ left: Math.round(rect.left) + document.body.scrollLeft
+ };
+
+ if (this._lineHeight === null) {
+ this._lineHeight = Math.round(rect.bottom - rect.top);
+ }
+
+ // restore caret position
+ this._redactor.selection.restore();
+
+ return offsets;
+ },
+
+ _setUsername: function(event, item) {
+ if (event) {
+ event.preventDefault();
+ item = event.currentTarget;
+ }
+
+ var data = this._selectMention();
+ if (data === null) {
+ this._hideDropdown();
+
+ return;
+ }
+
+ // allow redactor to undo this
+ this._redactor.buffer.set();
+
+ data.selection.removeAllRanges();
+ data.selection.addRange(data.range);
+
+ var range = getSelection().getRangeAt(0);
+ range.deleteContents();
+ range.collapse(true);
+
+ var text = document.createTextNode('@' + elData(item, 'username') + '\u00A0');
+ range.insertNode(text);
+
+ range = document.createRange();
+ range.selectNode(text);
+ range.collapse(false);
+
+ data.selection.removeAllRanges();
+ data.selection.addRange(range);
+
+ this._hideDropdown();
+ },
+
+ _selectMention: function (skipCheck) {
+ var selection = window.getSelection();
+ if (!selection.rangeCount || !selection.isCollapsed) {
+ return null;
+ }
+
+ var container = selection.anchorNode;
+ if (container.nodeType === Node.TEXT_NODE) {
+ // work-around for Firefox after suggestions have been presented
+ container = container.parentNode;
+ }
+
+ // check if there is an '@' within the current range
+ if (container.textContent.indexOf('@') === -1) {
+ return null;
+ }
+
+ // check if we're inside code or quote blocks
+ var editor = this._redactor.core.editor()[0];
+ while (container && container !== editor) {
+ if (['PRE', 'WOLTLAB-QUOTE'].indexOf(container.nodeName) !== -1) {
+ return null;
+ }
+
+ container = container.parentNode;
+ }
+
+ var range = selection.getRangeAt(0);
+ var endContainer = range.startContainer;
+ var endOffset = range.startOffset;
+
+ // find the appropriate end location
+ while (endContainer.nodeType === Node.ELEMENT_NODE) {
+ if (endOffset === 0 && endContainer.childNodes.length === 0) {
+ // invalid start location
+ return null;
+ }
+
+ // startOffset for elements will always be after a node index
+ // or at the very start, which means if there is only text node
+ // and the caret is after it, startOffset will equal `1`
+ endContainer = endContainer.childNodes[(endOffset ? endOffset - 1 : 0)];
+ if (endOffset > 0) {
+ if (endContainer.nodeType === Node.TEXT_NODE) {
+ endOffset = endContainer.textContent.length;
+ }
+ else {
+ endOffset = endContainer.childNodes.length;
+ }
+ }
+ }
+
+ var startContainer = endContainer;
+ var startOffset = -1;
+ while (startContainer !== null) {
+ if (startContainer.nodeType !== Node.TEXT_NODE) {
+ return null;
+ }
+
+ if (startContainer.textContent.indexOf('@') !== -1) {
+ startOffset = startContainer.textContent.lastIndexOf('@');
+
+ break;
+ }
+
+ startContainer = startContainer.previousSibling;
+ }
+
+ if (startOffset === -1) {
+ // there was a non-text node that was in our way
+ return null;
+ }
+
+ try {
+ // mark the entire text, starting from the '@' to the current cursor position
+ range = document.createRange();
+ range.setStart(startContainer, startOffset);
+ range.setEnd(endContainer, endOffset);
+ }
+ catch (e) {
+ window.console.debug(e);
+ return null;
+ }
+
+ if (skipCheck === false) {
+ // check if the `@` occurs at the very start of the container
+ // or at least has a whitespace in front of it
+ var text = '';
+ if (startOffset) {
+ text = startContainer.textContent.substr(0, startOffset);
+ }
+
+ while (startContainer = startContainer.previousSibling) {
+ if (startContainer.nodeType === Node.TEXT_NODE) {
+ text = startContainer.textContent + text;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (text.replace(/\u200B/g, '').match(/\S$/)) {
+ return null;
+ }
+ }
+ else {
+ // check if new range includes the mention text
+ if (range.cloneContents().textContent.replace(/\u200B/g, '').replace(/\u00A0/g, '').trim().replace(/^@/, '') !== this._mentionStart) {
+ // string mismatch
+ return null;
+ }
+ }
+
+ return {
+ range: range,
+ selection: selection
+ };
+ },
+
+ _updateDropdownPosition: function() {
+ var offset = this._getDropdownMenuPosition();
+ if (offset === null) {
+ this._hideDropdown();
+
+ return;
+ }
+ offset.top += 7; // add a little vertical gap
+
+ this._dropdownMenu.style.setProperty('left', offset.left + 'px', '');
+ this._dropdownMenu.style.setProperty('top', offset.top + 'px', '');
+
+ this._selectItem(0);
+
+ if (offset.top + this._dropdownMenu.offsetHeight + 10 > window.innerHeight + (window.scrollY || window.pageYOffset)) {
+ this._dropdownMenu.style.setProperty('top', offset.top - this._dropdownMenu.offsetHeight - 2 * this._lineHeight + 7 + 'px', '');
+ }
+ },
+
+ _selectItem: function(step) {
+ // find currently active item
+ var item = elBySel('.active', this._dropdownMenu);
+ if (item !== null) {
+ item.classList.remove('active');
+ }
+
+ this._itemIndex += step;
+ if (this._itemIndex < 0) {
+ this._itemIndex = this._dropdownMenu.childElementCount - 1;
+ }
+ else if (this._itemIndex >= this._dropdownMenu.childElementCount) {
+ this._itemIndex = 0;
+ }
+
+ this._dropdownMenu.children[this._itemIndex].classList.add('active');
+ },
+
+ _hideDropdown: function() {
+ if (this._dropdownMenu !== null) this._dropdownMenu.classList.remove('dropdownOpen');
+ this._dropdownActive = false;
+ this._itemIndex = 0;
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getSearchResultList',
+ className: 'wcf\\data\\user\\UserAction',
+ interfaceName: 'wcf\\data\\ISearchAction',
+ parameters: {
+ data: {
+ includeUserGroups: true,
+ scope: 'mention'
+ }
+ }
+ },
+ silent: true
+ };
+ },
+
+ _ajaxSuccess: function(data) {
+ if (!Array.isArray(data.returnValues) || !data.returnValues.length) {
+ this._hideDropdown();
+
+ return;
+ }
+
+ if (this._dropdownMenu === null) {
+ this._dropdownMenu = elCreate('ol');
+ this._dropdownMenu.className = 'dropdownMenu';
+
+ if (_dropdownContainer === null) {
+ _dropdownContainer = elCreate('div');
+ _dropdownContainer.className = 'dropdownMenuContainer';
+ document.body.appendChild(_dropdownContainer);
+ }
+
+ _dropdownContainer.appendChild(this._dropdownMenu);
+ }
+
+ this._dropdownMenu.innerHTML = '';
+
+ var callbackClick = this._setUsername.bind(this), link, listItem, user;
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ user = data.returnValues[i];
+
+ listItem = elCreate('li');
+ link = elCreate('a');
+ link.addEventListener('mousedown', callbackClick);
+ link.className = 'box16';
+ link.innerHTML = '<span>' + user.icon + '</span> <span>' + StringUtil.escapeHTML(user.label) + '</span>';
+ elData(link, 'user-id', user.objectID);
+ elData(link, 'username', user.label);
+
+ listItem.appendChild(link);
+ this._dropdownMenu.appendChild(listItem);
+ }
+
+ this._dropdownMenu.classList.add('dropdownOpen');
+ this._dropdownActive = true;
+
+ this._updateDropdownPosition();
+ }
+ };
+
+ return UiRedactorMention;
+});
+
+/**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Page
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Page',['WoltLabSuite/Core/Ui/Page/Search'], function(UiPageSearch) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _click: function() {},
+ _insert: function() {}
+ };
+ return Fake;
+ }
+
+ function UiRedactorPage(editor, button) { this.init(editor, button); }
+ UiRedactorPage.prototype = {
+ init: function (editor, button) {
+ this._editor = editor;
+
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ UiPageSearch.open(this._insert.bind(this));
+ },
+
+ _insert: function (pageID) {
+ this._editor.buffer.set();
+
+ this._editor.insert.text("[wsp='" + pageID + "'][/wsp]");
+ }
+ };
+
+ return UiRedactorPage;
+});
+
+/**
+ * Manages quotes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Quote
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Quote',['Core', 'EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './Metacode', './PseudoHeader'], function (Core, EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorMetacode, UiRedactorPseudoHeader) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _insertQuote: function() {},
+ _click: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _save: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {},
+ _dialogSubmit: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @param {jQuery} button toolbar button
+ * @constructor
+ */
+ function UiRedactorQuote(editor, button) { this.init(editor, button); }
+ UiRedactorQuote.prototype = {
+ /**
+ * Initializes the quote management.
+ *
+ * @param {Object} editor editor instance
+ * @param {jQuery} button toolbar button
+ */
+ init: function(editor, button) {
+ this._quote = null;
+ this._quotes = elByTag('woltlab-quote', editor.$editor[0]);
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ this._editor.button.addCallback(button, this._click.bind(this));
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+
+ // quote manager
+ EventHandler.add('com.woltlab.wcf.redactor2', 'insertQuote_' + this._elementId, this._insertQuote.bind(this));
+ },
+
+ /**
+ * Inserts a quote.
+ *
+ * @param {Object} data quote data
+ * @protected
+ */
+ _insertQuote: function (data) {
+ if (this._editor.WoltLabSource.isActive()) {
+ return;
+ }
+
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'showEditor');
+
+ var editor = this._editor.core.editor()[0];
+ this._editor.selection.restore();
+
+ this._editor.buffer.set();
+
+ // caret must be within a `<p>`, if it is not: move it
+ var block = this._editor.selection.block();
+ if (block === false) {
+ this._editor.focus.end();
+ block = this._editor.selection.block();
+ }
+
+ while (block && block.parentNode !== editor) {
+ block = block.parentNode;
+ }
+
+ var quote = elCreate('woltlab-quote');
+ elData(quote, 'author', data.author);
+ elData(quote, 'link', data.link);
+
+ var content = data.content;
+ if (data.isText) {
+ content = StringUtil.escapeHTML(content);
+ content = '<p>' + content + '</p>';
+ content = content.replace(/\n\n/g, '</p><p>');
+ content = content.replace(/\n/g, '<br>');
+ }
+ else {
+ //noinspection JSUnresolvedFunction
+ content = UiRedactorMetacode.convertFromHtml(this._editor.$element[0].id, content);
+ }
+
+ // bypass the editor as `insert.html()` doesn't like us
+ quote.innerHTML = content;
+
+ block.parentNode.insertBefore(quote, block.nextSibling);
+
+ if (block.nodeName === 'P' && (block.innerHTML === '<br>' || block.innerHTML.replace(/\u200B/g, '') === '')) {
+ block.parentNode.removeChild(block);
+ }
+
+ // avoid adjacent blocks that are not paragraphs
+ var sibling = quote.previousElementSibling;
+ if (sibling && sibling.nodeName !== 'P') {
+ sibling = elCreate('p');
+ sibling.textContent = '\u200B';
+ quote.parentNode.insertBefore(sibling, quote);
+ }
+
+ this._editor.WoltLabCaret.paragraphAfterBlock(quote);
+
+ this._editor.buffer.set();
+ },
+
+ /**
+ * Toggles the quote block on button click.
+ *
+ * @protected
+ */
+ _click: function() {
+ this._editor.button.toggle({}, 'woltlab-quote', 'func', 'block.format');
+
+ var quote = this._editor.selection.block();
+ if (quote && quote.nodeName === 'WOLTLAB-QUOTE') {
+ this._setTitle(quote);
+
+ quote.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(quote);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ var quote;
+ for (var i = 0, length = this._quotes.length; i < length; i++) {
+ quote = this._quotes[i];
+
+ quote.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(quote);
+ }
+ },
+
+ /**
+ * Opens the dialog overlay to edit the quote's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var quote = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(quote);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(quote);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._quote = quote;
+
+ UiDialog.open(this);
+ }
+ },
+
+ /**
+ * Saves the changes to the quote's properties.
+ *
+ * @protected
+ */
+ _dialogSubmit: function() {
+ var id = 'redactor-quote-' + this._elementId;
+ var urlInput = elById(id + '-url');
+
+ var url = urlInput.value.replace(/\u200B/g, '').trim();
+ // simple test to check if it at least looks like it could be a valid url
+ if (url.length && !/^https?:\/\/[^\/]+/.test(url)) {
+ elInnerError(urlInput, Language.get('wcf.editor.quote.url.error.invalid'));
+ return;
+ }
+ else {
+ elInnerError(urlInput, false);
+ }
+
+ // set author
+ elData(this._quote, 'author', elById(id + '-author').value);
+
+ // set url
+ elData(this._quote, 'link', url);
+
+ this._setTitle(this._quote);
+ this._editor.caret.after(this._quote);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * Sets or updates the quote's header title.
+ *
+ * @param {Element} quote quote element
+ * @protected
+ */
+ _setTitle: function(quote) {
+ var title = Language.get('wcf.editor.quote.title', {
+ author: elData(quote, 'author'),
+ url: elData(quote, 'url')
+ });
+
+ if (elData(quote, 'title') !== title) {
+ elData(quote, 'title', title);
+ }
+ },
+
+ _delete: function (event) {
+ event.preventDefault();
+
+ var caretEnd = this._quote.nextElementSibling || this._quote.previousElementSibling;
+ if (caretEnd === null && this._quote.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._quote.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._quote);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var id = 'redactor-quote-' + this._elementId,
+ idAuthor = id + '-author',
+ idButtonDelete = id + '-button-delete',
+ idButtonSave = id + '-button-save',
+ idUrl = id + '-url';
+
+ return {
+ id: id,
+ options: {
+ onClose: (function () {
+ this._editor.selection.restore();
+
+ UiDialog.destroy(this);
+ }).bind(this),
+
+ onSetup: (function() {
+ elById(idButtonDelete).addEventListener(WCF_CLICK_EVENT, this._delete.bind(this));
+ }).bind(this),
+
+ onShow: (function() {
+ elById(idAuthor).value = elData(this._quote, 'author');
+ elById(idUrl).value = elData(this._quote, 'link');
+ }).bind(this),
+
+ title: Language.get('wcf.editor.quote.edit')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="' + idAuthor + '">' + Language.get('wcf.editor.quote.author') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idAuthor + '" class="long" data-dialog-submit-on-enter="true">'
+ + '</dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="' + idUrl + '">' + Language.get('wcf.editor.quote.url') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idUrl + '" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.quote.url.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button id="' + idButtonSave + '" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.save') + '</button>'
+ + '<button id="' + idButtonDelete + '">' + Language.get('wcf.global.button.delete') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+
+ return UiRedactorQuote;
+});
+/**
+ * Manages spoilers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Redactor/Spoiler
+ */
+define('WoltLabSuite/Core/Ui/Redactor/Spoiler',['EventHandler', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './PseudoHeader'], function (EventHandler, EventKey, Language, StringUtil, DomUtil, UiDialog, UiRedactorPseudoHeader) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _bbcodeSpoiler: function() {},
+ _observeLoad: function() {},
+ _edit: function() {},
+ _setTitle: function() {},
+ _delete: function() {},
+ _dialogSetup: function() {},
+ _dialogSubmit: function() {}
+ };
+ return Fake;
+ }
+
+ var _headerHeight = 0;
+
+ /**
+ * @param {Object} editor editor instance
+ * @constructor
+ */
+ function UiRedactorSpoiler(editor) { this.init(editor); }
+ UiRedactorSpoiler.prototype = {
+ /**
+ * Initializes the spoiler management.
+ *
+ * @param {Object} editor editor instance
+ */
+ init: function(editor) {
+ this._editor = editor;
+ this._elementId = this._editor.$element[0].id;
+ this._spoiler = null;
+
+ EventHandler.add('com.woltlab.wcf.redactor2', 'bbcode_spoiler_' + this._elementId, this._bbcodeSpoiler.bind(this));
+ EventHandler.add('com.woltlab.wcf.redactor2', 'observe_load_' + this._elementId, this._observeLoad.bind(this));
+
+ // static bind to ensure that removing works
+ this._callbackEdit = this._edit.bind(this);
+
+ // bind listeners on init
+ this._observeLoad();
+ },
+
+ /**
+ * Intercepts the insertion of `[spoiler]` tags and uses
+ * the custom `<woltlab-spoiler>` element instead.
+ *
+ * @param {Object} data event data
+ * @protected
+ */
+ _bbcodeSpoiler: function(data) {
+ data.cancel = true;
+
+ this._editor.button.toggle({}, 'woltlab-spoiler', 'func', 'block.format');
+
+ var spoiler = this._editor.selection.block();
+ if (spoiler && spoiler.nodeName === 'WOLTLAB-SPOILER') {
+ this._setTitle(spoiler);
+
+ spoiler.addEventListener(WCF_CLICK_EVENT, this._callbackEdit);
+
+ // work-around for Safari
+ this._editor.caret.end(spoiler);
+ }
+ },
+
+ /**
+ * Binds event listeners and sets quote title on both editor
+ * initialization and when switching back from code view.
+ *
+ * @protected
+ */
+ _observeLoad: function() {
+ elBySelAll('woltlab-spoiler', this._editor.$editor[0], (function(spoiler) {
+ spoiler.addEventListener('mousedown', this._callbackEdit);
+ this._setTitle(spoiler);
+ }).bind(this));
+ },
+
+ /**
+ * Opens the dialog overlay to edit the spoiler's properties.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _edit: function(event) {
+ var spoiler = event.currentTarget;
+
+ if (_headerHeight === 0) {
+ _headerHeight = UiRedactorPseudoHeader.getHeight(spoiler);
+ }
+
+ // check if the click hit the header
+ var offset = DomUtil.offset(spoiler);
+ if (event.pageY > offset.top && event.pageY < (offset.top + _headerHeight)) {
+ event.preventDefault();
+
+ this._editor.selection.save();
+ this._spoiler = spoiler;
+
+ UiDialog.open(this);
+ }
+ },
+
+ /**
+ * Saves the changes to the spoiler's properties.
+ *
+ * @protected
+ */
+ _dialogSubmit: function() {
+ elData(this._spoiler, 'label', elById('redactor-spoiler-' + this._elementId + '-label').value);
+
+ this._setTitle(this._spoiler);
+ this._editor.caret.after(this._spoiler);
+
+ UiDialog.close(this);
+ },
+
+ /**
+ * Sets or updates the spoiler's header title.
+ *
+ * @param {Element} spoiler spoiler element
+ * @protected
+ */
+ _setTitle: function(spoiler) {
+ var title = Language.get('wcf.editor.spoiler.title', { label: elData(spoiler, 'label') });
+
+ if (elData(spoiler, 'title') !== title) {
+ elData(spoiler, 'title', title);
+ }
+ },
+
+ _delete: function (event) {
+ event.preventDefault();
+
+ var caretEnd = this._spoiler.nextElementSibling || this._spoiler.previousElementSibling;
+ if (caretEnd === null && this._spoiler.parentNode !== this._editor.core.editor()[0]) {
+ caretEnd = this._spoiler.parentNode;
+ }
+
+ if (caretEnd === null) {
+ this._editor.code.set('');
+ this._editor.focus.end();
+ }
+ else {
+ elRemove(this._spoiler);
+ this._editor.caret.end(caretEnd);
+ }
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ var id = 'redactor-spoiler-' + this._elementId,
+ idButtonDelete = id + '-button-delete',
+ idButtonSave = id + '-button-save',
+ idLabel = id + '-label';
+
+ return {
+ id: id,
+ options: {
+ onClose: (function () {
+ this._editor.selection.restore();
+
+ UiDialog.destroy(this);
+ }).bind(this),
+
+ onSetup: (function() {
+ elById(idButtonDelete).addEventListener(WCF_CLICK_EVENT, this._delete.bind(this));
+ }).bind(this),
+
+ onShow: (function() {
+ elById(idLabel).value = elData(this._spoiler, 'label');
+ }).bind(this),
+
+ title: Language.get('wcf.editor.spoiler.edit')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="' + idLabel + '">' + Language.get('wcf.editor.spoiler.label') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="' + idLabel + '" class="long" data-dialog-submit-on-enter="true">'
+ + '<small>' + Language.get('wcf.editor.spoiler.label.description') + '</small>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<div class="formSubmit">'
+ + '<button id="' + idButtonSave + '" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.save') + '</button>'
+ + '<button id="' + idButtonDelete + '">' + Language.get('wcf.global.button.delete') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+
+ return UiRedactorSpoiler;
+});
+define('WoltLabSuite/Core/Ui/Redactor/Table',['Language', 'Ui/Dialog'], function(Language, UiDialog) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ showDialog: function() {},
+ _submit: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callback = null;
+
+ return {
+ showDialog: function(options) {
+ UiDialog.open(this);
+
+ _callback = options.submitCallback;
+ },
+
+ _dialogSubmit: function() {
+ // check if rows and cols are within the boundaries
+ var isValid = true;
+ ['rows', 'cols'].forEach(function(type) {
+ var input = elById('redactor-table-' + type);
+ if (input.value < 1 || input.value > 100) {
+ isValid = false;
+ }
+ });
+
+ if (!isValid) return;
+
+ _callback();
+
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'redactorDialogTable',
+ options: {
+ onShow: function () {
+ elById('redactor-table-rows').value = 2;
+ elById('redactor-table-cols').value = 3;
+ },
+ title: Language.get('wcf.editor.table.insertTable')
+ },
+ source: '<dl>'
+ + '<dt><label for="redactor-table-rows">' + Language.get('wcf.editor.table.rows') + '</label></dt>'
+ + '<dd><input type="number" id="redactor-table-rows" class="small" min="1" max="100" value="2" data-dialog-submit-on-enter="true"></dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt><label for="redactor-table-cols">' + Language.get('wcf.editor.table.cols') + '</label></dt>'
+ + '<dd><input type="number" id="redactor-table-cols" class="small" min="1" max="100" value="3" data-dialog-submit-on-enter="true"></dd>'
+ + '</dl>'
+ + '<div class="formSubmit">'
+ + '<button id="redactor-modal-button-action" class="buttonPrimary" data-type="submit">' + Language.get('wcf.global.button.insert') + '</button>'
+ + '</div>'
+ };
+ }
+ };
+});
+
+define('WoltLabSuite/Core/Ui/Search/Page',['Core', 'Dom/Traverse', 'Dom/Util', 'Ui/Screen', 'Ui/SimpleDropdown', './Input'], function(Core, DomTraverse, DomUtil, UiScreen, UiSimpleDropdown, UiSearchInput) {
+ "use strict";
+
+ return {
+ init: function (objectType) {
+ var searchInput = elById('pageHeaderSearchInput');
+
+ new UiSearchInput(searchInput, {
+ ajax: {
+ className: 'wcf\\data\\search\\keyword\\SearchKeywordAction'
+ },
+ callbackDropdownInit: function(dropdownMenu) {
+ dropdownMenu.classList.add('dropdownMenuPageSearch');
+
+ if (UiScreen.is('screen-lg')) {
+ elData(dropdownMenu, 'dropdown-alignment-horizontal', 'right');
+
+ var minWidth = searchInput.clientWidth;
+ dropdownMenu.style.setProperty('min-width', minWidth + 'px', '');
+
+ // calculate offset to ignore the width caused by the submit button
+ var parent = searchInput.parentNode;
+ var offsetRight = (DomUtil.offset(parent).left + parent.clientWidth) - (DomUtil.offset(searchInput).left + minWidth);
+ var offsetTop = DomUtil.styleAsInt(window.getComputedStyle(parent), 'padding-bottom');
+ dropdownMenu.style.setProperty('transform', 'translateX(-' + Math.ceil(offsetRight) + 'px) translateY(-' + offsetTop + 'px)', '');
+ }
+ },
+ callbackSelect: function() {
+ setTimeout(function() {
+ DomTraverse.parentByTag(searchInput, 'FORM').submit();
+ }, 1);
+
+ return true;
+ }
+ });
+
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(DomUtil.identify(elBySel('.pageHeaderSearchType')));
+ var callback = this._click.bind(this);
+ elBySelAll('a[data-object-type]', dropdownMenu, function(link) {
+ link.addEventListener(WCF_CLICK_EVENT, callback);
+ });
+
+ // trigger click on init
+ var link = elBySel('a[data-object-type="' + objectType + '"]', dropdownMenu);
+ Core.triggerEvent(link, WCF_CLICK_EVENT);
+ },
+
+ _click: function(event) {
+ event.preventDefault();
+
+ var pageHeader = elById('pageHeader');
+ pageHeader.classList.add('searchBarForceOpen');
+ window.setTimeout(function() {
+ pageHeader.classList.remove('searchBarForceOpen');
+ }, 10);
+
+ var objectType = elData(event.currentTarget, 'object-type');
+
+ var container = elById('pageHeaderSearchParameters');
+ container.innerHTML = '';
+
+ var extendedLink = elData(event.currentTarget, 'extended-link');
+ if (extendedLink) {
+ elBySel('.pageHeaderSearchExtendedLink').href = extendedLink;
+ }
+
+ var parameters = elData(event.currentTarget, 'parameters');
+ if (parameters) {
+ parameters = JSON.parse(parameters);
+ }
+ else {
+ parameters = {};
+ }
+
+ if (objectType) parameters['types[]'] = objectType;
+
+ for (var key in parameters) {
+ if (parameters.hasOwnProperty(key)) {
+ var input = elCreate('input');
+ input.type = 'hidden';
+ input.name = key;
+ input.value = parameters[key];
+ container.appendChild(input);
+ }
+ }
+
+ // update label
+ var button = elBySel('.pageHeaderSearchType > .button > .pageHeaderSearchTypeLabel', elById('pageHeaderSearchInputContainer'));
+ button.textContent = event.currentTarget.textContent;
+ }
+ };
+});
+
+/**
+ * Inserts smilies into a WYSIWYG editor instance, with WAI-ARIA keyboard support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Smiley/Insert
+ */
+define('WoltLabSuite/Core/Ui/Smiley/Insert',['EventHandler', 'EventKey'], function (EventHandler, EventKey) {
+ 'use strict';
+
+ function UiSmileyInsert(editorId) { this.init(editorId); }
+
+ UiSmileyInsert.prototype = {
+ _editorId: '',
+
+ /**
+ * @param {string} editorId
+ */
+ init: function (editorId) {
+ this._editorId = editorId;
+
+ var container = elById('smilies-' + this._editorId);
+ if (!container) {
+ // form builder
+ container = elById(this._editorId + 'SmiliesTabContainer');
+ if (!container) {
+ throw new Error('Unable to find the message tab menu container containing the smilies.');
+ }
+ }
+
+ container.addEventListener('keydown', this._keydown.bind(this));
+ container.addEventListener('mousedown', this._mousedown.bind(this));
+ },
+
+ /**
+ * @param {KeyboardEvent} event
+ * @protected
+ */
+ _keydown: function(event) {
+ var activeButton = document.activeElement;
+ if (!activeButton.classList.contains('jsSmiley')) {
+ return;
+ }
+
+ if (EventKey.ArrowLeft(event) || EventKey.ArrowRight(event) || EventKey.Home(event) || EventKey.End(event)) {
+ event.preventDefault();
+
+ var smilies = Array.prototype.slice.call(elBySelAll('.jsSmiley', event.currentTarget));
+ if (EventKey.ArrowLeft(event)) {
+ smilies.reverse();
+ }
+
+ var index = smilies.indexOf(activeButton);
+ if (EventKey.Home(event)) {
+ index = 0;
+ }
+ else if (EventKey.End(event)) {
+ index = smilies.length - 1;
+ }
+ else {
+ index = index + 1;
+ if (index === smilies.length) {
+ index = 0;
+ }
+ }
+
+ smilies[index].focus();
+ }
+ else if (EventKey.Enter(event) || EventKey.Space(event)) {
+ event.preventDefault();
+
+ this._insert(elBySel('img', activeButton));
+ }
+ },
+
+ /**
+ * @param {MouseEvent} event
+ * @protected
+ */
+ _mousedown: function (event) {
+ event.preventDefault();
+
+ // Clicks may occur on a few different elements, but we are only looking for the image.
+ var listItem = event.target.closest('li');
+ var img = elBySel('img', listItem);
+ if (img) this._insert(img);
+ },
+
+ /**
+ * @param {Element} img
+ * @protected
+ */
+ _insert: function(img) {
+ EventHandler.fire('com.woltlab.wcf.redactor2', 'insertSmiley_' + this._editorId, {
+ img: img
+ });
+ }
+ };
+ return UiSmileyInsert;
+});
+
+/**
+ * Provides a selection dialog for FontAwesome icons with filter capabilities.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Style/FontAwesome
+ */
+define('WoltLabSuite/Core/Ui/Style/FontAwesome',['Language', 'Ui/Dialog', 'WoltLabSuite/Core/Ui/ItemList/Filter'], function (Language, UiDialog, UiItemListFilter) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ setup: function() {},
+ open: function() {},
+ _click: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _callback, _iconList, _itemListFilter;
+ var _icons = [];
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Style/FontAwesome
+ */
+ return {
+ /**
+ * Sets the list of available icons, must be invoked prior to any call
+ * to the `open()` method.
+ *
+ * @param {string[]} icons list of icon names excluding the `fa-` prefix
+ */
+ setup: function (icons) {
+ _icons = icons;
+ },
+
+ /**
+ * Shows the FontAwesome selection dialog, supplied callback will be
+ * invoked with the selection icon's name as the only argument.
+ *
+ * @param {Function<string>} callback callback on icon selection, receives icon name only
+ */
+ open: function(callback) {
+ if (_icons.length === 0) {
+ throw new Error("Missing icon data, please include the template before calling this method using `{include file='fontAwesomeJavaScript'}`.");
+ }
+
+ _callback = callback;
+
+ UiDialog.open(this);
+ },
+
+ /**
+ * Selects an icon, notifies the callback and closes the dialog.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ var item = event.target.closest('li');
+ var icon = elBySel('small', item).textContent.trim();
+
+ UiDialog.close(this);
+
+ _callback(icon);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'fontAwesomeSelection',
+ options: {
+ onSetup: (function() {
+ _iconList = elById('fontAwesomeIcons');
+
+ // build icons
+ var icon, html = '';
+ for (var i = 0, length = _icons.length; i < length; i++) {
+ icon = _icons[i];
+
+ html += '<li><span class="icon icon48 fa-' + icon + '"></span><small>' + icon + '</small></li>';
+ }
+
+ _iconList.innerHTML = html;
+ _iconList.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+
+ _itemListFilter = new UiItemListFilter('fontAwesomeIcons', {
+ callbackPrepareItem: function (item) {
+ var small = elBySel('small', item);
+ var text = small.textContent.trim();
+
+ return {
+ item: item,
+ span: small,
+ text: text
+ };
+ },
+ enableVisibilityFilter: false
+ });
+ }).bind(this),
+ onShow: function () {
+ _itemListFilter.reset();
+ },
+ title: Language.get('wcf.global.fontAwesome.selectIcon')
+ },
+ source: '<ul class="fontAwesomeIcons" id="fontAwesomeIcons"></ul>'
+ };
+ }
+ }
+});
+/**
+ * Provides a simple toggle to show or hide certain elements when the
+ * target element is checked.
+ *
+ * Be aware that the list of elements to show or hide accepts selectors
+ * which will be passed to `elBySel()`, causing only the first matched
+ * element to be used. If you require a whole list of elements identified
+ * by a single selector to be handled, please provide the actual list of
+ * elements instead.
+ *
+ * Usage:
+ *
+ * new UiToggleInput('input[name="foo"][value="bar"]', {
+ * show: ['#showThisContainer', '.makeThisVisibleToo'],
+ * hide: ['.notRelevantStuff', elById('fooBar')]
+ * });
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Toggle/Input
+ */
+define('WoltLabSuite/Core/Ui/Toggle/Input',['Core'], function(Core) {
+ "use strict";
+
+ /**
+ * @param {string} elementSelector element selector used with `elBySel()`
+ * @param {Object} options toggle options
+ * @constructor
+ */
+ function UiToggleInput(elementSelector, options) { this.init(elementSelector, options); }
+ UiToggleInput.prototype = {
+ /**
+ * Initializes a new input toggle.
+ *
+ * @param {string} elementSelector element selector used with `elBySel()`
+ * @param {Object} options toggle options
+ */
+ init: function(elementSelector, options) {
+ this._element = elBySel(elementSelector);
+ if (this._element === null) {
+ throw new Error("Unable to find element by selector '" + elementSelector + "'.");
+ }
+
+ var type = (this._element.nodeName === 'INPUT') ? elAttr(this._element, 'type') : '';
+ if (type !== 'checkbox' && type !== 'radio') {
+ throw new Error("Illegal element, expected input[type='checkbox'] or input[type='radio'].");
+ }
+
+ this._options = Core.extend({
+ hide: [],
+ show: []
+ }, options);
+
+ ['hide', 'show'].forEach((function(type) {
+ var element, i, length;
+ for (i = 0, length = this._options[type].length; i < length; i++) {
+ element = this._options[type][i];
+
+ if (typeof element !== 'string' && !(element instanceof Element)) {
+ throw new TypeError("The array '" + type + "' may only contain string selectors or DOM elements.");
+ }
+ }
+ }).bind(this));
+
+ this._element.addEventListener('change', this._change.bind(this));
+
+ this._handleElements(this._options.show, this._element.checked);
+ this._handleElements(this._options.hide, !this._element.checked);
+ },
+
+ /**
+ * Triggered when element is checked / unchecked.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _change: function(event) {
+ var showElements = event.currentTarget.checked;
+
+ this._handleElements(this._options.show, showElements);
+ this._handleElements(this._options.hide, !showElements);
+ },
+
+ /**
+ * Loops through the target elements and shows / hides them.
+ *
+ * @param {Array} elements list of elements or selectors
+ * @param {boolean} showElement true if elements should be shown
+ * @protected
+ */
+ _handleElements: function(elements, showElement) {
+ var element, tmp;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (typeof element === 'string') {
+ tmp = elBySel(element);
+ if (tmp === null) {
+ throw new Error("Unable to find element by selector '" + element + "'.");
+ }
+
+ elements[i] = element = tmp;
+ }
+
+ window[(showElement ? 'elShow' : 'elHide')](element);
+ }
+ }
+ };
+
+ return UiToggleInput;
+});
+
+/**
+ * Simple notification overlay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Editor
+ */
+define('WoltLabSuite/Core/Ui/User/Editor',['Ajax', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', 'Ui/Notification'], function(Ajax, Language, StringUtil, DomUtil, UiDialog, UiNotification) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ _click: function() {},
+ _submit: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ _dialogSetup: function() {}
+ };
+ return Fake;
+ }
+
+ var _actionName = '';
+ var _userHeader = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/User/Editor
+ */
+ return {
+ /**
+ * Initializes the user editor.
+ */
+ init: function() {
+ _userHeader = elBySel('.userProfileUser');
+
+ // init buttons
+ ['ban', 'disableAvatar', 'disableCoverPhoto', 'disableSignature', 'enable'].forEach((function(action) {
+ var button = elBySel('.userProfileButtonMenu .jsButtonUser' + StringUtil.ucfirst(action));
+
+ // button is missing if users lacks the permission
+ if (button) {
+ elData(button, 'action', action);
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Handles clicks on action buttons.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ //noinspection JSCheckFunctionSignatures
+ var action = elData(event.currentTarget, 'action');
+ var actionName = '';
+ switch (action) {
+ case 'ban':
+ if (elDataBool(_userHeader, 'banned')) {
+ actionName = 'unban';
+ }
+ break;
+
+ case 'disableAvatar':
+ if (elDataBool(_userHeader, 'disable-avatar')) {
+ actionName = 'enableAvatar';
+ }
+ break;
+
+ case 'disableCoverPhoto':
+ if (elDataBool(_userHeader, 'disable-cover-photo')) {
+ actionName = 'enableCoverPhoto';
+ }
+ break;
+
+ case 'disableSignature':
+ if (elDataBool(_userHeader, 'disable-signature')) {
+ actionName = 'enableSignature';
+ }
+ break;
+
+ case 'enable':
+ actionName = (elDataBool(_userHeader, 'is-disabled')) ? 'enable' : 'disable';
+ break;
+ }
+
+ if (actionName === '') {
+ _actionName = action;
+
+ UiDialog.open(this);
+ }
+ else {
+ Ajax.api(this, {
+ actionName: actionName
+ });
+ }
+ },
+
+ /**
+ * Handles form submit and input validation.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _submit: function(event) {
+ event.preventDefault();
+
+ var label = elById('wcfUiUserEditorExpiresLabel');
+
+ var expires = '';
+ var errorMessage = '';
+ if (!elById('wcfUiUserEditorNeverExpires').checked) {
+ expires = elById('wcfUiUserEditorExpiresDatePicker').value;
+ if (expires === '') {
+ errorMessage = Language.get('wcf.global.form.error.empty');
+ }
+ }
+
+ elInnerError(label, errorMessage);
+
+ var parameters = {};
+ parameters[_actionName + 'Expires'] = expires;
+ parameters[_actionName + 'Reason'] = elById('wcfUiUserEditorReason').value.trim();
+
+ Ajax.api(this, {
+ actionName: _actionName,
+ parameters: parameters
+ });
+ },
+
+ _ajaxSuccess: function(data) {
+ switch (data.actionName) {
+ case 'ban':
+ case 'unban':
+ elData(_userHeader, 'banned', (data.actionName === 'ban'));
+ elBySel('.userProfileButtonMenu .jsButtonUserBan').textContent = Language.get('wcf.user.' + (data.actionName === 'ban' ? 'unban' : 'ban'));
+
+ var contentTitle = elBySel('.contentTitle', _userHeader);
+ var banIcon = elBySel('.jsUserBanned', contentTitle);
+ if (data.actionName === 'ban') {
+ banIcon = elCreate('span');
+ banIcon.className = 'icon icon24 fa-lock jsUserBanned jsTooltip';
+ banIcon.title = data.returnValues;
+ contentTitle.appendChild(banIcon);
+ }
+ else if (banIcon) {
+ elRemove(banIcon);
+ }
+
+ break;
+
+ case 'disableAvatar':
+ case 'enableAvatar':
+ elData(_userHeader, 'disable-avatar', (data.actionName === 'disableAvatar'));
+ elBySel('.userProfileButtonMenu .jsButtonUserDisableAvatar').textContent = Language.get('wcf.user.' + (data.actionName === 'disableAvatar' ? 'enable' : 'disable') + 'Avatar');
+
+ break;
+
+ case 'disableCoverPhoto':
+ case 'enableCoverPhoto':
+ elData(_userHeader, 'disable-cover-photo', (data.actionName === 'disableCoverPhoto'));
+ elBySel('.userProfileButtonMenu .jsButtonUserDisableCoverPhoto').textContent = Language.get('wcf.user.' + (data.actionName === 'disableCoverPhoto' ? 'enable' : 'disable') + 'CoverPhoto');
+
+ break;
+
+ case 'disableSignature':
+ case 'enableSignature':
+ elData(_userHeader, 'disable-signature', (data.actionName === 'disableSignature'));
+ elBySel('.userProfileButtonMenu .jsButtonUserDisableSignature').textContent = Language.get('wcf.user.' + (data.actionName === 'disableSignature' ? 'enable' : 'disable') + 'Signature');
+
+ break;
+
+ case 'enable':
+ case 'disable':
+ elData(_userHeader, 'is-disabled', (data.actionName === 'disable'));
+ elBySel('.userProfileButtonMenu .jsButtonUserEnable').textContent = Language.get('wcf.acp.user.' + (data.actionName === 'enable' ? 'disable' : 'enable'));
+
+ break;
+ }
+
+ if (data.actionName === 'ban' || data.actionName === 'disableAvatar' || data.actionName === 'disableCoverPhoto' || data.actionName === 'disableSignature') {
+ UiDialog.close(this);
+ }
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\UserAction',
+ objectIDs: [ elData(_userHeader, 'object-id') ]
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiUserEditor',
+ options: {
+ onSetup: (function (content) {
+ elById('wcfUiUserEditorNeverExpires').addEventListener('change', function () {
+ window[(this.checked) ? 'elHide' : 'elShow'](elById('wcfUiUserEditorExpiresSettings'));
+ });
+
+ elBySel('button.buttonPrimary', content).addEventListener(WCF_CLICK_EVENT, this._submit.bind(this));
+ }).bind(this),
+ onShow: function(content) {
+ UiDialog.setTitle('wcfUiUserEditor', Language.get('wcf.user.' + _actionName + '.confirmMessage'));
+
+ var label = elById('wcfUiUserEditorReason').nextElementSibling;
+ var phrase = 'wcf.user.' + _actionName + '.reason.description';
+ label.textContent = Language.get(phrase);
+ window[(label.textContent === phrase) ? 'elHide' : 'elShow'](label);
+
+ label = elById('wcfUiUserEditorNeverExpires').nextElementSibling;
+ label.textContent = Language.get('wcf.user.' + _actionName + '.neverExpires');
+
+ label = elBySel('label[for="wcfUiUserEditorExpires"]', content);
+ label.textContent = Language.get('wcf.user.' + _actionName + '.expires');
+
+ label = elById('wcfUiUserEditorExpiresLabel');
+ label.textContent = Language.get('wcf.user.' + _actionName + '.expires.description');
+ }
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiUserEditorReason">' + Language.get('wcf.global.reason') + '</label></dt>'
+ + '<dd><textarea id="wcfUiUserEditorReason" cols="40" rows="3"></textarea><small></small></dd>'
+ + '</dl>'
+ + '<dl>'
+ + '<dt></dt>'
+ + '<dd><label><input type="checkbox" id="wcfUiUserEditorNeverExpires" checked> <span></span></label></dd>'
+ + '</dl>'
+ + '<dl id="wcfUiUserEditorExpiresSettings" style="display: none">'
+ + '<dt><label for="wcfUiUserEditorExpires"></label></dt>'
+ + '<dd>'
+ + '<input type="date" name="wcfUiUserEditorExpires" id="wcfUiUserEditorExpires" class="medium" min="' + new Date(TIME_NOW * 1000).toISOString() + '" data-ignore-timezone="true">'
+ + '<small id="wcfUiUserEditorExpiresLabel"></small>'
+ + '</dd>'
+ +'</dl>'
+ + '</div>'
+ + '<div class="formSubmit"><button class="buttonPrimary">' + Language.get('wcf.global.button.submit') + '</button></div>'
+ };
+ }
+ };
+});
+
+/**
+ * Shows and hides an element that depends on certain selected pages when setting up conditions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Condition/Page/Dependence
+ */
+define('WoltLabSuite/Core/Controller/Condition/Page/Dependence',['Dom/ChangeListener', 'Dom/Traverse', 'EventHandler', 'ObjectMap'], function(DomChangeListener, DomTraverse, EventHandler, ObjectMap) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ register: function() {},
+ _checkVisibility: function() {},
+ _hideDependentElement: function() {},
+ _showDependentElement: function() {}
+ };
+ return Fake;
+ }
+
+ var _pages = elBySelAll('input[name="pageIDs[]"]');
+ var _dependentElements = [];
+ var _pageIds = new ObjectMap();
+ var _hiddenElements = new ObjectMap();
+
+ var _didInit = false;
+
+ return {
+ register: function(dependentElement, pageIds) {
+ _dependentElements.push(dependentElement);
+ _pageIds.set(dependentElement, pageIds);
+ _hiddenElements.set(dependentElement, []);
+
+ if (!_didInit) {
+ for (var i = 0, length = _pages.length; i < length; i++) {
+ _pages[i].addEventListener('change', this._checkVisibility.bind(this));
+ }
+
+ _didInit = true;
+ }
+
+ // remove the dependent element before submit if it is hidden
+ DomTraverse.parentByTag(dependentElement, 'FORM').addEventListener('submit', function() {
+ if (dependentElement.style.getPropertyValue('display') === 'none') {
+ dependentElement.remove();
+ }
+ });
+
+ this._checkVisibility();
+ },
+
+ /**
+ * Checks if only relevant pages are selected. If that is the case, the dependent
+ * element is shown, otherwise it is hidden.
+ *
+ * @private
+ */
+ _checkVisibility: function() {
+ var dependentElement, page, pageIds, checkedPageIds, irrelevantPageIds;
+
+ depenentElementLoop: for (var i = 0, length = _dependentElements.length; i < length; i++) {
+ dependentElement = _dependentElements[i];
+ pageIds = _pageIds.get(dependentElement);
+
+ checkedPageIds = [];
+ for (var j = 0, length2 = _pages.length; j < length2; j++) {
+ page = _pages[j];
+
+ if (page.checked) {
+ checkedPageIds.push(~~page.value);
+ }
+ }
+
+ irrelevantPageIds = checkedPageIds.filter(function(pageId) {
+ return pageIds.indexOf(pageId) === -1;
+ });
+
+ if (!checkedPageIds.length || irrelevantPageIds.length) {
+ this._hideDependentElement(dependentElement);
+ }
+ else {
+ this._showDependentElement(dependentElement);
+ }
+ }
+
+ EventHandler.fire('com.woltlab.wcf.pageConditionDependence', 'checkVisivility');
+ },
+
+ /**
+ * Hides all elements that depend on the given element.
+ *
+ * @param {HTMLElement} dependentElement
+ */
+ _hideDependentElement: function(dependentElement) {
+ elHide(dependentElement);
+
+ var hiddenElements = _hiddenElements.get(dependentElement);
+ for (var i = 0, length = hiddenElements.length; i < length; i++) {
+ elHide(hiddenElements[i]);
+ }
+
+ _hiddenElements.set(dependentElement, []);
+ },
+
+ /**
+ * Shows all elements that depend on the given element.
+ *
+ * @param {HTMLElement} dependentElement
+ */
+ _showDependentElement: function(dependentElement) {
+ elShow(dependentElement);
+
+ // make sure that all parent elements are also visible
+ var parentNode = dependentElement;
+ while ((parentNode = parentNode.parentNode) && parentNode instanceof Element) {
+ if (parentNode.style.getPropertyValue('display') === 'none') {
+ _hiddenElements.get(dependentElement).push(parentNode);
+ }
+
+ elShow(parentNode);
+ }
+ }
+ };
+});
+
+/**
+ * Map route planner based on Google Maps.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/Map/Route/Planner
+ */
+define('WoltLabSuite/Core/Controller/Map/Route/Planner',[
+ 'Dom/Traverse',
+ 'Dom/Util',
+ 'Language',
+ 'Ui/Dialog',
+ 'WoltLabSuite/Core/Ajax/Status'
+], function(
+ DomTraverse,
+ DomUtil,
+ Language,
+ UiDialog,
+ AjaxStatus
+) {
+ /**
+ * @constructor
+ */
+ function Planner(buttonId, destination) {
+ this._button = elById(buttonId);
+ if (this._button === null) {
+ throw new Error("Unknown button with id '" + buttonId + "'");
+ }
+
+ this._button.addEventListener('click', this._openDialog.bind(this));
+
+ this._destination = destination;
+ }
+ Planner.prototype = {
+ /**
+ * Sets up the route planner dialog.
+ */
+ _dialogSetup: function() {
+ return {
+ id: this._button.id + 'Dialog',
+ options: {
+ onShow: this._initDialog.bind(this),
+ title: Language.get('wcf.map.route.planner')
+ },
+ source: '<div class="googleMapsDirectionsContainer" style="display: none;">' +
+ '<div class="googleMap"></div>' +
+ '<div class="googleMapsDirections"></div>' +
+ '</div>' +
+ '<small class="googleMapsDirectionsGoogleLinkContainer"><a href="' + this._getGoogleMapsLink() + '" class="googleMapsDirectionsGoogleLink" target="_blank" style="display: none;">' + Language.get('wcf.map.route.viewOnGoogleMaps') + '</a></small>' +
+ '<dl>' +
+ '<dt>' + Language.get('wcf.map.route.origin') + '</dt>' +
+ '<dd><input type="text" name="origin" class="long" autofocus /></dd>' +
+ '</dl>' +
+ '<dl style="display: none;">' +
+ '<dt>' + Language.get('wcf.map.route.travelMode') + '</dt>' +
+ '<dd>' +
+ '<select name="travelMode">' +
+ '<option value="driving">' + Language.get('wcf.map.route.travelMode.driving') + '</option>' +
+ '<option value="walking">' + Language.get('wcf.map.route.travelMode.walking') + '</option>' +
+ '<option value="bicycling">' + Language.get('wcf.map.route.travelMode.bicycling') + '</option>' +
+ '<option value="transit">' + Language.get('wcf.map.route.travelMode.transit') + '</option>' +
+ '</select>' +
+ '</dd>' +
+ '</dl>'
+ }
+ },
+
+ /**
+ * Calculates the route based on the given result of a location search.
+ *
+ * @param {object} data
+ */
+ _calculateRoute: function(data) {
+ var dialog = UiDialog.getDialog(this).dialog;
+
+ if (data.label) {
+ this._originInput.value = data.label;
+ }
+
+ if (this._map === undefined) {
+ this._map = new google.maps.Map(elByClass('googleMap', dialog)[0], {
+ disableDoubleClickZoom: WCF.Location.GoogleMaps.Settings.get('disableDoubleClickZoom'),
+ draggable: WCF.Location.GoogleMaps.Settings.get('draggable'),
+ mapTypeId: google.maps.MapTypeId.ROADMAP,
+ scaleControl: WCF.Location.GoogleMaps.Settings.get('scaleControl'),
+ scrollwheel: WCF.Location.GoogleMaps.Settings.get('scrollwheel')
+ });
+
+ this._directionsService = new google.maps.DirectionsService();
+ this._directionsRenderer = new google.maps.DirectionsRenderer();
+
+ this._directionsRenderer.setMap(this._map);
+ this._directionsRenderer.setPanel(elByClass('googleMapsDirections', dialog)[0]);
+
+ this._googleLink = elByClass('googleMapsDirectionsGoogleLink', dialog)[0];
+ }
+
+ var request = {
+ destination: this._destination,
+ origin: data.location,
+ provideRouteAlternatives: true,
+ travelMode: google.maps.TravelMode[this._travelMode.value.toUpperCase()]
+ };
+
+ AjaxStatus.show();
+ this._directionsService.route(request, this._setRoute.bind(this));
+
+ elAttr(this._googleLink, 'href', this._getGoogleMapsLink(data.location, this._travelMode.value));
+
+ this._lastOrigin = data.location;
+ },
+
+ /**
+ * Returns the Google Maps link based on the given optional directions origin
+ * and optional travel mode.
+ *
+ * @param {google.maps.LatLng} origin
+ * @param {string} travelMode
+ * @return {string}
+ */
+ _getGoogleMapsLink: function(origin, travelMode) {
+ if (origin) {
+ var link = 'https://www.google.com/maps/dir/?api=1' +
+ '&origin=' + origin.lat() + ',' + origin.lng() + '' +
+ '&destination=' + this._destination.lat() + ',' + this._destination.lng();
+
+ if (travelMode) {
+ link += '&travelmode=' + travelMode;
+ }
+
+ return link;
+ }
+
+ return 'https://www.google.com/maps/search/?api=1&query=' + this._destination.lat() + ',' + this._destination.lng();
+ },
+
+ /**
+ * Initializes the route planning dialog.
+ */
+ _initDialog: function() {
+ if (!this._didInitDialog) {
+ var dialog = UiDialog.getDialog(this).dialog;
+
+ // make input element a location search
+ this._originInput = elBySel('input[name="origin"]', dialog);
+ new WCF.Location.GoogleMaps.LocationSearch(this._originInput, this._calculateRoute.bind(this));
+
+ this._travelMode = elBySel('select[name="travelMode"]', dialog);
+ this._travelMode.addEventListener('change', this._updateRoute.bind(this));
+
+ this._didInitDialog = true;
+ }
+ },
+
+ /**
+ * Opens the route planning dialog.
+ */
+ _openDialog: function() {
+ UiDialog.open(this);
+ },
+
+ /**
+ * Handles the response of the direction service.
+ *
+ * @param {object} result
+ * @param {string} status
+ */
+ _setRoute: function(result, status) {
+ AjaxStatus.hide();
+
+ if (status === 'OK') {
+ elShow(this._map.getDiv().parentNode);
+
+ google.maps.event.trigger(this._map, 'resize');
+
+ this._directionsRenderer.setDirections(result);
+
+ elShow(DomTraverse.parentByTag(this._travelMode, 'DL'));
+ elShow(this._googleLink);
+
+ elInnerError(this._originInput, false);
+ }
+ else {
+ // map irrelevant errors to not found error
+ if (status !== 'OVER_QUERY_LIMIT' && status !== 'REQUEST_DENIED') {
+ status = 'NOT_FOUND';
+ }
+
+ elInnerError(this._originInput, Language.get('wcf.map.route.error.' + status.toLowerCase()));
+ }
+ },
+
+ /**
+ * Updates the route after the travel mode has been changed.
+ */
+ _updateRoute: function() {
+ this._calculateRoute({
+ location: this._lastOrigin
+ });
+ }
+ };
+
+ return Planner;
+});
+
+/**
+ * Handles email notification type for user notification settings.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Controller/User/Notification/Settings
+ */
+define('WoltLabSuite/Core/Controller/User/Notification/Settings',['Dictionary', 'Language', 'Dom/Traverse', 'Ui/SimpleDropdown'], function(Dictionary, Language, DomTraverse, UiSimpleDropdown) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ setup: function() {},
+ _initGroup: function() {},
+ _click: function() {},
+ _createDropdown: function() {},
+ _selectType: function() {}
+ };
+ return Fake;
+ }
+
+ var _data = new Dictionary();
+
+ var _callbackClick = null;
+ var _callbackSelectType = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Controller/User/Notification/Settings
+ */
+ var ControllerUserNotificationSettings = {
+ /**
+ * Binds event listeners for all notifications supporting emails.
+ */
+ setup: function() {
+ _callbackClick = this._click.bind(this);
+ _callbackSelectType = this._selectType.bind(this);
+
+ var group, mailSetting, groups = elBySelAll('#notificationSettings .flexibleButtonGroup');
+ for (var i = 0, length = groups.length; i < length; i++) {
+ group = groups[i];
+
+ mailSetting = elBySel('.notificationSettingsEmail', group);
+ if (mailSetting === null) {
+ continue;
+ }
+
+ this._initGroup(group, mailSetting);
+ }
+ },
+
+ /**
+ * Initializes a setting.
+ *
+ * @param {Element} group button group element
+ * @param {Element} mailSetting mail settings element
+ */
+ _initGroup: function(group, mailSetting) {
+ var groupId = ~~elData(group, 'object-id');
+
+ var disabledNotification = elById('settings_' + groupId + '_disabled');
+ disabledNotification.addEventListener(WCF_CLICK_EVENT, function() { mailSetting.classList.remove('active'); });
+ var enabledNotification = elById('settings_' + groupId + '_enabled');
+ enabledNotification.addEventListener(WCF_CLICK_EVENT, function() { mailSetting.classList.add('active'); });
+
+ var mailValue = DomTraverse.childByTag(mailSetting, 'INPUT');
+
+ var button = DomTraverse.childByTag(mailSetting, 'A');
+ elData(button, 'object-id', groupId);
+ button.addEventListener(WCF_CLICK_EVENT, _callbackClick);
+
+ _data.set(groupId, {
+ button: button,
+ dropdownMenu: null,
+ mailSetting: mailSetting,
+ mailValue: mailValue
+ });
+ },
+
+ /**
+ * Creates and displays the email type dropdown.
+ *
+ * @param {Object} event event object
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ var button = event.currentTarget;
+ var objectId = ~~elData(button, 'object-id');
+ var data = _data.get(objectId);
+ if (data.dropdownMenu === null) {
+ data.dropdownMenu = this._createDropdown(objectId, data.mailValue.value);
+
+ button.parentNode.classList.add('dropdown');
+ button.parentNode.appendChild(data.dropdownMenu);
+
+ UiSimpleDropdown.init(button, event);
+ }
+ else {
+ var items = DomTraverse.childrenByTag(data.dropdownMenu, 'LI'), value = data.mailValue.value;
+ for (var i = 0; i < 4; i++) {
+ items[i].classList[(elData(items[i], 'value') === value) ? 'add' : 'remove']('active');
+ }
+ }
+ },
+
+ /**
+ * Creates the email type dropdown.
+ *
+ * @param {int} objectId notification event id
+ * @param {string} initialValue initial email type
+ * @returns {Element} dropdown menu object
+ */
+ _createDropdown: function(objectId, initialValue) {
+ var dropdownMenu = elCreate('ul');
+ dropdownMenu.className = 'dropdownMenu';
+ elData(dropdownMenu, 'object-id', objectId);
+
+ var link, listItem, value, items = ['instant', 'daily', 'divider', 'none'];
+ for (var i = 0; i < 4; i++) {
+ value = items[i];
+
+ listItem = elCreate('li');
+ if (value === 'divider') {
+ listItem.className = 'dropdownDivider';
+ }
+ else {
+ link = elCreate('a');
+ link.textContent = Language.get('wcf.user.notification.mailNotificationType.' + value);
+ listItem.appendChild(link);
+ elData(listItem, 'value', value);
+ listItem.addEventListener(WCF_CLICK_EVENT, _callbackSelectType);
+
+ if (initialValue === value) {
+ listItem.className = 'active';
+ }
+ }
+
+ dropdownMenu.appendChild(listItem);
+ }
+
+ return dropdownMenu;
+ },
+
+ /**
+ * Sets the selected email notification type.
+ *
+ * @param {Object} event event object
+ */
+ _selectType: function(event) {
+ var value = elData(event.currentTarget, 'value');
+ var groupId = ~~elData(event.currentTarget.parentNode, 'object-id');
+
+ var data = _data.get(groupId);
+ data.mailValue.value = value;
+ elBySel('span.title', data.mailSetting).textContent = Language.get('wcf.user.notification.mailNotificationType.' + value);
+
+ data.button.classList[(value === 'none') ? 'remove' : 'add']('yellow');
+ data.button.classList[(value === 'none') ? 'remove' : 'add']('active');
+ }
+ };
+
+ return ControllerUserNotificationSettings;
+});
+
+/**
+ * Data handler for a captcha form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Captcha
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Captcha',['Core', './Field', 'WoltLabSuite/Core/Controller/Captcha'], function(Core, FormBuilderField, Captcha) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldCaptcha(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldCaptcha, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#getData
+ */
+ _getData: function() {
+ if (Captcha.has(this._fieldId)) {
+ return Captcha.getData(this._fieldId);
+ }
+
+ return {};
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ // does nothing
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#destroy
+ */
+ destroy: function() {
+ if (Captcha.has(this._fieldId)) {
+ Captcha.delete(this._fieldId);
+ }
+ }
+ });
+
+ return FormBuilderFieldCaptcha;
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form represented by checkboxes.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Checkboxes
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Checkboxes',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldCheckboxes(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldCheckboxes, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = [];
+
+ for (var i = 0, length = this._fields.length; i < length; i++) {
+ if (this._fields[i].checked) {
+ data[this._fieldId].push(this._fields[i].value);
+ }
+ }
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ this._fields = elBySelAll('input[name="' + this._fieldId + '[]"]');
+ }
+ });
+
+ return FormBuilderFieldCheckboxes;
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form that stores its value via a checkbox being
+ * checked or not.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Checked
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Checked',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldInput(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldInput, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = ~~this._field.checked;
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldInput;
+});
+
+/**
+ * Data handler for a date form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Date
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Date',['Core', 'WoltLabSuite/Core/Date/Picker', './Field'], function(Core, DatePicker, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldDate(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldDate, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = DatePicker.getValue(this._field);
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldDate;
+});
+
+/**
+ * Data handler for an item list form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/ItemList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/ItemList',['Core', './Field', 'WoltLabSuite/Core/Ui/ItemList/Static'], function(Core, FormBuilderField, UiItemListStatic) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldItemList(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldItemList, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+ data[this._fieldId] = [];
+
+ var values = UiItemListStatic.getValues(this._fieldId);
+ for (var i = 0, length = values.length; i < length; i++) {
+ // TODO: data[this._fieldId] is an array but if code assumes object
+ if (values[i].objectId) {
+ data[this._fieldId][values[i].objectId] = values[i].value;
+ }
+ else {
+ data[this._fieldId].push(values[i].value);
+ }
+ }
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldItemList;
+});
+
+/**
+ * Data handler for a radio button form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/RadioButton
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/RadioButton',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldRadioButton(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldRadioButton, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#getData
+ */
+ _getData: function() {
+ var data = {};
+
+ for (var i = 0, length = this._fields.length; i < length; i++) {
+ if (this._fields[i].checked) {
+ data[this._fieldId] = this._fields[i].value;
+ break;
+ }
+ }
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ this._fields = elBySelAll('input[name=' + this._fieldId + ']');
+ },
+ });
+
+ return FormBuilderFieldRadioButton;
+});
+
+/**
+ * Data handler for a simple acl form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/SimpleAcl
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/SimpleAcl',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldSimpleAcl(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldSimpleAcl, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var groupIds = [];
+ elBySelAll('input[name="' + this._fieldId + '[group][]"]', undefined, function(input) {
+ groupIds.push(~~input.value);
+ });
+
+ var usersIds = [];
+ elBySelAll('input[name="' + this._fieldId + '[user][]"]', undefined, function(input) {
+ usersIds.push(~~input.value);
+ });
+
+ var data = {};
+
+ data[this._fieldId] = {
+ group: groupIds,
+ user: usersIds
+ };
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_readField
+ */
+ _readField: function() {
+ // does nothing
+ }
+ });
+
+ return FormBuilderFieldSimpleAcl;
+});
+
+/**
+ * Data handler for a tag form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Tag
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Tag',['Core', './Field', 'WoltLabSuite/Core/Ui/ItemList'], function(Core, FormBuilderField, UiItemList) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldTag(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldTag, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+ data[this._fieldId] = [];
+
+ var values = UiItemList.getValues(this._fieldId);
+ for (var i = 0, length = values.length; i < length; i++) {
+ data[this._fieldId].push(values[i].value);
+ }
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldTag;
+});
+
+/**
+ * Data handler for a user form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/User
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/User',['Core', './Field', 'WoltLabSuite/Core/Ui/ItemList'], function(Core, FormBuilderField, UiItemList) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldUser(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldUser, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var values = UiItemList.getValues(this._fieldId);
+ var usernames = [];
+ for (var i = 0, length = values.length; i < length; i++) {
+ if (values[i].objectId) {
+ usernames.push(values[i].value);
+ }
+ }
+
+ var data = {};
+ data[this._fieldId] = usernames.join(',');
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldUser;
+});
+
+/**
+ * Data handler for a form builder field in an Ajax form that stores its value in an input's value
+ * attribute.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Value
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Value',['Core', './Field'], function(Core, FormBuilderField) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldValue(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldValue, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ data[this._fieldId] = this._field.value;
+
+ return data;
+ }
+ });
+
+ return FormBuilderFieldValue;
+});
+
+/**
+ * Data handler for an i18n form builder field in an Ajax form that stores its value in an input's
+ * value attribute.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/ValueI18n
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/ValueI18n',['Core', './Field', 'WoltLabSuite/Core/Language/Input'], function(Core, FormBuilderField, LanguageInput) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldValueI18n(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldValueI18n, FormBuilderField, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#_getData
+ */
+ _getData: function() {
+ var data = {};
+
+ var values = LanguageInput.getValues(this._fieldId);
+ if (values.size > 1) {
+ data[this._fieldId + '_i18n'] = values.toObject();
+ }
+ else {
+ data[this._fieldId] = values.get(0);
+ }
+
+ return data;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#destroy
+ */
+ destroy: function() {
+ LanguageInput.unregister(this._fieldId);
+ }
+ });
+
+ return FormBuilderFieldValueI18n;
+});
+
+/**
+ * Handles the comment response add feature.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Add
+ */
+define('WoltLabSuite/Core/Ui/Comment/Response/Add',[
+ 'Core', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Comment/Add'
+],
+function(
+ Core, Language, DomChangeListener, DomUtil, DomTraverse, UiNotification, UiCommentAdd
+) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ getContainer: function() {},
+ getContent: function() {},
+ setContent: function() {},
+ _submitGuestDialog: function() {},
+ _submit: function() {},
+ _getParameters: function () {},
+ _validate: function() {},
+ throwError: function() {},
+ _showLoadingOverlay: function() {},
+ _hideLoadingOverlay: function() {},
+ _reset: function() {},
+ _handleError: function() {},
+ _getEditor: function() {},
+ _insertMessage: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentResponseAdd(container, options) { this.init(container, options); }
+ Core.inherit(UiCommentResponseAdd, UiCommentAdd, {
+ init: function (container, options) {
+ UiCommentResponseAdd._super.prototype.init.call(this, container);
+
+ this._options = Core.extend({
+ callbackInsert: null
+ }, options);
+ },
+
+ /**
+ * Returns the editor container for placement or `null` if the editor is busy.
+ *
+ * @return {(Element|null)}
+ */
+ getContainer: function() {
+ return (this._isBusy) ? null : this._container;
+ },
+
+ /**
+ * Retrieves the current content from the editor.
+ *
+ * @return {string}
+ */
+ getContent: function () {
+ return window.jQuery(this._textarea).redactor('code.get');
+ },
+
+ /**
+ * Sets the content and places the caret at the end of the editor.
+ *
+ * @param {string} html
+ */
+ setContent: function (html) {
+ window.jQuery(this._textarea).redactor('code.set', html);
+ window.jQuery(this._textarea).redactor('WoltLabCaret.endOfEditor');
+
+ // the error message can appear anywhere in the container, not exclusively after the textarea
+ var innerError = elBySel('.innerError', this._textarea.parentNode);
+ if (innerError !== null) elRemove(innerError);
+
+ this._content.classList.remove('collapsed');
+ this._focusEditor();
+ },
+
+ _getParameters: function () {
+ var parameters = UiCommentResponseAdd._super.prototype._getParameters.call(this);
+ parameters.data.commentID = ~~elData(this._container.closest('.comment'), 'object-id');
+
+ return parameters;
+ },
+
+ _insertMessage: function(data) {
+ var commentContent = DomTraverse.childByClass(this._container.parentNode, 'commentContent');
+ var responseList = commentContent.nextElementSibling;
+ if (responseList === null || !responseList.classList.contains('commentResponseList')) {
+ responseList = elCreate('ul');
+ responseList.className = 'containerList commentResponseList';
+ elData(responseList, 'responses', 0);
+
+ commentContent.parentNode.insertBefore(responseList, commentContent.nextSibling);
+ }
+
+ // insert HTML
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.insertHtml(data.returnValues.template, responseList, 'append');
+
+ UiNotification.show(Language.get('wcf.global.success.add'));
+
+ DomChangeListener.trigger();
+
+ // reset editor
+ window.jQuery(this._textarea).redactor('code.set', '');
+
+ if (this._options.callbackInsert !== null) this._options.callbackInsert();
+
+ // update counter
+ elData(responseList, 'responses', responseList.children.length);
+
+ return responseList.lastElementChild;
+ },
+
+ _ajaxSetup: function() {
+ var data = UiCommentResponseAdd._super.prototype._ajaxSetup.call(this);
+ data.data.actionName = 'addResponse';
+
+ return data;
+ }
+ });
+
+ return UiCommentResponseAdd;
+});
+
+/**
+ * Provides editing support for comment responses.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Comment/Response/Edit
+ */
+define(
+ 'WoltLabSuite/Core/Ui/Comment/Response/Edit',[
+ 'Ajax', 'Core', 'Dictionary', 'Environment',
+ 'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse',
+ 'Dom/Util', 'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll', 'WoltLabSuite/Core/Ui/Comment/Edit'
+ ],
+ function(
+ Ajax, Core, Dictionary, Environment,
+ EventHandler, Language, List, DomChangeListener, DomTraverse,
+ DomUtil, UiNotification, UiReusableDropdown, UiScroll, UiCommentEdit
+ )
+{
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ init: function() {},
+ rebuild: function() {},
+ _click: function() {},
+ _prepare: function() {},
+ _showEditor: function() {},
+ _restoreMessage: function() {},
+ _save: function() {},
+ _validate: function() {},
+ throwError: function() {},
+ _showMessage: function() {},
+ _hideEditor: function() {},
+ _restoreEditor: function() {},
+ _destroyEditor: function() {},
+ _getEditorId: function() {},
+ _getObjectId: function() {},
+ _ajaxFailure: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {}
+ };
+ return Fake;
+ }
+
+ /**
+ * @constructor
+ */
+ function UiCommentResponseEdit(container) { this.init(container); }
+ Core.inherit(UiCommentResponseEdit, UiCommentEdit, {
+ /**
+ * Initializes the comment edit manager.
+ *
+ * @param {Element} container container element
+ */
+ init: function(container) {
+ this._activeElement = null;
+ this._callbackClick = null;
+ this._container = container;
+ this._editorContainer = null;
+ this._responses = new List();
+
+ this.rebuild();
+
+ DomChangeListener.add('Ui/Comment/Response/Edit_' + DomUtil.identify(this._container), this.rebuild.bind(this));
+ },
+
+ /**
+ * Initializes each applicable message, should be called whenever new
+ * messages are being displayed.
+ */
+ rebuild: function() {
+ elBySelAll('.commentResponse', this._container, (function (response) {
+ if (this._responses.has(response)) {
+ return;
+ }
+
+ if (elDataBool(response, 'can-edit')) {
+ var button = elBySel('.jsCommentResponseEditButton', response);
+ if (button !== null) {
+ if (this._callbackClick === null) {
+ this._callbackClick = this._click.bind(this);
+ }
+
+ button.addEventListener(WCF_CLICK_EVENT, this._callbackClick);
+ }
+ }
+
+ this._responses.add(response);
+ }).bind(this));
+ },
+
+ /**
+ * Handles clicks on the edit button.
+ *
+ * @param {?Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ if (this._activeElement === null) {
+ this._activeElement = event.currentTarget.closest('.commentResponse');
+
+ this._prepare();
+
+ Ajax.api(this, {
+ actionName: 'beginEdit',
+ objectIDs: [this._getObjectId(this._activeElement)]
+ });
+ }
+ else {
+ UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+ }
+ },
+
+ /**
+ * Prepares the message for editor display.
+ *
+ * @protected
+ */
+ _prepare: function() {
+ this._editorContainer = elCreate('div');
+ this._editorContainer.className = 'commentEditorContainer';
+ this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
+
+ var content = elBySel('.commentResponseContent', this._activeElement);
+ content.insertBefore(this._editorContainer, content.firstChild);
+ },
+
+ /**
+ * Shows the update message.
+ *
+ * @param {Object} data ajax response data
+ * @protected
+ */
+ _showMessage: function(data) {
+ // set new content
+ //noinspection JSCheckFunctionSignatures
+ DomUtil.setInnerHtml(elBySel('.commentResponseContent .userMessage', this._editorContainer.parentNode), data.returnValues.message);
+
+ this._restoreMessage();
+
+ UiNotification.show();
+ },
+
+ /**
+ * Returns the unique editor id.
+ *
+ * @return {string} editor id
+ * @protected
+ */
+ _getEditorId: function() {
+ return 'commentResponseEditor' + this._getObjectId(this._activeElement);
+ },
+
+ _ajaxSetup: function() {
+ var objectTypeId = ~~elData(this._container, 'object-type-id');
+
+ return {
+ data: {
+ className: 'wcf\\data\\comment\\response\\CommentResponseAction',
+ parameters: {
+ data: {
+ objectTypeID: objectTypeId
+ }
+ }
+ },
+ silent: true
+ };
+ }
+ });
+
+ return UiCommentResponseEdit;
+});
+
+/**
+ * Manages the sticky page header.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Header/Fixed
+ */
+define('WoltLabSuite/Core/Ui/Page/Header/Fixed',['Core', 'EventHandler', 'Ui/Alignment', 'Ui/CloseOverlay', 'Ui/SimpleDropdown', 'Ui/Screen'], function(Core, EventHandler, UiAlignment, UiCloseOverlay, UiSimpleDropdown, UiScreen) {
+ "use strict";
+
+ var _pageHeader, _pageHeaderContainer, _pageHeaderPanel, _pageHeaderSearch, _searchInput, _topMenu, _userPanelSearchButton;
+ var _isMobile = false;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Header/Fixed
+ */
+ return {
+ /**
+ * Initializes the sticky page header handler.
+ */
+ init: function() {
+ _pageHeader = elById('pageHeader');
+ _pageHeaderContainer = elById('pageHeaderContainer');
+
+ this._initSearchBar();
+
+ UiScreen.on('screen-md-down', {
+ match: function () { _isMobile = true; },
+ unmatch: function () { _isMobile = false; },
+ setup: function () { _isMobile = true; }
+ });
+
+ EventHandler.add('com.woltlab.wcf.Search', 'close', this._closeSearchBar.bind(this));
+ },
+
+ /**
+ * Provides the collapsible search bar.
+ *
+ * @protected
+ */
+ _initSearchBar: function() {
+ _pageHeaderSearch = elById('pageHeaderSearch');
+ _pageHeaderSearch.addEventListener(WCF_CLICK_EVENT, function(event) { event.stopPropagation(); });
+
+ _pageHeaderPanel = elById('pageHeaderPanel');
+ _searchInput = elById('pageHeaderSearchInput');
+ _topMenu = elById('topMenu');
+
+ _userPanelSearchButton = elById('userPanelSearchButton');
+ _userPanelSearchButton.addEventListener(WCF_CLICK_EVENT, (function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (_pageHeader.classList.contains('searchBarOpen')) {
+ this._closeSearchBar();
+ }
+ else {
+ this._openSearchBar();
+ }
+ }).bind(this));
+
+ UiCloseOverlay.add('WoltLabSuite/Core/Ui/Page/Header/Fixed', (function() {
+ if (_pageHeader.classList.contains('searchBarForceOpen')) return;
+
+ this._closeSearchBar();
+ }).bind(this));
+
+ EventHandler.add('com.woltlab.wcf.MainMenuMobile', 'more', (function(data) {
+ if (data.identifier === 'com.woltlab.wcf.search') {
+ data.handler.close(true);
+
+ Core.triggerEvent(_userPanelSearchButton, WCF_CLICK_EVENT);
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Opens the search bar.
+ *
+ * @protected
+ */
+ _openSearchBar: function() {
+ window.WCF.Dropdown.Interactive.Handler.closeAll();
+
+ _pageHeader.classList.add('searchBarOpen');
+ _userPanelSearchButton.parentNode.classList.add('open');
+
+ if (!_isMobile) {
+ // calculate value for `right` on desktop
+ UiAlignment.set(_pageHeaderSearch, _topMenu, {
+ horizontal: 'right'
+ });
+ }
+
+ _pageHeaderSearch.style.setProperty('top', _pageHeaderPanel.clientHeight + 'px', '');
+ _searchInput.focus();
+ window.setTimeout(function() {
+ _searchInput.selectionStart = _searchInput.selectionEnd = _searchInput.value.length;
+ }, 1);
+ },
+
+ /**
+ * Closes the search bar.
+ *
+ * @protected
+ */
+ _closeSearchBar: function () {
+ _pageHeader.classList.remove('searchBarOpen');
+ _userPanelSearchButton.parentNode.classList.remove('open');
+
+ ['bottom', 'left', 'right', 'top'].forEach(function(propertyName) {
+ _pageHeaderSearch.style.removeProperty(propertyName);
+ });
+
+ _searchInput.blur();
+
+ // close the scope selection
+ var scope = elBySel('.pageHeaderSearchType', _pageHeaderSearch);
+ UiSimpleDropdown.close(scope.id);
+ }
+ };
+});
+
+/**
+ * Suggestions for page object ids with external response data processing.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Search/Input
+ * @extends module:WoltLabSuite/Core/Ui/Search/Input
+ */
+define('WoltLabSuite/Core/Ui/Page/Search/Input',['Core', 'WoltLabSuite/Core/Ui/Search/Input'], function(Core, UiSearchInput) {
+ "use strict";
+
+ /**
+ * @param {Element} element input element
+ * @param {Object=} options search options and settings
+ * @constructor
+ */
+ function UiPageSearchInput(element, options) { this.init(element, options); }
+ Core.inherit(UiPageSearchInput, UiSearchInput, {
+ init: function(element, options) {
+ options = Core.extend({
+ ajax: {
+ className: 'wcf\\data\\page\\PageAction'
+ },
+ callbackSuccess: null
+ }, options);
+
+ if (typeof options.callbackSuccess !== 'function') {
+ throw new Error("Expected a valid callback function for 'callbackSuccess'.");
+ }
+
+ UiPageSearchInput._super.prototype.init.call(this, element, options);
+
+ this._pageId = 0;
+ },
+
+ /**
+ * Sets the target page id.
+ *
+ * @param {int} pageId target page id
+ */
+ setPageId: function(pageId) {
+ this._pageId = pageId;
+ },
+
+ _getParameters: function(value) {
+ var data = UiPageSearchInput._super.prototype._getParameters.call(this, value);
+
+ data.objectIDs = [this._pageId];
+
+ return data;
+ },
+
+ _ajaxSuccess: function(data) {
+ this._options.callbackSuccess(data);
+ }
+ });
+
+ return UiPageSearchInput;
+});
+
+/**
+ * Provides access to the lookup function of page handlers, allowing the user to search and
+ * select page object ids.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Search/Handler
+ */
+define('WoltLabSuite/Core/Ui/Page/Search/Handler',['Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog', './Input'], function(Language, StringUtil, DomUtil, UiDialog, UiPageSearchInput) {
+ "use strict";
+
+ var _callback = null;
+ var _searchInput = null;
+ var _searchInputLabel = null;
+ var _searchInputHandler = null;
+ var _resultList = null;
+ var _resultListContainer = null;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/Page/Search/Handler
+ */
+ return {
+ /**
+ * Opens the lookup overlay for provided page id.
+ *
+ * @param {int} pageId page id
+ * @param {string} title dialog title
+ * @param {function} callback callback function provided with the user-selected object id
+ * @param {string?} labelLanguageItem optional language item name for the search input label
+ */
+ open: function (pageId, title, callback, labelLanguageItem) {
+ _callback = callback;
+
+ UiDialog.open(this);
+ UiDialog.setTitle(this, title);
+
+ if (labelLanguageItem) {
+ _searchInputLabel.textContent = Language.get(labelLanguageItem);
+ }
+ else {
+ _searchInputLabel.textContent = Language.get('wcf.page.pageObjectID.search.terms');
+ }
+
+ this._getSearchInputHandler().setPageId(pageId);
+ },
+
+ /**
+ * Builds the result list.
+ *
+ * @param {Object} data AJAX response data
+ * @protected
+ */
+ _buildList: function(data) {
+ this._resetList();
+
+ // no matches
+ if (!Array.isArray(data.returnValues) || data.returnValues.length === 0) {
+ elInnerError(_searchInput, Language.get('wcf.page.pageObjectID.search.noResults'));
+
+ return;
+ }
+
+ var image, item, listItem;
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ item = data.returnValues[i];
+ image = item.image;
+ if (/^fa-/.test(image)) {
+ image = '<span class="icon icon48 ' + image + ' pointer jsTooltip" title="' + Language.get('wcf.global.select') + '"></span>';
+ }
+
+ listItem = elCreate('li');
+ elData(listItem, 'object-id', item.objectID);
+
+ listItem.innerHTML = '<div class="box48">'
+ + image
+ + '<div>'
+ + '<div class="containerHeadline">'
+ + '<h3><a href="' + StringUtil.escapeHTML(item.link) + '">' + StringUtil.escapeHTML(item.title) + '</a></h3>'
+ + (item.description ? '<p>' + item.description + '</p>' : '')
+ + '</div>'
+ + '</div>'
+ + '</div>';
+
+ listItem.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+
+ _resultList.appendChild(listItem);
+ }
+
+ elShow(_resultListContainer);
+ },
+
+ /**
+ * Resets the list and removes any error elements.
+ *
+ * @protected
+ */
+ _resetList: function() {
+ elInnerError(_searchInput, false);
+
+ _resultList.innerHTML = '';
+
+ elHide(_resultListContainer);
+ },
+
+ /**
+ * Initializes the search input handler and returns the instance.
+ *
+ * @returns {UiPageSearchInput} search input handler
+ * @protected
+ */
+ _getSearchInputHandler: function() {
+ if (_searchInputHandler === null) {
+ var callback = this._buildList.bind(this);
+ _searchInputHandler = new UiPageSearchInput(elById('wcfUiPageSearchInput'), {
+ callbackSuccess: callback
+ });
+ }
+
+ return _searchInputHandler;
+ },
+
+ /**
+ * Handles clicks on the item unless the click occurred directly on a link.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _click: function(event) {
+ if (event.target.nodeName === 'A') {
+ return;
+ }
+
+ event.stopPropagation();
+
+ _callback(elData(event.currentTarget, 'object-id'));
+ UiDialog.close(this);
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiPageSearchHandler',
+ options: {
+ onShow: function() {
+ if (_searchInput === null) {
+ _searchInput = elById('wcfUiPageSearchInput');
+ _searchInputLabel = _searchInput.parentNode.previousSibling.childNodes[0];
+ _resultList = elById('wcfUiPageSearchResultList');
+ _resultListContainer = elById('wcfUiPageSearchResultListContainer');
+ }
+
+ // clear search input
+ _searchInput.value = '';
+
+ // reset results
+ elHide(_resultListContainer);
+ _resultList.innerHTML = '';
+
+ _searchInput.focus();
+ },
+ title: ''
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiPageSearchInput">' + Language.get('wcf.page.pageObjectID.search.terms') + '</label></dt>'
+ + '<dd>'
+ + '<input type="text" id="wcfUiPageSearchInput" class="long">'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<section id="wcfUiPageSearchResultListContainer" class="section sectionContainerList">'
+ + '<header class="sectionHeader">'
+ + '<h2 class="sectionTitle">' + Language.get('wcf.page.pageObjectID.search.results') + '</h2>'
+ + '</header>'
+ + '<ul id="wcfUiPageSearchResultList" class="containerList wcfUiPageSearchResultList"></ul>'
+ + '</section>'
+ };
+ }
+ };
+});
+
+/**
+ * Handles the reaction list in the user profile.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Reaction/Profile/Loader
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Ui/Reaction/Profile/Loader',['Ajax', 'Core', 'Language'], function(Ajax, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiReactionProfileLoader(userID, firstReactionTypeID) { this.init(userID, firstReactionTypeID); }
+ UiReactionProfileLoader.prototype = {
+ /**
+ * Initializes a new ReactionListLoader object.
+ *
+ * @param integer userID
+ */
+ init: function(userID, firstReactionTypeID) {
+ this._container = elById('likeList');
+ this._userID = userID;
+ this._reactionTypeID = firstReactionTypeID;
+ this._targetType = 'received';
+ this._options = {
+ parameters: []
+ };
+
+ if (!this._userID) {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Profile/Loader] Invalid parameter 'userID' given.");
+ }
+
+ if (!this._reactionTypeID) {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Profile/Loader] Invalid parameter 'firstReactionTypeID' given.");
+ }
+
+ var loadButtonList = elCreate('li');
+ loadButtonList.className = 'likeListMore showMore';
+ this._noMoreEntries = elCreate('small');
+ this._noMoreEntries.innerHTML = Language.get('wcf.like.reaction.noMoreEntries');
+ this._noMoreEntries.style.display = 'none';
+ loadButtonList.appendChild(this._noMoreEntries);
+
+ this._loadButton = elCreate('button');
+ this._loadButton.className = 'small';
+ this._loadButton.innerHTML = Language.get('wcf.like.reaction.more');
+ this._loadButton.addEventListener(WCF_CLICK_EVENT, this._loadReactions.bind(this));
+ this._loadButton.style.display = 'none';
+ loadButtonList.appendChild(this._loadButton);
+ this._container.appendChild(loadButtonList);
+
+ if (elBySel('#likeList > li').length === 2) {
+ this._noMoreEntries.style.display = '';
+ }
+ else {
+ this._loadButton.style.display = '';
+ }
+
+ this._setupReactionTypeButtons();
+ this._setupTargetTypeButtons();
+ },
+
+ /**
+ * Set up the reaction type buttons.
+ */
+ _setupReactionTypeButtons: function() {
+ var element, elements = elBySelAll('#reactionType .button');
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ element.addEventListener(WCF_CLICK_EVENT, this._changeReactionTypeValue.bind(this, ~~elData(element, 'reaction-type-id')));
+ }
+ },
+
+ /**
+ * Set up the target type buttons.
+ */
+ _setupTargetTypeButtons: function() {
+ var element, elements = elBySelAll('#likeType .button');
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ element.addEventListener(WCF_CLICK_EVENT, this._changeTargetType.bind(this, elData(element, 'like-type')));
+ }
+ },
+
+ /**
+ * Changes the reaction target type (given or received) and reload the entire element.
+ *
+ * @param {string} targetType
+ */
+ _changeTargetType: function(targetType) {
+ if (targetType !== 'given' && targetType !== 'received') {
+ throw new Error("[WoltLabSuite/Core/Ui/Reaction/Profile/Loader] Invalid parameter 'targetType' given.");
+ }
+
+ if (targetType !== this._targetType) {
+ // remove old active state
+ elBySel('#likeType .button.active').classList.remove('active');
+
+ // add active status to new button
+ elBySel('#likeType .button[data-like-type="'+ targetType +'"]').classList.add('active');
+
+ this._targetType = targetType;
+ this._reload();
+ }
+ },
+
+ /**
+ * Changes the reaction type value and reload the entire element.
+ *
+ * @param {int} reactionTypeID
+ */
+ _changeReactionTypeValue: function(reactionTypeID) {
+ if (this._reactionTypeID !== reactionTypeID) {
+ // remove old active state
+ elBySel('#reactionType .button.active').classList.remove('active');
+
+ // add active status to new button
+ elBySel('#reactionType .button[data-reaction-type-id="'+ reactionTypeID +'"]').classList.add('active');
+
+ this._reactionTypeID = reactionTypeID;
+ this._reload();
+ }
+ },
+
+ /**
+ * Handles reload.
+ */
+ _reload: function() {
+ var elements = elBySelAll('#likeList > li:not(:first-child):not(:last-child)');
+
+ for (var i = 0, length = elements.length; i < length; i++) {
+ this._container.removeChild(elements[i]);
+ }
+
+ elData(this._container, 'last-like-time', 0);
+
+ this._loadReactions();
+ },
+
+ /**
+ * Load a list of reactions.
+ */
+ _loadReactions: function() {
+ this._options.parameters.userID = this._userID;
+ this._options.parameters.lastLikeTime = elData(this._container, 'last-like-time');
+ this._options.parameters.targetType = this._targetType;
+ this._options.parameters.reactionTypeID = this._reactionTypeID;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.template) {
+ elBySel('#likeList > li:nth-last-child(1)').insertAdjacentHTML('beforebegin', data.returnValues.template);
+
+ elData(this._container, 'last-like-time', data.returnValues.lastLikeTime);
+ this._noMoreEntries.style.display = 'none';
+ this._loadButton.style.display = '';
+ }
+ else {
+ this._noMoreEntries.style.display = '';
+ this._loadButton.style.display = 'none';
+ }
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'load',
+ className: '\\wcf\\data\\reaction\\ReactionAction'
+ }
+ };
+ }
+ };
+
+ return UiReactionProfileLoader;
+});
+
+define('WoltLabSuite/Core/Ui/User/Activity/Recent',['Ajax', 'Language', 'Dom/Util'], function(Ajax, Language, DomUtil) {
+ "use strict";
+
+ function UiUserActivityRecent(containerId) { this.init(containerId); }
+ UiUserActivityRecent.prototype = {
+ init: function (containerId) {
+ this._containerId = containerId;
+ var container = elById(this._containerId);
+ this._list = elBySel('.recentActivityList', container);
+
+ var showMoreItem = elCreate('li');
+ showMoreItem.className = 'showMore';
+ if (this._list.childElementCount) {
+ showMoreItem.innerHTML = '<button class="small">' + Language.get('wcf.user.recentActivity.more') + '</button>';
+ showMoreItem.children[0].addEventListener(WCF_CLICK_EVENT, this._showMore.bind(this));
+ }
+ else {
+ showMoreItem.innerHTML = '<small>' + Language.get('wcf.user.recentActivity.noMoreEntries') + '</small>';
+ }
+
+ this._list.appendChild(showMoreItem);
+ this._showMoreItem = showMoreItem;
+
+ elBySelAll('.jsRecentActivitySwitchContext .button', container, (function (button) {
+ button.addEventListener(WCF_CLICK_EVENT, (function (event) {
+ event.preventDefault();
+
+ if (!button.classList.contains('active')) {
+ this._switchContext();
+ }
+ }).bind(this));
+ }).bind(this));
+ },
+
+ _showMore: function (event) {
+ event.preventDefault();
+
+ this._showMoreItem.children[0].disabled = true;
+
+ Ajax.api(this, {
+ actionName: 'load',
+ parameters: {
+ boxID: ~~elData(this._list, 'box-id'),
+ filteredByFollowedUsers: elDataBool(this._list, 'filtered-by-followed-users'),
+ lastEventId: elData(this._list, 'last-event-id'),
+ lastEventTime: elData(this._list, 'last-event-time'),
+ userID: ~~elData(this._list, 'user-id')
+ }
+ });
+ },
+
+ _switchContext: function() {
+ Ajax.api(
+ this,
+ {
+ actionName: 'switchContext'
+ },
+ (function () {
+ window.location.hash = '#' + this._containerId;
+ window.location.reload();
+ }).bind(this)
+ );
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.template) {
+ DomUtil.insertHtml(data.returnValues.template, this._showMoreItem, 'before');
+
+ elData(this._list, 'last-event-time', data.returnValues.lastEventTime);
+ elData(this._list, 'last-event-id', data.returnValues.lastEventID);
+
+ this._showMoreItem.children[0].disabled = false;
+ }
+ else {
+ this._showMoreItem.innerHTML = '<small>' + Language.get('wcf.user.recentActivity.noMoreEntries') + '</small>';
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\activity\\event\\UserActivityEventAction'
+ }
+ };
+ }
+ };
+
+ return UiUserActivityRecent;
+});
+
+/**
+ * Deletes the current user cover photo.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/CoverPhoto/Delete
+ */
+define('WoltLabSuite/Core/Ui/User/CoverPhoto/Delete',['Ajax', 'EventHandler', 'Language', 'Ui/Confirmation', 'Ui/Notification'], function (Ajax, EventHandler, Language, UiConfirmation, UiNotification) {
+ "use strict";
+
+ var _button;
+ var _userId = 0;
+
+ /**
+ * @exports WoltLabSuite/Core/Ui/User/CoverPhoto/Delete
+ */
+ return {
+ /**
+ * Initializes the delete handler and enables the delete button on upload.
+ */
+ init: function (userId) {
+ _button = elBySel('.jsButtonDeleteCoverPhoto');
+ _button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ _userId = userId;
+
+ EventHandler.add('com.woltlab.wcf.user', 'coverPhoto', function (data) {
+ if (typeof data.url === 'string' && data.url.length > 0) {
+ elShow(_button.parentNode);
+ }
+ });
+ },
+
+ /**
+ * Handles clicks on the delete button.
+ *
+ * @param {Event} event
+ * @protected
+ */
+ _click: function (event) {
+ event.preventDefault();
+
+ UiConfirmation.show({
+ confirm: Ajax.api.bind(Ajax, this),
+ message: Language.get('wcf.user.coverPhoto.delete.confirmMessage')
+ });
+ },
+
+ _ajaxSuccess: function (data) {
+ elBySel('.userProfileCoverPhoto').style.setProperty('background-image', 'url(' + data.returnValues.url + ')', '');
+
+ elHide(_button.parentNode);
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ actionName: 'deleteCoverPhoto',
+ className: 'wcf\\data\\user\\UserProfileAction',
+ parameters: {
+ userID: _userId
+ }
+ }
+ };
+ }
+ };
+});
+
+/**
+ * Uploads the user cover photo via AJAX.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/CoverPhoto/Upload
+ */
+define('WoltLabSuite/Core/Ui/User/CoverPhoto/Upload',['Core', 'EventHandler', 'Upload', 'Ui/Notification', 'Ui/Dialog'], function(Core, EventHandler, Upload, UiNotification, UiDialog) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiUserCoverPhotoUpload(userId) {
+ Upload.call(this, 'coverPhotoUploadButtonContainer', 'coverPhotoUploadPreview', {
+ action: 'uploadCoverPhoto',
+ className: 'wcf\\data\\user\\UserProfileAction'
+ });
+
+ this._userId = userId;
+ }
+ Core.inherit(UiUserCoverPhotoUpload, Upload, {
+ _getParameters: function() {
+ return {
+ userID: this._userId
+ };
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Upload#_success
+ */
+ _success: function(uploadId, data) {
+ // remove or display the error message
+ elInnerError(this._button, data.returnValues.errorMessage);
+
+ // remove the upload progress
+ this._target.innerHTML = '';
+
+ if (data.returnValues.url) {
+ elBySel('.userProfileCoverPhoto').style.setProperty('background-image', 'url(' + data.returnValues.url + ')', '');
+
+ UiDialog.close('userProfileCoverPhotoUpload');
+ UiNotification.show();
+
+ EventHandler.fire('com.woltlab.wcf.user', 'coverPhoto', {
+ url: data.returnValues.url
+ });
+ }
+ }
+ });
+
+ return UiUserCoverPhotoUpload;
+});
+
+/**
+ * Handles the user trophy dialog.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Trophy/List
+ */
+define('WoltLabSuite/Core/Ui/User/Trophy/List',['Ajax', 'Core', 'Dictionary', 'Dom/Util', 'Ui/Dialog', 'WoltLabSuite/Core/Ui/Pagination', 'Dom/ChangeListener', 'List'], function(Ajax, Core, Dictionary, DomUtil, UiDialog, UiPagination, DomChangeListener, List) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function UiUserTrophyList() { this.init(); }
+ UiUserTrophyList.prototype = {
+ /**
+ * Initializes the user trophy list.
+ */
+ init: function() {
+ this._cache = new Dictionary();
+ this._knownElements = new List();
+
+ this._options = {
+ className: 'wcf\\data\\user\\trophy\\UserTrophyAction',
+ parameters: {}
+ };
+
+ this._rebuild();
+
+ DomChangeListener.add('WoltLabSuite/Core/Ui/User/Trophy/List', this._rebuild.bind(this));
+ },
+
+ /**
+ * Adds event userTrophyOverlayList elements.
+ */
+ _rebuild: function() {
+ elBySelAll('.userTrophyOverlayList', undefined, (function (element) {
+ if (!this._knownElements.has(element)) {
+ element.addEventListener(WCF_CLICK_EVENT, this._open.bind(this, elData(element, 'user-id')));
+
+ this._knownElements.add(element);
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Opens the user trophy list for a specific user.
+ *
+ * @param {int} userId
+ * @param {Event} event event object
+ */
+ _open: function(userId, event) {
+ event.preventDefault();
+
+ this._currentPageNo = 1;
+ this._currentUser = userId;
+ this._showPage();
+ },
+
+ /**
+ * Shows the current or given page.
+ *
+ * @param {int=} pageNo page number
+ */
+ _showPage: function(pageNo) {
+ if (pageNo !== undefined) {
+ this._currentPageNo = pageNo;
+ }
+
+ if (this._cache.has(this._currentUser)) {
+ // validate pageNo
+ if (this._cache.get(this._currentUser).get('pageCount') !== 0 && (this._currentPageNo < 1 || this._currentPageNo > this._cache.get(this._currentUser).get('pageCount'))) {
+ throw new RangeError("pageNo must be between 1 and " + this._cache.get(this._currentUser).get('pageCount') + " (" + this._currentPageNo + " given).");
+ }
+ }
+ else {
+ // init user page cache
+ this._cache.set(this._currentUser, new Dictionary());
+ }
+
+ if (this._cache.get(this._currentUser).has(this._currentPageNo)) {
+ var dialog = UiDialog.open(this, this._cache.get(this._currentUser).get(this._currentPageNo));
+ UiDialog.setTitle('userTrophyListOverlay', this._cache.get(this._currentUser).get('title'));
+
+ if (this._cache.get(this._currentUser).get('pageCount') > 1) {
+ var element = elBySel('.jsPagination', dialog.content);
+ if (element !== null) {
+ new UiPagination(element, {
+ activePage: this._currentPageNo,
+ maxPage: this._cache.get(this._currentUser).get('pageCount'),
+ callbackSwitch: this._showPage.bind(this)
+ });
+ }
+ }
+ }
+ else {
+ this._options.parameters.pageNo = this._currentPageNo;
+ this._options.parameters.userID = this._currentUser;
+
+ Ajax.api(this, {
+ parameters: this._options.parameters
+ });
+ }
+ },
+
+ _ajaxSuccess: function(data) {
+ if (data.returnValues.pageCount !== undefined) {
+ this._cache.get(this._currentUser).set('pageCount', ~~data.returnValues.pageCount);
+ }
+
+ this._cache.get(this._currentUser).set(this._currentPageNo, data.returnValues.template);
+ this._cache.get(this._currentUser).set('title', data.returnValues.title);
+ this._showPage();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ actionName: 'getGroupedUserTrophyList',
+ className: this._options.className
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'userTrophyListOverlay',
+ options: {
+ title: ""
+ },
+ source: null
+ };
+ }
+ };
+
+ return UiUserTrophyList;
+});
+
+/**
+ * Handles the JavaScript part of the label form field.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Controller/Label
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Controller/Label',['Core', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], function(Core, DomUtil, Language, UiSimpleDropdown) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldLabel(fielId, labelId, options) {
+ this.init(fielId, labelId, options);
+ };
+ FormBuilderFieldLabel.prototype = {
+ /**
+ * Initializes the label form field.
+ *
+ * @param {string} fieldId id of the relevant form builder field
+ * @param {integer} labelId id of the currently selected label
+ * @param {object} options additional label options
+ */
+ init: function(fieldId, labelId, options) {
+ this._formFieldContainer = elById(fieldId + 'Container');
+ this._labelChooser = elByClass('labelChooser', this._formFieldContainer)[0];
+ this._options = Core.extend({
+ forceSelection: false,
+ showWithoutSelection: false
+ }, options);
+
+ this._input = elCreate('input');
+ this._input.type = 'hidden';
+ this._input.id = fieldId;
+ this._input.name = fieldId;
+ this._input.value = ~~labelId;
+ this._formFieldContainer.appendChild(this._input);
+
+ var labelChooserId = DomUtil.identify(this._labelChooser);
+
+ // init dropdown
+ var dropdownMenu = UiSimpleDropdown.getDropdownMenu(labelChooserId);
+ if (dropdownMenu === null) {
+ UiSimpleDropdown.init(elByClass('dropdownToggle', this._labelChooser)[0]);
+ dropdownMenu = UiSimpleDropdown.getDropdownMenu(labelChooserId);
+ }
+
+ var additionalOptionList = null;
+ if (this._options.showWithoutSelection || !this._options.forceSelection) {
+ additionalOptionList = elCreate('ul');
+ dropdownMenu.appendChild(additionalOptionList);
+
+ var dropdownDivider = elCreate('li');
+ dropdownDivider.className = 'dropdownDivider';
+ additionalOptionList.appendChild(dropdownDivider);
+ }
+
+ if (this._options.showWithoutSelection) {
+ var listItem = elCreate('li');
+ elData(listItem, 'label-id', -1);
+ this._blockScroll(listItem);
+ additionalOptionList.appendChild(listItem);
+
+ var span = elCreate('span');
+ listItem.appendChild(span);
+
+ var label = elCreate('span');
+ label.className = 'badge label';
+ label.innerHTML = Language.get('wcf.label.withoutSelection');
+ span.appendChild(label);
+ }
+
+ if (!this._options.forceSelection) {
+ var listItem = elCreate('li');
+ elData(listItem, 'label-id', 0);
+ this._blockScroll(listItem);
+ additionalOptionList.appendChild(listItem);
+
+ var span = elCreate('span');
+ listItem.appendChild(span);
+
+ var label = elCreate('span');
+ label.className = 'badge label';
+ label.innerHTML = Language.get('wcf.label.none');
+ span.appendChild(label);
+ }
+
+ elBySelAll('li:not(.dropdownDivider)', dropdownMenu, function(listItem) {
+ listItem.addEventListener('click', this._click.bind(this));
+
+ if (labelId) {
+ if (~~elData(listItem, 'label-id') === labelId) {
+ this._selectLabel(listItem);
+ }
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Blocks page scrolling for the given element.
+ *
+ * @param {HTMLElement} element
+ */
+ _blockScroll: function(element) {
+ element.addEventListener(
+ 'wheel',
+ function(event) {
+ event.preventDefault();
+ },
+ {
+ passive: false
+ }
+ );
+ },
+
+ /**
+ * Select a label after clicking on it.
+ *
+ * @param {Event} event click event in label selection dropdown
+ */
+ _click: function(event) {
+ event.preventDefault();
+
+ this._selectLabel(event.currentTarget, false);
+ },
+
+ /**
+ * Selects the given label.
+ *
+ * @param {HTMLElement} label
+ */
+ _selectLabel: function(label) {
+ // save label
+ var labelId = elData(label, 'label-id');
+ if (!labelId) {
+ labelId = 0;
+ }
+
+ // replace button with currently selected label
+ var displayLabel = elBySel('span > span', label);
+ var button = elBySel('.dropdownToggle > span', this._labelChooser);
+ button.className = displayLabel.className;
+ button.textContent = displayLabel.textContent;
+
+ this._input.value = labelId;
+ }
+ };
+
+ return FormBuilderFieldLabel;
+});
+
+/**
+ * Handles the JavaScript part of the rating form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Controller/Rating
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Controller/Rating',['Dictionary', 'Environment'], function(Dictionary, Environment) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldRating(fieldId, value, activeCssClasses, defaultCssClasses) {
+ this.init(fieldId, value, activeCssClasses, defaultCssClasses);
+ };
+ FormBuilderFieldRating.prototype = {
+ /**
+ * Initializes the rating form field.
+ *
+ * @param {string} fieldId id of the relevant form builder field
+ * @param {integer} value current value of the field
+ * @param {string[]} activeCssClasses CSS classes for the active state of rating elements
+ * @param {string[]} defaultCssClasses CSS classes for the default state of rating elements
+ */
+ init: function(fieldId, value, activeCssClasses, defaultCssClasses) {
+ this._field = elBySel('#' + fieldId + 'Container');
+ if (this._field === null) {
+ throw new Error("Unknown field with id '" + fieldId + "'");
+ }
+
+ this._input = elCreate('input');
+ this._input.id = fieldId;
+ this._input.name = fieldId;
+ this._input.type = 'hidden';
+ this._input.value = value;
+ this._field.appendChild(this._input);
+
+ this._activeCssClasses = activeCssClasses;
+ this._defaultCssClasses = defaultCssClasses;
+
+ this._ratingElements = new Dictionary();
+
+ var ratingList = elBySel('.ratingList', this._field);
+ ratingList.addEventListener('mouseleave', this._restoreRating.bind(this));
+
+ elBySelAll('li', ratingList, function(listItem) {
+ if (listItem.classList.contains('ratingMetaButton')) {
+ listItem.addEventListener('click', this._metaButtonClick.bind(this));
+ listItem.addEventListener('mouseenter', this._restoreRating.bind(this));
+ }
+ else {
+ this._ratingElements.set(~~elData(listItem, 'rating'), listItem);
+
+ listItem.addEventListener('click', this._listItemClick.bind(this));
+ listItem.addEventListener('mouseenter', this._listItemMouseEnter.bind(this));
+ listItem.addEventListener('mouseleave', this._listItemMouseLeave.bind(this));
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Saves the rating associated with the clicked rating element.
+ *
+ * @param {Event} event rating element `click` event
+ */
+ _listItemClick: function(event) {
+ this._input.value = ~~elData(event.currentTarget, 'rating');
+
+ if (Environment.platform() !== 'desktop') {
+ this._restoreRating();
+ }
+ },
+
+ /**
+ * Updates the rating UI when hovering over a rating element.
+ *
+ * @param {Event} event rating element `mouseenter` event
+ */
+ _listItemMouseEnter: function(event) {
+ var currentRating = elData(event.currentTarget, 'rating');
+
+ this._ratingElements.forEach(function(ratingElement, rating) {
+ var icon = elByClass('icon', ratingElement)[0];
+
+ this._toggleIcon(icon, ~~rating <= ~~currentRating);
+ }.bind(this));
+ },
+
+ /**
+ * Updates the rating UI when leaving a rating element by changing all rating elements
+ * to their default state.
+ */
+ _listItemMouseLeave: function() {
+ this._ratingElements.forEach(function(ratingElement) {
+ var icon = elByClass('icon', ratingElement)[0];
+
+ this._toggleIcon(icon, false);
+ }.bind(this));
+ },
+
+ /**
+ * Handles clicks on meta buttons.
+ *
+ * @param {Event} event meta button `click` event
+ */
+ _metaButtonClick: function(event) {
+ if (elData(event.currentTarget, 'action') === 'removeRating') {
+ this._input.value = '';
+
+ this._listItemMouseLeave();
+ }
+ },
+
+ /**
+ * Updates the rating UI by changing the rating elements to the stored rating state.
+ */
+ _restoreRating: function() {
+ this._ratingElements.forEach(function(ratingElement, rating) {
+ var icon = elByClass('icon', ratingElement)[0];
+
+ this._toggleIcon(icon, ~~rating <= ~~this._input.value);
+ }.bind(this));
+ },
+
+ /**
+ * Toggles the state of the given icon based on the given state parameter.
+ *
+ * @param {HTMLElement} icon toggled icon
+ * @param {boolean} active is `true` if icon will be changed to `active` state, otherwise changed to `default` state
+ */
+ _toggleIcon: function(icon, active) {
+ active = active || false;
+
+ if (active) {
+ for (var i = 0; i < this._defaultCssClasses.length; i++) {
+ icon.classList.remove(this._defaultCssClasses[i]);
+ }
+
+ for (var i = 0; i < this._activeCssClasses.length; i++) {
+ icon.classList.add(this._activeCssClasses[i]);
+ }
+ }
+ else {
+ for (var i = 0; i < this._activeCssClasses.length; i++) {
+ icon.classList.remove(this._activeCssClasses[i]);
+ }
+
+ for (var i = 0; i < this._defaultCssClasses.length; i++) {
+ icon.classList.add(this._defaultCssClasses[i]);
+ }
+ }
+ }
+ };
+
+ return FormBuilderFieldRating;
+});
+
+/**
+ * Abstract implementation of a form field dependency.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract',['./Manager'], function(DependencyManager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Abstract(dependentElementId, fieldId) {
+ this.init(dependentElementId, fieldId);
+ };
+ Abstract.prototype = {
+ /**
+ * Checks if the dependency is met.
+ *
+ * @abstract
+ */
+ checkDependency: function() {
+ throw new Error("Missing implementation of WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract.checkDependency!");
+ },
+
+ /**
+ * Return the node whose availability depends on the value of a field.
+ *
+ * @return {HtmlElement} dependent node
+ */
+ getDependentNode: function() {
+ return this._dependentElement;
+ },
+
+ /**
+ * Returns the field the availability of the element dependents on.
+ *
+ * @return {HtmlElement} field controlling element availability
+ */
+ getField: function() {
+ return this._field;
+ },
+
+ /**
+ * Returns all fields requiring `change` event listeners for this
+ * dependency to be properly resolved.
+ *
+ * @return {HtmlElement[]} fields to register event listeners on
+ */
+ getFields: function() {
+ return this._fields;
+ },
+
+ /**
+ * Initializes the new dependency object.
+ *
+ * @param {string} dependentElementId id of the (container of the) dependent element
+ * @param {string} fieldId id of the field controlling element availability
+ *
+ * @throws {Error} if either depenent element id or field id are invalid
+ */
+ init: function(dependentElementId, fieldId) {
+ this._dependentElement = elById(dependentElementId);
+ if (this._dependentElement === null) {
+ throw new Error("Unknown dependent element with container id '" + dependentElementId + "Container'.");
+ }
+
+ this._field = elById(fieldId);
+ if (this._field === null) {
+ this._fields = [];
+ elBySelAll('input[type=radio][name=' + fieldId + ']', undefined, function(field) {
+ this._fields.push(field);
+ }.bind(this));
+
+ if (!this._fields.length) {
+ throw new Error("Unknown field with id '" + fieldId + "'.");
+ }
+ }
+ else {
+ this._fields = [this._field];
+
+ // handle special case of boolean form fields that have to form fields
+ if (this._field.tagName === 'INPUT' && this._field.type === 'radio' && elData(this._field, 'no-input-id') !== '') {
+ this._noField = elById(elData(this._field, 'no-input-id'));
+ if (this._noField === null) {
+ throw new Error("Cannot find 'no' input field for input field '" + fieldId + "'");
+ }
+
+ this._fields.push(this._noField);
+ }
+ }
+
+ DependencyManager.addDependency(this);
+ }
+ };
+
+ return Abstract;
+});
+
+/**
+ * Form field dependency implementation that requires the value of a field not to be empty.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/NonEmpty
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/NonEmpty',['./Abstract', 'Core'], function(Abstract, Core) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function NonEmpty(dependentElementId, fieldId) {
+ this.init(dependentElementId, fieldId);
+ };
+ Core.inherit(NonEmpty, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract#checkDependency
+ */
+ checkDependency: function() {
+ switch (this._field.tagName) {
+ case 'INPUT':
+ switch (this._field.type) {
+ case 'checkbox':
+ // TODO: check if working
+ return this._field.checked;
+
+ case 'radio':
+ if (this._noField && this._noField.checked) {
+ return false;
+ }
+
+ return this._field.checked;
+
+ default:
+ return this._field.value.trim().length !== 0;
+ }
+
+ case 'SELECT':
+ // TODO: check if working for multiselect
+ return this._field.value.length !== 0;
+
+ case 'TEXTAREA':
+ // TODO: check if working
+ return this._field.value.trim().length !== 0;
+ }
+ }
+ });
+
+ return NonEmpty;
+});
+
+/**
+ * Form field dependency implementation that requires a field to have a certain value.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Value
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Value',['./Abstract', 'Core', './Manager'], function(Abstract, Core, Manager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Value(dependentElementId, fieldId, isNegated) {
+ this.init(dependentElementId, fieldId);
+
+ this._isNegated = false;
+ };
+ Core.inherit(Value, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract#checkDependency
+ */
+ checkDependency: function() {
+ if (!this._values) {
+ throw new Error("Values have not been set.");
+ }
+
+ var value;
+ if (this._field) {
+ if (Manager.isHiddenByDependencies(this._field)) {
+ return false;
+ }
+
+ value = this._field.value;
+ }
+ else {
+ for (var i = 0, length = this._fields.length, field; i < length; i++) {
+ field = this._fields[i];
+
+ if (field.checked) {
+ if (Manager.isHiddenByDependencies(field)) {
+ return false;
+ }
+
+ value = field.value;
+
+ break;
+ }
+ }
+ }
+
+ // do not use `Array.prototype.indexOf()` as we use a weak comparision
+ for (var i = 0, length = this._values.length; i < length; i++) {
+ if (this._values[i] == value) {
+ if (this._isNegated) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ if (this._isNegated) {
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Sets if the field value may not have any of the set values.
+ *
+ * @param {bool} negate
+ * @return {WoltLabSuite/Core/Form/Builder/Field/Dependency/Value}
+ */
+ negate: function(negate) {
+ this._isNegated = negate;
+
+ return this;
+ },
+
+ /**
+ * Sets the possible values the field may have for the dependency to be met.
+ *
+ * @param {array} values
+ * @return {WoltLabSuite/Core/Form/Builder/Field/Dependency/Value}
+ */
+ values: function(values) {
+ this._values = values;
+
+ return this;
+ }
+ });
+
+ return Value;
+});
+
+/**
+ * Data handler for a content language form builder field in an Ajax form.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Language/ContentLanguage
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Language/ContentLanguage',['Core', 'WoltLabSuite/Core/Language/Chooser', '../Value'], function(Core, LanguageChooser, FormBuilderFieldValue) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function FormBuilderFieldContentLanguage(fieldId) {
+ this.init(fieldId);
+ };
+ Core.inherit(FormBuilderFieldContentLanguage, FormBuilderFieldValue, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Field#destroy
+ */
+ destroy: function() {
+ LanguageChooser.removeChooser(this._fieldId);
+ }
+ });
+
+ return FormBuilderFieldContentLanguage;
+});
+
+/**
+ * Abstract implementation of a handler for the visibility of container due the dependencies
+ * of its children.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Abstract',['EventHandler', '../Manager'], function(EventHandler, DependencyManager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Abstract(containerId) {
+ this.init(containerId);
+ };
+ Abstract.prototype = {
+ /**
+ * Checks if the container should be visible and shows or hides it accordingly.
+ *
+ * @abstract
+ */
+ checkContainer: function() {
+ throw new Error("Missing implementation of WoltLabSuite/Core/Form/Builder/Field/Dependency/Container.checkContainer!");
+ },
+
+ /**
+ * Initializes a new container dependency handler for the container with the given
+ * id.
+ *
+ * @param {string} containerId id of the handled container
+ *
+ * @throws {TypeError} if container id is no string
+ * @throws {Error} if container id is invalid
+ */
+ init: function(containerId) {
+ if (typeof containerId !== 'string') {
+ throw new TypeError("Container id has to be a string.");
+ }
+
+ this._container = elById(containerId);
+ if (this._container === null) {
+ throw new Error("Unknown container with id '" + containerId + "'.");
+ }
+
+ DependencyManager.addContainerCheckCallback(this.checkContainer.bind(this));
+ }
+ };
+
+ return Abstract
+});
+
+/**
+ * Default implementation for a container visibility handler due to the dependencies of its
+ * children that only considers the visibility of all of its children.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default',['./Abstract', 'Core', '../Manager'], function(Abstract, Core, DependencyManager) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Default(containerId) {
+ this.init(containerId);
+ };
+ Core.inherit(Default, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default#checkContainer
+ */
+ checkContainer: function() {
+ if (elDataBool(this._container, 'ignore-dependencies')) {
+ return;
+ }
+
+ // only consider containers that have not been hidden by their own dependencies
+ if (DependencyManager.isHiddenByDependencies(this._container)) {
+ return;
+ }
+
+ var containerIsVisible = !elIsHidden(this._container);
+ var containerShouldBeVisible = false;
+
+ var children = this._container.children;
+ var start = 0;
+ // ignore container header for visibility considerations
+ if (this._container.children.item(0).tagName === 'H2' || this._container.children.item(0).tagName === 'HEADER') {
+ var start = 1;
+ }
+
+ for (var i = start, length = children.length; i < length; i++) {
+ if (!elIsHidden(children.item(i))) {
+ containerShouldBeVisible = true;
+ break;
+ }
+ }
+
+ if (containerIsVisible !== containerShouldBeVisible) {
+ if (containerShouldBeVisible) {
+ elShow(this._container);
+ }
+ else {
+ elHide(this._container);
+ }
+
+ // check containers again to make sure parent containers can react to
+ // changing the visibility of this container
+ DependencyManager.checkContainers();
+ }
+ }
+ });
+
+ return Default;
+});
+
+/**
+ * Container visibility handler implementation for a tab menu tab that, in addition to the
+ * tab itself, also handles the visibility of the tab menu list item.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Tab
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Tab',['./Abstract', 'Core', 'Dom/Util', '../Manager', 'Ui/TabMenu'], function(Abstract, Core, DomUtil, DependencyManager, UiTabMenu) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function Tab(containerId) {
+ this.init(containerId);
+ };
+ Core.inherit(Tab, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default#checkContainer
+ */
+ checkContainer: function() {
+ // only consider containers that have not been hidden by their own dependencies
+ if (DependencyManager.isHiddenByDependencies(this._container)) {
+ return;
+ }
+
+ var containerIsVisible = !elIsHidden(this._container);
+ var containerShouldBeVisible = false;
+
+ var children = this._container.children;
+ for (var i = 0, length = children.length; i < length; i++) {
+ if (!elIsHidden(children.item(i))) {
+ containerShouldBeVisible = true;
+ break;
+ }
+ }
+
+ if (containerIsVisible !== containerShouldBeVisible) {
+ var tabMenuListItem = elBySel('#' + DomUtil.identify(this._container.parentNode) + ' > nav > ul > li[data-name=' + this._container.id + ']', this._container.parentNode.parentNode);
+ if (tabMenuListItem === null) {
+ throw new Error("Cannot find tab menu entry for tab '" + this._container.id + "'.");
+ }
+
+ if (containerShouldBeVisible) {
+ elShow(this._container);
+ elShow(tabMenuListItem);
+ }
+ else {
+ elHide(this._container);
+ elHide(tabMenuListItem);
+
+ var tabMenu = UiTabMenu.getTabMenu(DomUtil.identify(tabMenuListItem.closest('.tabMenuContainer')));
+
+ // check if currently active tab will be hidden
+ if (tabMenu.getActiveTab() === tabMenuListItem) {
+ tabMenu.selectFirstVisible();
+ }
+ }
+
+ // check containers again to make sure parent containers can react to
+ // changing the visibility of this container
+ DependencyManager.checkContainers();
+ }
+ }
+ });
+
+ return Tab;
+});
+
+/**
+ * Container visibility handler implementation for a tab menu that checks visibility
+ * based on the visibility of its tab menu list items.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/TabMenu
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Dependency/Abstract
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/TabMenu',['./Abstract', 'Core', 'Dom/Util', '../Manager', 'Ui/TabMenu'], function(Abstract, Core, DomUtil, DependencyManager, UiTabMenu) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function TabMenu(containerId) {
+ this.init(containerId);
+ };
+ Core.inherit(TabMenu, Abstract, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default#checkContainer
+ */
+ checkContainer: function() {
+ // only consider containers that have not been hidden by their own dependencies
+ if (DependencyManager.isHiddenByDependencies(this._container)) {
+ return;
+ }
+
+ var containerIsVisible = !elIsHidden(this._container);
+ var containerShouldBeVisible = false;
+
+ var tabMenuListItems = elBySelAll('#' + DomUtil.identify(this._container) + ' > nav > ul > li', this._container.parentNode);
+ for (var i = 0, length = tabMenuListItems.length; i < length; i++) {
+ if (!elIsHidden(tabMenuListItems[i])) {
+ containerShouldBeVisible = true;
+ break;
+ }
+ }
+
+ if (containerIsVisible !== containerShouldBeVisible) {
+ if (containerShouldBeVisible) {
+ elShow(this._container);
+
+ UiTabMenu.getTabMenu(DomUtil.identify(this._container)).selectFirstVisible();
+ }
+ else {
+ elHide(this._container);
+ }
+
+ // check containers again to make sure parent containers can react to
+ // changing the visibility of this container
+ DependencyManager.checkContainers();
+ }
+ }
+ });
+
+ return TabMenu;
+});
+
+/**
+ * Abstract implementation of the JavaScript component of a form field handling
+ * a list of packages.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList',['Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language'], function(DomChangeListener, DomTraverse, DomUtil, EventKey, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function AbstractPackageList(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ AbstractPackageList.prototype = {
+ /**
+ * Initializes the package list handler.
+ *
+ * @param {string} formFieldId id of the associated form field
+ * @param {object[]} existingPackages data of existing packages
+ */
+ init: function(formFieldId, existingPackages) {
+ this._formFieldId = formFieldId;
+
+ this._packageList = elById(this._formFieldId + '_packageList');
+ if (this._packageList === null) {
+ throw new Error("Cannot find package list for packages field with id '" + this._formFieldId + "'.");
+ }
+
+ this._packageIdentifier = elById(this._formFieldId + '_packageIdentifier');
+ if (this._packageIdentifier === null) {
+ throw new Error("Cannot find package identifier form field for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._packageIdentifier.addEventListener('keypress', this._keyPress.bind(this));
+
+ this._addButton = elById(this._formFieldId + '_addButton');
+ if (this._addButton === null) {
+ throw new Error("Cannot find add button for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._addButton.addEventListener('click', this._addPackage.bind(this));
+
+ this._form = this._packageList.closest('form');
+ if (this._form === null) {
+ throw new Error("Cannot find form element for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._form.addEventListener('submit', this._submit.bind(this));
+
+ existingPackages.forEach(this._addPackageByData.bind(this));
+ },
+
+ /**
+ * Adds a package to the package list as a consequence of the given
+ * event. If the package data is invalid, an error message is shown
+ * and no package is added.
+ *
+ * @param {Event} event event that triggered trying to add the package
+ */
+ _addPackage: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // validate data
+ if (!this._validateInput()) {
+ return;
+ }
+
+ this._addPackageByData(this._getInputData());
+
+ // empty fields
+ this._emptyInput();
+
+ this._packageIdentifier.focus();
+ },
+
+ /**
+ * Adds a package to the package list using the given package data.
+ *
+ * @param {object} packageData
+ */
+ _addPackageByData: function(packageData) {
+ // add package to list
+ var listItem = elCreate('li');
+ this._populateListItem(listItem, packageData);
+
+ // add delete button
+ var deleteButton = elCreate('span');
+ deleteButton.className = 'icon icon16 fa-times pointer jsTooltip';
+ elAttr(deleteButton, 'title', Language.get('wcf.global.button.delete'));
+ deleteButton.addEventListener('click', this._removePackage.bind(this));
+ DomUtil.prepend(deleteButton, listItem);
+
+ this._packageList.appendChild(listItem);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Creates the hidden fields when the form is submitted.
+ *
+ * @param {HTMLElement} listElement package list element from the package list
+ * @param {int} index package index
+ */
+ _createSubmitFields: function(listElement, index) {
+ var packageIdentifier = elCreate('input');
+ elAttr(packageIdentifier, 'type', 'hidden');
+ elAttr(packageIdentifier, 'name', this._formFieldId + '[' + index + '][packageIdentifier]')
+ packageIdentifier.value = elData(listElement, 'package-identifier');
+ this._form.appendChild(packageIdentifier);
+ },
+
+ /**
+ * Empties the input fields.
+ */
+ _emptyInput() {
+ this._packageIdentifier.value = '';
+ },
+
+ /**
+ * Returns the error element for the given form field element.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getErrorElement: function(element, createIfNoNExistent) {
+ var error = DomTraverse.nextByClass(element, 'innerError');
+
+ if (error === null && createIfNoNExistent) {
+ error = elCreate('small');
+ error.className = 'innerError';
+
+ DomUtil.insertAfter(error, element);
+ }
+
+ return error;
+ },
+
+ /**
+ * Returns the current data of the input fields to add a new package.
+ *
+ * @return {object}
+ */
+ _getInputData: function() {
+ return {
+ packageIdentifier: this._packageIdentifier.value
+ };
+ },
+
+ /**
+ * Returns the error element for the package identifier form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getPackageIdentifierErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._packageIdentifier, createIfNonExistent);
+ },
+
+ /**
+ * Adds a package to the package list after pressing ENTER in a
+ * text field.
+ *
+ * @param {Event} event
+ */
+ _keyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ this._addPackage(event);
+ }
+ },
+
+ /**
+ * Adds all necessary package-relavant data to the given list item.
+ *
+ * @param {HTMLElement} listItem package list element holding package data
+ * @param {object} packageData package data
+ */
+ _populateListItem(listItem, packageData) {
+ elData(listItem, 'package-identifier', packageData.packageIdentifier);
+ },
+
+ /**
+ * Removes a package by clicking on its delete button.
+ *
+ * @param {Event} event delete button click event
+ */
+ _removePackage: function(event) {
+ elRemove(event.currentTarget.closest('li'));
+
+ // remove field errors if the last package has been deleted
+ if (
+ !this._packageList.childElementCount &&
+ this._packageList.nextElementSibling.tagName === 'SMALL' &&
+ this._packageList.nextElementSibling.classList.contains('innerError')
+ ) {
+ elRemove(this._packageList.nextElementSibling);
+ }
+ },
+
+ /**
+ * Adds all necessary (hidden) form fields to the form when
+ * submitting the form.
+ */
+ _submit: function() {
+ DomTraverse.childrenByTag(this._packageList, 'LI').forEach(this._createSubmitFields.bind(this));
+ },
+
+ /**
+ * Returns `true` if the currently entered package data is valid.
+ * Otherwise `false` is returned and relevant error messages are
+ * shown.
+ *
+ * @return {boolean}
+ */
+ _validateInput: function() {
+ return this._validatePackageIdentifier();
+ },
+
+ /**
+ * Returns `true` if the currently entered package identifier is
+ * valid. Otherwise `false` is returned and an error message is
+ * shown.
+ *
+ * @return {boolean}
+ */
+ _validatePackageIdentifier: function() {
+ var packageIdentifier = this._packageIdentifier.value;
+
+ if (packageIdentifier === '') {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.global.form.error.empty');
+
+ return false;
+ }
+
+ if (packageIdentifier.length < 3) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.minimumLength');
+
+ return false;
+ }
+ else if (packageIdentifier.length > 191) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.maximumLength');
+
+ return false;
+ }
+
+ // see `wcf\data\package\Package::isValidPackageName()`
+ if (!packageIdentifier.match(/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/)) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.format');
+
+ return false;
+ }
+
+ // check if package has already been added
+ var duplicate = false;
+ DomTraverse.childrenByTag(this._packageList, 'LI').forEach(function(listItem, index) {
+ if (elData(listItem, 'package-identifier') === packageIdentifier) {
+ duplicate = true;
+ }
+ });
+
+ if (duplicate) {
+ this._getPackageIdentifierErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.packageIdentifier.error.duplicate');
+
+ return false;
+ }
+
+ // remove outdated errors
+ var error = this._getPackageIdentifierErrorElement();
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ },
+
+ /**
+ * Returns `true` if the given version is valid. Otherwise `false`
+ * is returned and an error message is shown.
+ *
+ * @param {string} version validated version
+ * @param {function} versionErrorGetter returns the version error element
+ * @return {boolean}
+ */
+ _validateVersion: function(version, versionErrorGetter) {
+ // see `wcf\data\package\Package::isValidVersion()`
+ // the version is no a required attribute
+ if (version !== '') {
+ if (version.length > 255) {
+ versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.maximumLength');
+
+ return false;
+ }
+
+ // see `wcf\data\package\Package::isValidVersion()`
+ if (!version.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
+ versionErrorGetter(true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
+
+ return false;
+ }
+ }
+
+ // remove outdated errors
+ var error = versionErrorGetter();
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ }
+ };
+
+ return AbstractPackageList;
+});
+
+/**
+ * Manages the packages entered in a devtools project excluded package form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/ExcludedPackages
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/ExcludedPackages',['./AbstractPackageList', 'Core', 'Language'], function(AbstractPackageList, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function ExcludedPackages(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ Core.inherit(ExcludedPackages, AbstractPackageList, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#init
+ */
+ init: function(formFieldId, existingPackages) {
+ ExcludedPackages._super.prototype.init.call(this, formFieldId, existingPackages);
+
+ this._version = elById(this._formFieldId + '_version');
+ if (this._version === null) {
+ throw new Error("Cannot find version form field for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._version.addEventListener('keypress', this._keyPress.bind(this));
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_createSubmitFields
+ */
+ _createSubmitFields: function(listElement, index) {
+ ExcludedPackages._super.prototype._createSubmitFields.call(this, listElement, index);
+
+ var version = elCreate('input');
+ elAttr(version, 'type', 'hidden');
+ elAttr(version, 'name', this._formFieldId + '[' + index + '][version]')
+ version.value = elData(listElement, 'version');
+ this._form.appendChild(version);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_emptyInput
+ */
+ _emptyInput() {
+ ExcludedPackages._super.prototype._emptyInput.call(this);
+
+ this._version.value = '';
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_getInputData
+ */
+ _getInputData: function() {
+ return Core.extend(ExcludedPackages._super.prototype._getInputData.call(this), {
+ version: this._version.value
+ });
+ },
+
+ /**
+ * Returns the error element for the version form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getVersionErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._version, createIfNonExistent);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_populateListItem
+ */
+ _populateListItem(listItem, packageData) {
+ ExcludedPackages._super.prototype._populateListItem.call(this, listItem, packageData);
+
+ elData(listItem, 'version', packageData.version);
+ listItem.innerHTML = ' ' + Language.get('wcf.acp.devtools.project.excludedPackage.excludedPackage', {
+ packageIdentifier: packageData.packageIdentifier,
+ version: packageData.version
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_validateInput
+ */
+ _validateInput: function() {
+ return ExcludedPackages._super.prototype._validateInput.call(this) && this._validateVersion(
+ this._version.value,
+ this._getVersionErrorElement.bind(this)
+ );
+ }
+ });
+
+ return ExcludedPackages;
+});
+
+/**
+ * Manages the instructions entered in a devtools project instructions form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/Instructions
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/Instructions',[
+ 'Dom/ChangeListener',
+ 'Dom/Traverse',
+ 'Dom/Util',
+ 'EventKey',
+ 'Language',
+ 'Ui/Confirmation',
+ 'Ui/Dialog',
+ 'WoltLabSuite/Core/Ui/Sortable/List'
+], function(
+ DomChangeListener,
+ DomTraverse,
+ DomUtil,
+ EventKey,
+ Language,
+ UiConfirmation,
+ UiDialog,
+ UiSortableList
+) {
+ "use strict";
+
+ var _applicationPips = ['acpTemplate', 'file', 'script', 'template'];
+
+ /**
+ * @constructor
+ */
+ function Instructions(
+ formFieldId,
+ instructionsTemplate,
+ instructionsEditDialogTemplate,
+ instructionEditDialogTemplate,
+ pipDefaultFilenames,
+ existingInstructions
+ ) {
+ this.init(
+ formFieldId,
+ instructionsTemplate,
+ instructionsEditDialogTemplate,
+ instructionEditDialogTemplate,
+ pipDefaultFilenames,
+ existingInstructions || []
+ );
+ };
+ Instructions.prototype = {
+ /**
+ * Initializes the instructions handler.
+ *
+ * @param {string} formFieldId id of the associated form field
+ * @param {Template} instructionsTemplate template used for a new set of instructions
+ * @param {Template} instructionsEditDialogTemplate template used for instructions edit dialogs
+ * @param {Template} instructionEditDialogTemplate template used for instruction edit dialogs
+ * @param {object} pipDefaultFilenames maps pip names to their default filenames
+ * @param {object[]} existingInstructions data of existing instructions
+ */
+ init: function(
+ formFieldId,
+ instructionsTemplate,
+ instructionsEditDialogTemplate,
+ instructionEditDialogTemplate,
+ pipDefaultFilenames,
+ existingInstructions
+ ) {
+ this._formFieldId = formFieldId;
+ this._instructionsTemplate = instructionsTemplate;
+ this._instructionsEditDialogTemplate = instructionsEditDialogTemplate;
+ this._instructionEditDialogTemplate = instructionEditDialogTemplate;
+ this._instructionsCounter = 0;
+ this._pipDefaultFilenames = pipDefaultFilenames;
+ this._instructionCounter = 0;
+
+ this._instructionsList = elById(this._formFieldId + '_instructionsList');
+ if (this._instructionsList === null) {
+ throw new Error("Cannot find package list for packages field with id '" + this._formFieldId + "'.");
+ }
+
+ this._instructionsType = elById(this._formFieldId + '_instructionsType');
+ if (this._instructionsType === null) {
+ throw new Error("Cannot find instruction type form field for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._instructionsType.addEventListener('change', this._toggleFromVersionFormField.bind(this));
+
+ this._fromVersion = elById(this._formFieldId + '_fromVersion');
+ if (this._fromVersion === null) {
+ throw new Error("Cannot find from version form field for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._fromVersion.addEventListener('keypress', this._instructionsKeyPress.bind(this));
+
+ this._addButton = elById(this._formFieldId + '_addButton');
+ if (this._addButton === null) {
+ throw new Error("Cannot find add button for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._addButton.addEventListener('click', this._addInstructions.bind(this));
+
+ this._form = this._instructionsList.closest('form');
+ if (this._form === null) {
+ throw new Error("Cannot find form element for instructions field with id '" + this._formFieldId + "'.");
+ }
+ this._form.addEventListener('submit', this._submit.bind(this));
+
+ var hasInstallInstructions = false;
+
+ for (var index in existingInstructions) {
+ var instructions = existingInstructions[index];
+
+ if (instructions.type === 'install') {
+ hasInstallInstructions = true;
+ break;
+ }
+ }
+
+ // ensure that there are always installation instructions
+ if (!hasInstallInstructions) {
+ this._addInstructionsByData({
+ fromVersion: '',
+ type: 'install'
+ });
+ }
+
+ existingInstructions.forEach(this._addInstructionsByData.bind(this));
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Adds an instruction to a set of instructions as a consequence
+ * of the given event. If the instruction data is invalid, an
+ * error message is shown and no instruction is added.
+ *
+ * @param {Event} event event that triggered trying to add the instruction
+ */
+ _addInstruction: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var instructionsId = elData(event.currentTarget.closest('li.section'), 'instructions-id');
+
+ // note: data will be validated/filtered by the server
+
+ var pipField = elById(this._formFieldId + '_instructions' + instructionsId + '_pip');
+
+ // ignore pressing button if no PIP has been selected
+ if (!pipField.value) {
+ return;
+ }
+
+ var valueField = elById(this._formFieldId + '_instructions' + instructionsId + '_value');
+ var runStandaloneField = elById(this._formFieldId + '_instructions' + instructionsId + '_runStandalone');
+ var applicationField = elById(this._formFieldId + '_instructions' + instructionsId + '_application');
+
+ this._addInstructionByData(instructionsId, {
+ application: _applicationPips.indexOf(pipField.value) !== -1 ? applicationField.value : '',
+ pip: pipField.value,
+ runStandalone: ~~runStandaloneField.checked,
+ value: valueField.value
+ });
+
+ // empty fields
+ pipField.value = '';
+ valueField.value = '';
+ runStandaloneField.checked = false;
+ applicationField.value = '';
+ elById(this._formFieldId + '_instructions' + instructionsId + '_valueDescription').innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description');
+ this._toggleApplicationFormField(instructionsId);
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Adds an instruction to the set of instructions with the given id.
+ *
+ * @param {int} instructionsId
+ * @param {object} instructionData
+ */
+ _addInstructionByData: function(instructionsId, instructionData) {
+ var instructionId = ++this._instructionCounter;
+
+ var instructionList = elById(this._formFieldId + '_instructions' + instructionsId + '_instructionList');
+
+ var listItem = elCreate('li');
+ listItem.className = 'sortableNode';
+ listItem.id = this._formFieldId + '_instruction' + instructionId;
+ elData(listItem, 'instruction-id', instructionId);
+ elData(listItem, 'application', instructionData.application);
+ elData(listItem, 'pip', instructionData.pip);
+ elData(listItem, 'runStandalone', instructionData.runStandalone);
+ elData(listItem, 'value', instructionData.value);
+
+ var content = '' +
+ '<div class="sortableNodeLabel">' +
+ ' <div class="jsDevtoolsProjectInstruction">' +
+ ' ' + Language.get('wcf.acp.devtools.project.instruction.instruction', instructionData);
+
+ if (instructionData.errors) {
+ for (var index in instructionData.errors) {
+ content += '<small class="innerError">' + instructionData.errors[index] + '</small>';
+ }
+ }
+
+ content += '' +
+ ' </div>' +
+ ' <span class="statusDisplay sortableButtonContainer">' +
+ ' <span class="icon icon16 fa-pencil pointer jsTooltip" id="' + this._formFieldId + '_instruction' + instructionId + '_editButton" title="' + Language.get('wcf.global.button.edit') + '"></span>' +
+ ' <span class="icon icon16 fa-times pointer jsTooltip" id="' + this._formFieldId + '_instruction' + instructionId + '_deleteButton" title="' + Language.get('wcf.global.button.delete') + '"></span>' +
+ ' </span>' +
+ '</div>';
+
+ listItem.innerHTML = content;
+
+ instructionList.appendChild(listItem);
+
+ elById(this._formFieldId + '_instruction' + instructionId + '_deleteButton').addEventListener('click', this._removeInstruction.bind(this));
+ elById(this._formFieldId + '_instruction' + instructionId + '_editButton').addEventListener('click', this._editInstruction.bind(this));
+ },
+
+ /**
+ * Adds a set of instructions as a consequenc of the given event.
+ * If the instructions data is invalid, an error message is shown
+ * and no instruction set is added.
+ *
+ * @param {Event} event event that triggered trying to add the instructions
+ */
+ _addInstructions: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // validate data
+ if (!this._validateInstructionsType() || (this._instructionsType.value === 'update' && !this._validateFromVersion(this._fromVersion))) {
+ return;
+ }
+
+ this._addInstructionsByData({
+ fromVersion: this._instructionsType.value === 'update' ? this._fromVersion.value : '',
+ type: this._instructionsType.value
+ });
+
+ // empty fields
+ this._instructionsType.value = '';
+ this._fromVersion.value = '';
+
+ this._toggleFromVersionFormField();
+
+ DomChangeListener.trigger();
+ },
+
+ /**
+ * Adds a set of instructions.
+ *
+ * @param {object} instructionData
+ */
+ _addInstructionsByData: function(instructionsData) {
+ var instructionsId = ++this._instructionsCounter;
+
+ var listItem = elCreate('li');
+ listItem.className = 'section';
+ listItem.innerHTML = this._instructionsTemplate.fetch({
+ instructionsId: instructionsId,
+ sectionTitle: Language.get('wcf.acp.devtools.project.instructions.type.' + instructionsData.type + '.title', {
+ fromVersion: instructionsData.fromVersion
+ }),
+ type: instructionsData.type
+ });
+
+ listItem.id = this._formFieldId + '_instructions' + instructionsId;
+ elData(listItem, 'instructions-id', instructionsId);
+ elData(listItem, 'type', instructionsData.type);
+ elData(listItem, 'fromVersion', instructionsData.fromVersion);
+
+ elById(this._formFieldId + '_instructions' + instructionsId + '_valueDescription')
+
+ this._instructionsList.appendChild(listItem);
+
+ var instructionListContainer = elById(this._formFieldId + '_instructions' + instructionsId + '_instructionListContainer');
+ for (var errorMessage of instructionsData.errors || []) {
+ var small = elCreate('small');
+ small.className = 'innerError';
+ small.innerHTML = errorMessage;
+
+ instructionListContainer.parentNode.insertBefore(small, instructionListContainer);
+ }
+
+ new UiSortableList({
+ containerId: instructionListContainer.id,
+ isSimpleSorting: true,
+ options: {
+ toleranceElement: '> div'
+ }
+ });
+
+ var deleteButton = elById(this._formFieldId + '_instructions' + instructionsId + '_deleteButton');
+ if (instructionsData.type === 'update') {
+ elById(this._formFieldId + '_instructions' + instructionsId + '_deleteButton').addEventListener('click', this._removeInstructions.bind(this));
+ elById(this._formFieldId + '_instructions' + instructionsId + '_editButton').addEventListener('click', this._editInstructions.bind(this));
+ }
+
+ elById(this._formFieldId + '_instructions' + instructionsId + '_pip').addEventListener('change', this._changeInstructionPip.bind(this));
+ elById(this._formFieldId + '_instructions' + instructionsId + '_value').addEventListener('keypress', this._instructionKeyPress.bind(this));
+ elById(this._formFieldId + '_instructions' + instructionsId + '_addButton').addEventListener('click', this._addInstruction.bind(this));
+
+ if (instructionsData.instructions) {
+ for (var index in instructionsData.instructions) {
+ this._addInstructionByData(instructionsId, instructionsData.instructions[index]);
+ }
+ }
+ },
+
+ /**
+ * Is called if the selected package installation plugin of an
+ * instruction is changed.
+ *
+ * @param {Event} event change event
+ */
+ _changeInstructionPip: function(event) {
+ var pip = event.currentTarget.value;
+ var instructionsId = elData(event.currentTarget.closest('li.section'), 'instructions-id');
+ var description = elById(this._formFieldId + '_instructions' + instructionsId + '_valueDescription');
+
+ // update value description
+ if (this._pipDefaultFilenames[pip] !== '') {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description.defaultFilename', {
+ defaultFilename: this._pipDefaultFilenames[pip]
+ });
+ }
+ else {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description');
+ }
+
+ var valueDlClassList = elById(this._formFieldId + '_instructions' + instructionsId + '_value').closest('dl').classList;
+ var applicationDl = elById(this._formFieldId + '_instructions' + instructionsId + '_application').closest('dl');
+
+ // toggle application selector
+ this._toggleApplicationFormField(instructionsId);
+ },
+
+ /**
+ * Opens a dialog to edit an existing instruction.
+ *
+ * @param {Event} event edit button click event
+ */
+ _editInstruction: function(event) {
+ var listItem = event.currentTarget.closest('li');
+
+ var instructionId = elData(listItem, 'instruction-id');
+ var application = elData(listItem, 'application');
+ var pip = elData(listItem, 'pip');
+ var runStandalone = elDataBool(listItem, 'runStandalone');
+ var value = elData(listItem, 'value');
+
+ var dialogContent = this._instructionEditDialogTemplate.fetch({
+ runStandalone: runStandalone,
+ value: value
+ });
+
+ var dialogId = 'instructionEditDialog' + instructionId;
+ if (!UiDialog.getDialog(dialogId)) {
+ UiDialog.openStatic(dialogId, dialogContent, {
+ onSetup: function(content) {
+ var applicationSelect = elBySel('select[name=application]', content);
+ var pipSelect = elBySel('select[name=pip]', content);
+ var runStandaloneInput = elBySel('input[name=runStandalone]', content);
+ var valueInput = elBySel('input[name=value]', content);
+
+ // set values of `select` elements
+ applicationSelect.value = application;
+ pipSelect.value = pip;
+
+ var submit = function() {
+ var listItem = elById(this._formFieldId + '_instruction' + instructionId);
+ elData(listItem, 'application', _applicationPips.indexOf(pipSelect.value) !== -1 ? applicationSelect.value : '');
+ elData(listItem, 'pip', pipSelect.value);
+ elData(listItem, 'runStandalone', ~~runStandaloneInput.checked);
+ elData(listItem, 'value', valueInput.value);
+
+ // note: data will be validated/filtered by the server
+
+ elByClass('jsDevtoolsProjectInstruction', listItem)[0].innerHTML = Language.get('wcf.acp.devtools.project.instruction.instruction', {
+ application: elData(listItem, 'application'),
+ pip: elData(listItem, 'pip'),
+ runStandalone: elDataBool(listItem, 'runStandalone'),
+ value: elData(listItem, 'value'),
+ });
+
+ DomChangeListener.trigger();
+
+ UiDialog.close(dialogId);
+ }.bind(this);
+
+ valueInput.addEventListener('keypress', function(event) {
+ if (EventKey.Enter(event)) {
+ submit();
+ }
+ });
+
+ elBySel('button[data-type=submit]', content).addEventListener('click', submit);
+
+ var pipChange = function() {
+ var pip = pipSelect.value;
+
+ if (_applicationPips.indexOf(pip) !== -1) {
+ elShow(applicationSelect.closest('dl'));
+ }
+ else {
+ elHide(applicationSelect.closest('dl'));
+ }
+
+ var description = DomTraverse.nextByTag(valueInput, 'SMALL');
+ if (this._pipDefaultFilenames[pip] !== '') {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description.defaultFilename', {
+ defaultFilename: this._pipDefaultFilenames[pip]
+ });
+ }
+ else {
+ description.innerHTML = Language.get('wcf.acp.devtools.project.instruction.value.description');
+ }
+ }.bind(this);
+
+ pipSelect.addEventListener('change', pipChange);
+ pipChange();
+ }.bind(this),
+ title: Language.get('wcf.acp.devtools.project.instruction.edit')
+ });
+ }
+ else {
+ UiDialog.openStatic(dialogId);
+ }
+ },
+
+ /**
+ * Opens a dialog to edit an existing set of instructions.
+ *
+ * @param {Event} event edit button click event
+ */
+ _editInstructions: function(event) {
+ var listItem = event.currentTarget.closest('li');
+
+ var instructionsId = elData(listItem, 'instructions-id');
+ var fromVersion = elData(listItem, 'fromVersion');
+
+ var dialogContent = this._instructionsEditDialogTemplate.fetch({
+ fromVersion: fromVersion
+ });
+
+ var dialogId = 'instructionsEditDialog' + instructionsId;
+ if (!UiDialog.getDialog(dialogId)) {
+ UiDialog.openStatic(dialogId, dialogContent, {
+ onSetup: function (content) {
+ var fromVersion = elBySel('input[name=fromVersion]', content);
+
+ var submit = function () {
+ if (!this._validateFromVersion(fromVersion)) {
+ return;
+ }
+
+ var instructions = elById(this._formFieldId + '_instructions' + instructionsId);
+ elData(instructions, 'fromVersion', fromVersion.value);
+
+ elByClass('jsInstructionsTitle', instructions)[0].textContent = Language.get('wcf.acp.devtools.project.instructions.type.update.title', {
+ fromVersion: fromVersion.value
+ });
+
+ DomChangeListener.trigger();
+
+ UiDialog.close(dialogId);
+ }.bind(this);
+
+ fromVersion.addEventListener('keypress', function (event) {
+ if (EventKey.Enter(event)) {
+ submit();
+ }
+ });
+
+ elBySel('button[data-type=submit]', content).addEventListener('click', submit);
+ }.bind(this),
+ title: Language.get('wcf.acp.devtools.project.instructions.edit')
+ });
+ }
+ else {
+ UiDialog.openStatic(dialogId);
+ }
+ },
+
+ /**
+ * Returns the error element for the given form field element.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getErrorElement: function(element, createIfNoNExistent) {
+ var error = DomTraverse.nextByClass(element, 'innerError');
+
+ if (error === null && createIfNoNExistent) {
+ error = elCreate('small');
+ error.className = 'innerError';
+
+ DomUtil.insertAfter(error, element);
+ }
+
+ return error;
+ },
+
+ /**
+ * Returns the error element for the from version form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getFromVersionErrorElement: function(inputField, createIfNonExistent) {
+ return this._getErrorElement(inputField, createIfNonExistent);
+ },
+
+ /**
+ * Returns the error element for the instruction type form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getInstructionsTypeErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._instructionsType, createIfNonExistent);
+ },
+
+ /**
+ * Adds an instruction after pressing ENTER in a relevant text
+ * field.
+ *
+ * @param {Event} event
+ */
+ _instructionKeyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ this._addInstruction(event);
+ }
+ },
+
+ /**
+ * Adds a set of instruction after pressing ENTER in a relevant
+ * text field.
+ *
+ * @param {Event} event
+ */
+ _instructionsKeyPress: function(event) {
+ if (EventKey.Enter(event)) {
+ this._addInstructions(event);
+ }
+ },
+
+ /**
+ * Removes an instruction by clicking on its delete button.
+ *
+ * @param {Event} event delete button click event
+ */
+ _removeInstruction: function(event) {
+ var instruction = event.currentTarget.closest('li');
+
+ UiConfirmation.show({
+ confirm: function() {
+ elRemove(instruction);
+ },
+ message: Language.get('wcf.acp.devtools.project.instruction.delete.confirmMessages')
+ });
+ },
+
+ /**
+ * Removes a set of instructions by clicking on its delete button.
+ *
+ * @param {Event} event delete button click event
+ */
+ _removeInstructions: function(event) {
+ var instructions = event.currentTarget.closest('li');
+
+ UiConfirmation.show({
+ confirm: function() {
+ elRemove(instructions);
+ },
+ message: Language.get('wcf.acp.devtools.project.instructions.delete.confirmMessages')
+ });
+ },
+
+ /**
+ * Adds all necessary (hidden) form fields to the form when
+ * submitting the form.
+ */
+ _submit: function(event) {
+ DomTraverse.childrenByTag(this._instructionsList, 'LI').forEach(function(instructions, instructionsIndex) {
+ var namePrefix = this._formFieldId + '[' + instructionsIndex + ']';
+
+ var instructionsType = elCreate('input');
+ elAttr(instructionsType, 'type', 'hidden');
+ elAttr(instructionsType, 'name', namePrefix + '[type]')
+ instructionsType.value = elData(instructions, 'type');
+ this._form.appendChild(instructionsType);
+
+ if (instructionsType.value === 'update') {
+ var fromVersion = elCreate('input');
+ elAttr(fromVersion, 'type', 'hidden');
+ elAttr(fromVersion, 'name', this._formFieldId + '[' + instructionsIndex + '][fromVersion]')
+ fromVersion.value = elData(instructions, 'fromVersion');
+ this._form.appendChild(fromVersion);
+ }
+
+ DomTraverse.childrenByTag(elById(instructions.id + '_instructionList'), 'LI').forEach(function(instruction, instructionIndex) {
+ var namePrefix = this._formFieldId + '[' + instructionsIndex + '][instructions][' + instructionIndex + ']';
+
+ for (var property of ['pip', 'value', 'runStandalone']) {
+ var element = elCreate('input');
+ elAttr(element, 'type', 'hidden');
+ elAttr(element, 'name', namePrefix + '[' + property + ']')
+ element.value = elData(instruction, property);
+ this._form.appendChild(element);
+ }
+
+ if (_applicationPips.indexOf(elData(instruction, 'pip')) !== -1) {
+ var application = elCreate('input');
+ elAttr(application, 'type', 'hidden');
+ elAttr(application, 'name', namePrefix + '[application]')
+ application.value = elData(instruction, 'application');
+ this._form.appendChild(application);
+ }
+ }.bind(this));
+ }.bind(this));
+ },
+
+ /**
+ * Toggles the visibility of the application form field based on
+ * the selected pip for the instructions with the given id.
+ *
+ * @param {int} instructionsId id of the relevant instruction set
+ */
+ _toggleApplicationFormField: function(instructionsId) {
+ var pip = elById(this._formFieldId + '_instructions' + instructionsId + '_pip').value;
+
+ var valueDlClassList = elById(this._formFieldId + '_instructions' + instructionsId + '_value').closest('dl').classList;
+ var applicationDl = elById(this._formFieldId + '_instructions' + instructionsId + '_application').closest('dl');
+
+ if (_applicationPips.indexOf(pip) !== -1) {
+ valueDlClassList.remove('col-md-9');
+ valueDlClassList.add('col-md-7');
+ elShow(applicationDl);
+ }
+ else {
+ valueDlClassList.remove('col-md-7');
+ valueDlClassList.add('col-md-9');
+ elHide(applicationDl);
+ }
+ },
+
+ /**
+ * Toggles the visibility of the `fromVersion` form field based on
+ * the selected instructions type.
+ */
+ _toggleFromVersionFormField: function() {
+ var instructionsTypeList = this._instructionsType.closest('dl').classList;
+ var fromVersionDl = this._fromVersion.closest('dl');
+
+ if (this._instructionsType.value === 'update') {
+ instructionsTypeList.remove('col-md-10');
+ instructionsTypeList.add('col-md-5');
+ elShow(fromVersionDl);
+ }
+ else {
+ instructionsTypeList.remove('col-md-5');
+ instructionsTypeList.add('col-md-10');
+ elHide(fromVersionDl);
+ }
+ },
+
+ /**
+ * Returns `true` if the currently entered update "from version"
+ * is valid. Otherwise `false` is returned and an error message
+ * is shown.
+ *
+ * @return {boolean}
+ */
+ _validateFromVersion: function(inputField) {
+ var version = inputField.value;
+
+ if (version === '') {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.global.form.error.empty');
+
+ return false;
+ }
+
+ if (version.length > 50) {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.maximumLength');
+
+ return false;
+ }
+
+ // wildcard versions are checked on the server side
+ if (version.indexOf('*') === -1) {
+ // see `wcf\data\package\Package::isValidVersion()`
+ if (!version.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
+
+ return false;
+ }
+ }
+ else if (!version.replace('*', '0').match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$/i)) {
+ this._getFromVersionErrorElement(inputField, true).textContent = Language.get('wcf.acp.devtools.project.packageVersion.error.format');
+
+ return false;
+ }
+
+ // remove outdated errors
+ var error = this._getFromVersionErrorElement(inputField);
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ },
+
+ /**
+ * Returns `true` if the entered update instructions type is valid.
+ * Otherwise `false` is returned and an error message is shown.
+ *
+ * @return {boolean}
+ */
+ _validateInstructionsType: function() {
+ if (this._instructionsType.value !== 'install' && this._instructionsType.value !== 'update') {
+ if (this._instructionsType.value === '') {
+ this._getInstructionsTypeErrorElement(true).textContent = Language.get('wcf.global.form.error.empty');
+ }
+ else {
+ this._getInstructionsTypeErrorElement(true).textContent = Language.get('wcf.global.form.error.noValidSelection');
+ }
+
+ return false;
+ }
+
+ // there may only be one set of installation instructions
+ if (this._instructionsType.value === 'install') {
+ var hasInstall = false;
+ [].forEach.call(this._instructionsList.children, function(instructions) {
+ if (elData(instructions, 'type') === 'install') {
+ hasInstall = true;
+ }
+ });
+
+ if (hasInstall) {
+ this._getInstructionsTypeErrorElement(true).textContent = Language.get('wcf.acp.devtools.project.instructions.type.update.error.duplicate');
+
+ return false;
+ }
+ }
+
+ // remove outdated errors
+ var error = this._getInstructionsTypeErrorElement();
+ if (error !== null) {
+ elRemove(error);
+ }
+
+ return true;
+ }
+ };
+
+ return Instructions;
+});
+
+/**
+ * Manages the packages entered in a devtools project optional package form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/OptionalPackages
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/OptionalPackages',['./AbstractPackageList', 'Core', 'Language'], function(AbstractPackageList, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function OptionalPackages(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ Core.inherit(OptionalPackages, AbstractPackageList, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_populateListItem
+ */
+ _populateListItem(listItem, packageData) {
+ OptionalPackages._super.prototype._populateListItem.call(this, listItem, packageData);
+
+ listItem.innerHTML = ' ' + Language.get('wcf.acp.devtools.project.optionalPackage.optionalPackage', {
+ file: packageData.file,
+ packageIdentifier: packageData.packageIdentifier
+ });
+ }
+ });
+
+ return OptionalPackages;
+});
+
+/**
+ * Manages the packages entered in a devtools project required package form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/RequiredPackages
+ * @see module:WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList
+ * @since 5.2
+ */
+define('WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/RequiredPackages',['./AbstractPackageList', 'Core', 'Language'], function(AbstractPackageList, Core, Language) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function RequiredPackages(formFieldId, existingPackages) {
+ this.init(formFieldId, existingPackages);
+ };
+ Core.inherit(RequiredPackages, AbstractPackageList, {
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#init
+ */
+ init: function(formFieldId, existingPackages) {
+ RequiredPackages._super.prototype.init.call(this, formFieldId, existingPackages);
+
+ this._minVersion = elById(this._formFieldId + '_minVersion');
+ if (this._minVersion === null) {
+ throw new Error("Cannot find minimum version form field for packages field with id '" + this._formFieldId + "'.");
+ }
+ this._minVersion.addEventListener('keypress', this._keyPress.bind(this));
+
+ this._file = elById(this._formFieldId + '_file');
+ if (this._file === null) {
+ throw new Error("Cannot find file form field for required field with id '" + this._formFieldId + "'.");
+ }
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_createSubmitFields
+ */
+ _createSubmitFields: function(listElement, index) {
+ RequiredPackages._super.prototype._createSubmitFields.call(this, listElement, index);
+
+ var minVersion = elCreate('input');
+ elAttr(minVersion, 'type', 'hidden');
+ elAttr(minVersion, 'name', this._formFieldId + '[' + index + '][minVersion]')
+ minVersion.value = elData(listElement, 'min-version');
+ this._form.appendChild(minVersion);
+
+ var file = elCreate('input');
+ elAttr(file, 'type', 'hidden');
+ elAttr(file, 'name', this._formFieldId + '[' + index + '][file]')
+ file.value = elData(listElement, 'file');
+ this._form.appendChild(file);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_emptyInput
+ */
+ _emptyInput() {
+ RequiredPackages._super.prototype._emptyInput.call(this);
+
+ this._minVersion.value = '';
+ this._file.checked = false;
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_getInputData
+ */
+ _getInputData: function() {
+ return Core.extend(RequiredPackages._super.prototype._getInputData.call(this), {
+ file: this._file.checked,
+ minVersion: this._minVersion.value
+ });
+ },
+
+ /**
+ * Returns the error element for the minimum version form field.
+ * If `createIfNonExistent` is not given or `false`, `null` is returned
+ * if there is no error element, otherwise an empty error element
+ * is created and returned.
+ *
+ * @param {?boolean} createIfNonExistent
+ * @return {?HTMLElement}
+ */
+ _getMinVersionErrorElement: function(createIfNonExistent) {
+ return this._getErrorElement(this._minVersion, createIfNonExistent);
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_populateListItem
+ */
+ _populateListItem(listItem, packageData) {
+ RequiredPackages._super.prototype._populateListItem.call(this, listItem, packageData);
+
+ elData(listItem, 'min-version', packageData.minVersion);
+ elData(listItem, 'file', ~~packageData.file);
+ listItem.innerHTML = ' ' + Language.get('wcf.acp.devtools.project.requiredPackage.requiredPackage', {
+ file: ~~packageData.file,
+ minVersion: packageData.minVersion,
+ packageIdentifier: packageData.packageIdentifier
+ });
+ },
+
+ /**
+ * @see WoltLabSuite/Core/Form/Builder/Field/Devtools/Project/AbstractPackageList#_validateInput
+ */
+ _validateInput: function() {
+ return RequiredPackages._super.prototype._validateInput.call(this) && this._validateVersion(
+ this._minVersion.value,
+ this._getMinVersionErrorElement.bind(this)
+ );
+ }
+ });
+
+ return RequiredPackages;
+});
+
+/**
+ * Default implementation for user interaction menu items used in the user profile.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Abstract
+ */
+define('WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Abstract',['Ajax', 'Dom/Util'], function(Ajax, DomUtil) {
+ "use strict";
+
+ /**
+ * Creates a new user profile menu item.
+ *
+ * @param {int} userId user id
+ * @param {boolean} isActive true if item is initially active
+ * @constructor
+ */
+ function UiUserProfileMenuItemAbstract(userId, isActive) {}
+ UiUserProfileMenuItemAbstract.prototype = {
+ /**
+ * Creates a new user profile menu item.
+ *
+ * @param {int} userId user id
+ * @param {boolean} isActive true if item is initially active
+ */
+ init: function(userId, isActive) {
+ this._userId = userId;
+ this._isActive = (isActive !== false);
+
+ this._initButton();
+ this._updateButton();
+ },
+
+ /**
+ * Initializes the menu item.
+ *
+ * @protected
+ */
+ _initButton: function() {
+ var button = elCreate('a');
+ button.href = '#';
+ button.addEventListener(WCF_CLICK_EVENT, this._toggle.bind(this));
+
+ var listItem = elCreate('li');
+ listItem.appendChild(button);
+
+ var menu = elBySel('.userProfileButtonMenu[data-menu="interaction"]');
+ DomUtil.prepend(listItem, menu);
+
+ this._button = button;
+ this._listItem = listItem;
+ },
+
+ /**
+ * Handles clicks on the menu item button.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _toggle: function(event) {
+ event.preventDefault();
+
+ Ajax.api(this, {
+ actionName: this._getAjaxActionName(),
+ parameters: {
+ data: {
+ userID: this._userId
+ }
+ }
+ });
+ },
+
+ /**
+ * Updates the button state and label.
+ *
+ * @protected
+ */
+ _updateButton: function() {
+ this._button.textContent = this._getLabel();
+ this._listItem.classList[(this._isActive ? 'add' : 'remove')]('active');
+ },
+
+ /**
+ * Returns the button label.
+ *
+ * @return {string} button label
+ * @protected
+ * @abstract
+ */
+ _getLabel: function() {
+ throw new Error("Implement me!");
+ },
+
+ /**
+ * Returns the Ajax action name.
+ *
+ * @return {string} ajax action name
+ * @protected
+ * @abstract
+ */
+ _getAjaxActionName: function() {
+ throw new Error("Implement me!");
+ },
+
+ /**
+ * Handles successful Ajax requests.
+ *
+ * @protected
+ * @abstract
+ */
+ _ajaxSuccess: function() {
+ throw new Error("Implement me!");
+ },
+
+ /**
+ * Returns the default Ajax request data
+ *
+ * @return {Object} ajax request data
+ * @protected
+ * @abstract
+ */
+ _ajaxSetup: function() {
+ throw new Error("Implement me!");
+ }
+ };
+
+ return UiUserProfileMenuItemAbstract;
+});
+
+define('WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Follow',['Core', 'Language', 'Ui/Notification', './Abstract'], function(Core, Language, UiNotification, UiUserProfileMenuItemAbstract) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _getLabel: function() {},
+ _getAjaxActionName: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ init: function() {},
+ _initButton: function() {},
+ _toggle: function() {},
+ _updateButton: function() {}
+ };
+ return Fake;
+ }
+
+ function UiUserProfileMenuItemFollow(userId, isActive) { this.init(userId, isActive); }
+ Core.inherit(UiUserProfileMenuItemFollow, UiUserProfileMenuItemAbstract, {
+ _getLabel: function() {
+ return Language.get('wcf.user.button.' + (this._isActive ? 'un' : '') + 'follow');
+ },
+
+ _getAjaxActionName: function() {
+ return this._isActive ? 'unfollow' : 'follow';
+ },
+
+ _ajaxSuccess: function(data) {
+ this._isActive = (data.returnValues.following ? true : false);
+ this._updateButton();
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\follow\\UserFollowAction'
+ }
+ };
+ }
+ });
+
+ return UiUserProfileMenuItemFollow;
+});
+
+define('WoltLabSuite/Core/Ui/User/Profile/Menu/Item/Ignore',['Core', 'Language', 'Ui/Notification', './Abstract'], function(Core, Language, UiNotification, UiUserProfileMenuItemAbstract) {
+ "use strict";
+
+ if (!COMPILER_TARGET_DEFAULT) {
+ var Fake = function() {};
+ Fake.prototype = {
+ _getLabel: function() {},
+ _getAjaxActionName: function() {},
+ _ajaxSuccess: function() {},
+ _ajaxSetup: function() {},
+ init: function() {},
+ _initButton: function() {},
+ _toggle: function() {},
+ _updateButton: function() {}
+ };
+ return Fake;
+ }
+
+ function UiUserProfileMenuItemIgnore(userId, isActive) { this.init(userId, isActive); }
+ Core.inherit(UiUserProfileMenuItemIgnore, UiUserProfileMenuItemAbstract, {
+ _getLabel: function() {
+ return Language.get('wcf.user.button.' + (this._isActive ? 'un' : '') + 'ignore');
+ },
+
+ _getAjaxActionName: function() {
+ return this._isActive ? 'unignore' : 'ignore';
+ },
+
+ _ajaxSuccess: function(data) {
+ this._isActive = (data.returnValues.isIgnoredUser ? true : false);
+ this._updateButton();
+
+ UiNotification.show();
+ },
+
+ _ajaxSetup: function() {
+ return {
+ data: {
+ className: 'wcf\\data\\user\\ignore\\UserIgnoreAction'
+ }
+ };
+ }
+ });
+
+ return UiUserProfileMenuItemIgnore;
+});
+
+/*
+ * Polyfill for `Element.prototype.matches()` and `Element.prototype.closest()`
+ * Copyright (c) 2015 Jonathan Neal - https://github.com/jonathantneal/closest
+ * License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/)
+ */
+(function(ELEMENT) {
+ ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector;
+
+ ELEMENT.closest = ELEMENT.closest || function closest(selector) {
+ var element = this;
+
+ while (element) {
+ if (element.matches(selector)) {
+ break;
+ }
+
+ element = element.parentElement;
+ }
+
+ return element;
+ };
+}(Element.prototype));
+
+define("closest", function(){});
+
+(function(window) {
+ var orgRequire = window.require;
+ var queue = [];
+ var counter = 0;
+
+ window.orgRequire = orgRequire
+
+ window.require = function(dependencies, callback, errBack) {
+ if (!Array.isArray(dependencies)) {
+ return orgRequire.apply(window, arguments);
+ }
+
+ var promise = new Promise(function (resolve, reject) {
+ var i = counter++;
+ queue.push(i);
+
+ orgRequire(dependencies, function () {
+ var args = arguments;
+
+ queue[queue.indexOf(i)] = function() { resolve(args); };
+
+ executeCallbacks();
+ }, function (err) {
+ queue[queue.indexOf(i)] = function() { reject(err); };
+
+ executeCallbacks();
+ });
+ });
+
+ if (callback) {
+ promise = promise.then(function (objects) {
+ return callback.apply(window, objects);
+ });
+ }
+ if (errBack) {
+ promise.catch(errBack);
+ }
+
+ return promise;
+ };
+ window.require.config = orgRequire.config;
+
+ function executeCallbacks() {
+ while (queue.length) {
+ if (typeof queue[0] !== 'function') {
+ break;
+ }
+
+ queue.shift()();
+ }
+ }
+})(window);
+
+define("require.linearExecution", function(){});
+