From a2c008390565bc11d276f478c423fe2136d70efe Mon Sep 17 00:00:00 2001 From: Tam Date: Wed, 11 Jul 2018 12:17:42 +0100 Subject: [PATCH 1/8] Fixed #110 --- CHANGELOG.md | 5 +++++ src/fields/SeoField.php | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b27014e..a53bd14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.3.1 - WIP + +### Changed +- SEO robots no longer return empty values #110 + ## 3.3.0 - 2018-05-25 **Heads up:** This update includes changes to the SEO `meta.twig`. If you are using a custom version you can review the changes [here](https://github.com/ethercreative/seo/commits/v3/src/templates/_seo/meta.twig). diff --git a/src/fields/SeoField.php b/src/fields/SeoField.php index bd7f419..0b58a5f 100644 --- a/src/fields/SeoField.php +++ b/src/fields/SeoField.php @@ -146,6 +146,17 @@ public function normalizeValue ($value, ElementInterface $element = null) $value['advanced'] ?? [] ); + // Filter out empty robots + if (array_key_exists('robots', $value['advanced'])) + { + $value['advanced']['robots'] = array_filter( + $value['advanced']['robots'], + function ($v) { + return !!$v; + } + ); + } + return $value; } From f7efc3f412c2088f1988d7c5d543a1dfacc3d9cf Mon Sep 17 00:00:00 2001 From: Tam Date: Wed, 11 Jul 2018 12:52:57 +0100 Subject: [PATCH 2/8] Simplified robots empty filter --- src/fields/SeoField.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/fields/SeoField.php b/src/fields/SeoField.php index 0b58a5f..980861b 100644 --- a/src/fields/SeoField.php +++ b/src/fields/SeoField.php @@ -149,12 +149,7 @@ public function normalizeValue ($value, ElementInterface $element = null) // Filter out empty robots if (array_key_exists('robots', $value['advanced'])) { - $value['advanced']['robots'] = array_filter( - $value['advanced']['robots'], - function ($v) { - return !!$v; - } - ); + $value['advanced']['robots'] = array_filter($value['advanced']['robots']); } return $value; From f90734bd0dfa2aafca28e1c09435c6bf4ea56776 Mon Sep 17 00:00:00 2001 From: Tam Date: Wed, 11 Jul 2018 12:58:04 +0100 Subject: [PATCH 3/8] Fixed #114 --- CHANGELOG.md | 1 + resources/js/field/FocusKeywords.js | 10 +++++++++- src/fields/SeoField.php | 5 ++--- src/resources/js/SeoField.min.js | 2 +- src/resources/js/SeoField.min.js.map | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a53bd14..bae2abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Changed - SEO robots no longer return empty values #110 +- Text in the keyword input will automatically be turned into a tag on blur #114 ## 3.3.0 - 2018-05-25 **Heads up:** This update includes changes to the SEO `meta.twig`. If you are using a custom version you can review the changes [here](https://github.com/ethercreative/seo/commits/v3/src/templates/_seo/meta.twig). diff --git a/resources/js/field/FocusKeywords.js b/resources/js/field/FocusKeywords.js index 56fab62..8ec4715 100644 --- a/resources/js/field/FocusKeywords.js +++ b/resources/js/field/FocusKeywords.js @@ -353,8 +353,16 @@ export default class FocusKeywords { /** * Fired when the keywords input is blurred */ - onInputBlur = () => { + onInputBlur = e => { this.inputWrap.classList.remove('focused'); + + if (e.target.value.trim() !== "") { + this.onInputKeyDown({ + target: e.target, + keyCode: 13, + preventDefault: () => {}, + }); + } }; /** diff --git a/src/fields/SeoField.php b/src/fields/SeoField.php index 980861b..f5df80c 100644 --- a/src/fields/SeoField.php +++ b/src/fields/SeoField.php @@ -148,9 +148,8 @@ public function normalizeValue ($value, ElementInterface $element = null) // Filter out empty robots if (array_key_exists('robots', $value['advanced'])) - { - $value['advanced']['robots'] = array_filter($value['advanced']['robots']); - } + $value['advanced']['robots'] = + array_filter($value['advanced']['robots']); return $value; } diff --git a/src/resources/js/SeoField.min.js b/src/resources/js/SeoField.min.js index 0a39ae4..881a89f 100644 --- a/src/resources/js/SeoField.min.js +++ b/src/resources/js/SeoField.min.js @@ -1,2 +1,2 @@ -var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},classCallCheck=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},createClass=function(){function e(e,t){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:"div",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],n=document.createElement(e),r=!0,a=!1,s=void 0;try{for(var o,l=Object.entries(t)[Symbol.iterator]();!(r=(o=l.next()).done);r=!0){var c=o.value,d=slicedToArray(c,2),h=d[0],u=d[1];u&&((void 0===u?"undefined":_typeof(u))!==_typeof(function(){})?("style"===h&&(u=u.replace(/(?:\r\n|\r|\n|\t|\s+)/g," ").trim()),n.setAttribute(h,u)):"ref"===h?u(n):n.addEventListener(h,u))}}catch(e){a=!0,s=e}finally{try{!r&&l.return&&l.return()}finally{if(a)throw s}}return Array.isArray(i)||(i=[i]),i.map(function(e){if(e)try{n.appendChild(e)}catch(t){n.appendChild(document.createTextNode(e))}}),n}var TextStatistics=function(){function e(t){classCallCheck(this,e),this.text=t?e.cleanText(t):t}return createClass(e,[{key:"fleschKincaidReadingEase",value:function(){return Math.round(10*(206.835-1.015*this.averageWordsPerSentence()-84.6*this.averageSyllablesPerWord()))/10}},{key:"fleschKincaidGradeLevel",value:function(){return Math.round(10*(.39*this.averageWordsPerSentence()+11.8*this.averageSyllablesPerWord()-15.59))/10}},{key:"gunningFogScore",value:function(){return Math.round(.4*(this.averageWordsPerSentence()+this.percentageWordsWithThreeSyllables(!1))*10)/10}},{key:"colemanLiauIndex",value:function(){return Math.round(10*(this.letterCount()/this.wordCount()*5.89-this.sentenceCount()/this.wordCount()*.3-15.8))/10}},{key:"smogIndex",value:function(){return Math.round(1.043*Math.sqrt(this.wordsWithThreeSyllables()*(30/this.sentenceCount())+3.1291)*10)/10}},{key:"automatedReadabilityIndex",value:function(){return Math.round(10*(this.letterCount()/this.wordCount()*4.71+this.wordCount()/this.sentenceCount()*.5-21.43))/10}},{key:"textLength",value:function(){return this.text.length}},{key:"letterCount",value:function(){return this.text.replace(/[^a-z]+/gi,"").length}},{key:"sentenceCount",value:function(){return this.text.replace(/[^.!?]/g,"").length||1}},{key:"wordCount",value:function(){return this.words().length||1}},{key:"words",value:function(){return this._words?this._words:(this._words=this.text.split(/[^a-z0-9']+/i),this._words)}},{key:"averageWordsPerSentence",value:function(){return this.wordCount()/this.sentenceCount()}},{key:"averageSyllablesPerWord",value:function(){var t=0,i=this.wordCount();return this.text.split(/\s+/).forEach(function(i){t+=e.syllableCount(i)}),(t||1)/(i||1)}},{key:"wordsWithThreeSyllables",value:function(){var e=this,t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=0;return t=!1!==t,this.text.split(/\s+/).forEach(function(n){n.match(/^[A-Z]/)&&!t||e.syllableCount(n)>2&&i++}),i}},{key:"percentageWordsWithThreeSyllables",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return this.wordsWithThreeSyllables(e)/this.wordCount()*100}}],[{key:"cleanText",value:function(e){return["li","p","h1","h2","h3","h4","h5","h6","dd"].forEach(function(t){e.replace("",".")}),e=e.replace(/<[^>]+>/g,"").replace(/[,:;()-]/g," ").replace(/[.!?]/g,".").replace(/^\s+/g,"").replace(/[ ]*(\n|\r\n|\r)[ ]*/g," ").replace(/([.])[. ]+/g,".").replace(/[ ]*([.])/g,". ").replace(/\s+/g," ").replace(/\s+$/g,"").replace(/'/g,""),e+="."}},{key:"syllableCount",value:function(e){var t=0,i=0;e=e.toLowerCase().replace(/[^a-z]/g,"");var n={simile:3,forever:3,shoreline:2};if(n.hasOwnProperty(e))return n[e];[/^un/,/^fore/,/ly$/,/less$/,/ful$/,/ers?$/,/ings?$/].forEach(function(t){e.match(t)&&(e=e.replace(t,""),i++)});var r=e.split(/[^aeiouy]+/gi).filter(function(e){return!!e.replace(/\s+/gi,"").length}).length;return t=r+i,[/cial/,/tia/,/cius/,/cious/,/giu/,/ion/,/iou/,/sia$/,/[^aeiuoyt]{2,}ed$/,/.ely$/,/[cg]h?e[rsd]?$/,/rved?$/,/[aeiouy][dt]es?$/,/[aeiouy][^aeiouydt]e[rsd]?$/,/^[dr]e[aeiou][^aeiou]+$/,/[aeiouy]rse$/].forEach(function(i){e.match(i)&&t--}),[/ia/,/riet/,/dien/,/iu/,/io/,/ii/,/[aeiouym]bl$/,/[aeiou]{3}/,/^mc/,/ism$/,/([^aeiouy])\1l$/,/[^l]lien/,/^coa[dglx]./,/[^gq]ua[^auieo]/,/dnt$/,/uity$/,/ie(r|st)$/].forEach(function(i){e.match(i)&&t++}),t||1}}]),e}();function fail(e){Craft.cp.displayError("SEO: "+e),window.console&&console.error.apply(console,["%cSEO: %c "+e,"font-weight: bold;","font-weight: normal;"])}function debounce(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:300,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=void 0;return 0===t?function(){e.apply(this,arguments)}:function(){var r=this,a=arguments;"SyntheticEvent"===a[0].constructor.name&&a[0].persist();var s=i&&!n;clearTimeout(n),n=setTimeout(function(){n=null,i||e.apply(r,a)},t),s&&e.apply(r,a)}}function capitalize(e){return e[0].toUpperCase()+e.slice(1)}function isExternalUrl(e){var t=function(e){var t=/https?:\/\/((?:[\w\d-]+\.)+[\w\d]{2,})/i.exec(e);return null!==t&&t[1]};return t(location.href)===t(e)}function countInArray(e,t){var i=0,n=e.length;if(Array.isArray(t))for(var r=e.length,a=t.length;n--;){for(var s=a,o=0;s--&&n+s1&&void 0!==arguments[1]?arguments[1]:"div",i=capitalize(e);return createElement(t,{class:"seo--light "+e,title:i},i)}var EntryMarkup=function(){function e(){classCallCheck(this,e),this.frame=null,this.postData=null,this.clean()}return createClass(e,[{key:"update",value:function(){var e=this;return new Promise(function(t,i){var n=Garnish.getPostData(document.getElementById("main-form"));e.postData&&Craft.compare(n,e.postData)?t(e.frame.contentWindow.document.body):(e.postData=n,$.ajax({url:Craft.livePreview.previewUrl,method:"POST",data:$.extend({},n,Craft.livePreview.basePostData),xhrFields:{withCredentials:!0},crossDomain:!0,success:function(n){n=(n=n.replace(//g,"")).replace(//g,""),e.frame.contentWindow.document.open(),e.frame.contentWindow.document.write(n),e.frame.contentWindow.document.close(),e.frame.contentWindow.document.body?t(e.frame.contentWindow.document.body):(fail("Failed to parse entry preview"),i())},error:function(){fail("Failed to retrieve entry preview"),i()}}))})}},{key:"clean",value:function(){this.frame&&document.body.removeChild(this.frame),this.frame=createElement("iframe",{frameborder:"0",style:"\n\t\t\t\twidth: 0;\n\t\t\t\theight: 0;\n\t\t\t"}),document.body.appendChild(this.frame)}}]),e}(),EntryMarkup$1=new EntryMarkup,SEO_RATING={NONE:"neutral",GOOD:"good",AVERAGE:"average",POOR:"poor"},SEO_RATING_LABEL={neutral:"Not yet rated",good:"Looks good",average:"Room for improvement",poor:"Needs work"},SEO_REASONS={noContent:"You don't have any content, adding some would be a good start!",titleLengthFailMin:"The title contains {l} characters which is less than the recommended minimum of 40 characters.",titleLengthFailMax:"The title contains {l} characters which is greater than the recommended maximum of 60 characters.",titleLengthSuccess:"The title is between the recommended minimum and maximum length.",titleKeywordFail:"The title does not contain the keyword. Try adding it near the beginning of the title.",titleKeywordSuccess:"The title contains the keyword near the beginning.",titleKeywordPosFail:"The title contains the keyword, but not near the beginning. Try to move it closer to the start of the title.",slugFail:"The URL does not contain the keyword. Try adding it to the slug.",slugSuccess:"The URL contains the keyword.",descFail:"The description does not contain the keyword. Try adding it near the beginning of the description.",descSuccess:"The description contains the keyword.",wordCountFail:"Your text contains {l} words, this is less than the recommended 300 word minimum.",wordCountSuccess:"Your text contains {l} words, this is more than the recommended 300 word minimum.",firstParagraphFail:"The keyword does not appear in the first paragraph of your text. Try adding it.",firstParagraphSuccess:"The keyword appears in the first paragraph of your text.",imagesFail:"Less than half of the images have alt tags containing the keyword, try adding it to more images.",imagesOk:"Half or more of the images have alt tags containing the keyword. To improve this, try adding keywords to all the images alt tags.",imagesSuccess:"All of the images have alt tags containing the keyword.",linksFail:"The page does not contain any outgoing links. Try adding some.",linksSuccess:"The page contains outgoing links.",headingsFail:"The page does not contain any headings that contain the keyword. Try adding some with the keyword.",headingsOk:"The page contains some lower importance headings that contain the keyword. Try adding the keyword to some h2's.",headingsSuccess:"The page contains higher importance headings with the keyword.",densityFail:"The keyword does not appear in the text. It is important to include it in your content.",densityFailUnder:"The keyword density is {d}%, which is well under the advised 2.5%. Try increasing the number of times the keyword is used.",densityOk:"The keyword density is {d}%, which is over the advised 2.5%. The keyword appears {c} times.",densitySuccess:"The keyword density is {d}%, which is near the advised 2.5%.",fleschFail:"The Flesch Reading ease score is {l} which is considered best for university graduates. Try reducing your sentence length to improve readability.",fleschOk:"The Flesch Reading ease score is {l} which is average, and considered easily readable by most users.",fleschSuccess:"The Flesch Reading ease score is {l} meaning your content is readable by all ages."},KeywordChecklist=function(){function e(t,i){classCallCheck(this,e),this.keyword=null,this.keywordLower=null,this.ratings=[],this.ratingOccurrence={},this.content=null,this.text=null,this.stats=null,this.renderChecklistItem=function(e){var t=e.rating,i=e.reason;return createElement("li",{},[createRating(t),createElement("p",{},i)])},this.namespace=t,this.SEO=i,this.bar=document.getElementById(t+"KeywordBar"),this.checklist=document.getElementById(t+"KeywordChecklist")}return createClass(e,[{key:"calculate",value:function(t,i){var n=this;EntryMarkup$1.update().then(function(r){if(n.ratings=[],n.keyword=t,n.keywordLower=t.toLowerCase(),n.text=r.textContent.replace(/(\r\n|\r|\n|\t+)/gim,""),""===n.text.trim())return n.addRating(SEO_RATING.POOR,SEO_REASONS.noContent),n.renderChecklist(),void i(SEO_RATING.POOR);n.content=r,n.stats=new TextStatistics(n.text),Object.getOwnPropertyNames(e.prototype).filter(function(e){return e.indexOf("judge")>-1}).forEach(function(e){n[e]()}),n.ratingOccurrence={},n.ratings.sort(function(t,i){return e.ratingValue(i.rating)-e.ratingValue(t.rating)}).forEach(function(e){var t=e.rating;n.ratingOccurrence.hasOwnProperty(t)||(n.ratingOccurrence[t]=0),n.ratingOccurrence[t]++});var a=Object.keys(n.ratingOccurrence).reduce(function(e,t){return n.ratingOccurrence[e]>n.ratingOccurrence[t]?e:t});n.renderChecklist(),i(a)}).catch(function(e){console.error(e)})}},{key:"clear",value:function(e){for(var t=this.bar.children.length;t--;){this.bar.children[t].style.transform=""}for(;this.checklist.firstElementChild;)this.checklist.removeChild(this.checklist.firstElementChild);var i=document.createElement("li");i.style.textAlign="center",i.textContent="No keyword selected",this.checklist.appendChild(i),e()}},{key:"renderChecklist",value:function(){for(var e=this,t=this.ratings.length,i=0,n=0;n60?SEO_RATING.POOR:SEO_RATING.GOOD,e<40?SEO_REASONS.titleLengthFailMin.replace("{l}",e):e>60?SEO_REASONS.titleLengthFailMax.replace("{l}",e):SEO_REASONS.titleLengthSuccess)}},{key:"judgeTitleKeyword",value:function(){var e=this.SEO.snippetFields.title.value,t=e.toLowerCase().indexOf(this.keywordLower);if(t>-1)return t<=.3*e.length?void this.addRating(SEO_RATING.GOOD,SEO_REASONS.titleKeywordSuccess):void this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.titleKeywordPosFail);this.addRating(SEO_RATING.POOR,SEO_REASONS.titleKeywordFail)}},{key:"judgeSlug",value:function(){if(this.SEO.snippetFields.slug){var e=this.SEO.snippetFields.slug.textContent.toLowerCase(),t=this.keywordLower.replace(/[^a-zA-Z0-9-_]/g,"");e.indexOf(t)>-1?this.addRating(SEO_RATING.GOOD,SEO_REASONS.slugSuccess):this.addRating(SEO_RATING.POOR,SEO_REASONS.slugFail)}}},{key:"judgeDesc",value:function(){this.SEO.snippetFields.desc.value.toLowerCase().indexOf(this.keywordLower)>-1?this.addRating(SEO_RATING.GOOD,SEO_REASONS.descSuccess):this.addRating(SEO_RATING.POOR,SEO_REASONS.descFail)}},{key:"judgeWordCount",value:function(){var e=this.stats.wordCount();e>300?this.addRating(SEO_RATING.GOOD,SEO_REASONS.wordCountSuccess.replace("{l}",e)):this.addRating(SEO_RATING.POOR,SEO_REASONS.wordCountFail.replace("{l}",e))}},{key:"judgeFirstParagraph",value:function(){var e=this.content.querySelector("p");e&&e.textContent.toLowerCase().indexOf(this.keywordLower)>-1?this.addRating(SEO_RATING.GOOD,SEO_REASONS.firstParagraphSuccess):this.addRating(SEO_RATING.POOR,SEO_REASONS.firstParagraphFail)}},{key:"judgeImages",value:function(){var e=this.content.getElementsByTagName("img");if(e.length){for(var t=0,i=e.length;i--;){var n=e[i].getAttribute("alt");n&&n.toLowerCase().indexOf(this.keywordLower)>-1&&t++}t>=.8*e.length?this.addRating(SEO_RATING.GOOD,SEO_REASONS.imagesSuccess):t>=.5*e.length?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.imagesOk):this.addRating(SEO_RATING.POOR,SEO_REASONS.imagesFail)}}},{key:"judgeLinks",value:function(){var e=this.content.getElementsByTagName("a");if(e.length){for(var t=0;t-1?t++:i++)}t>0?this.addRating(SEO_RATING.GOOD,SEO_REASONS.headingsSuccess):i>0?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.headingsOk):this.addRating(SEO_RATING.POOR,SEO_REASONS.headingsFail)}}},{key:"judgeDensity",value:function(){var e=this.stats.words(),t=~~this.keywordLower.indexOf(" ")?this.keywordLower.split(" "):this.keywordLower,i=countInArray(e,t),n=Array.isArray(t)?t.length:0,r=e.length-n,a=+(100+(i-r)/r*100).toFixed(2);a<1?this.addRating(SEO_RATING.POOR,SEO_REASONS.densityFailUnder.replace("{d}",a)):a<=2.5?this.addRating(SEO_RATING.GOOD,SEO_REASONS.densitySuccess.replace("{d}",a)):a>2.5?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.densityOk.replace("{d}",a).replace("{c}",i)):this.addRating(SEO_RATING.POOR,SEO_REASONS.densityFail)}},{key:"judgeFleschEase",value:function(){var e=this.stats.fleschKincaidReadingEase();e>=80?this.addRating(SEO_RATING.GOOD,SEO_REASONS.fleschSuccess.replace("{l}",e)):e>=60?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.fleschOk.replace("{l}",e)):this.addRating(SEO_RATING.POOR,SEO_REASONS.fleschFail.replace("{l}",e))}},{key:"addRating",value:function(e,t){this.ratings.push({rating:e,reason:t})}}],[{key:"ratingValue",value:function(e){switch(e){case SEO_RATING.POOR:return-1;case SEO_RATING.AVERAGE:return 1;case SEO_RATING.GOOD:return 2;default:return 0}}}]),e}(),FocusKeywords=function(){function e(t,i){var n=this;classCallCheck(this,e),this.activeKeywordIndex=null,this.recalculateKeyword=function(){if(n.stopObserving(),null!==n.activeKeywordIndex){var e=n.keywords[n.activeKeywordIndex];n.keywordsChecklist.calculate(e.keyword,n.onNewRating.bind(n,e.index))}setTimeout(function(){n.startObserving()},1)},this.onKeywordsChange=function(){var e={};n.keywordsField.value=JSON.stringify(n.keywords.map(function(t){var i=t.keyword,n=t.rating;return e.hasOwnProperty(n)||(e[n]=0),e[n]++,{keyword:i,rating:n}})),Object.keys(e).length?n.scoreField.value=Object.keys(e).reduce(function(t,i){return e[t]>e[i]?t:i}):n.scoreField.value=""},this.onNewRating=function(e,t){var i=n.keywords[e];if(i){i.rating=t;var r=n.getKeywordElementAtIndex(e);for(r.removeChild(r.firstElementChild),r.insertBefore(createRating(t,"span"),r.firstChild),n.keywordElem.textContent=i.keyword;n.ratingElem.firstChild;)n.ratingElem.removeChild(n.ratingElem.firstChild);n.ratingElem.appendChild(createRating(t)),n.ratingElem.appendChild(document.createTextNode(SEO_RATING_LABEL[t])),n.onKeywordsChange()}else n.onEmptyRating()},this.onEmptyRating=function(){for(n.keywordElem.innerHTML="No keyword selected";n.ratingElem.firstChild;)n.ratingElem.removeChild(n.ratingElem.firstChild);n.ratingElem.appendChild(createRating("neutral")),n.ratingElem.appendChild(document.createTextNode(SEO_RATING_LABEL.neutral))},this.onKeywordClick=function(e){e.preventDefault(),n.setActiveKeyword(e.target.dataset.index)},this.onKeywordRemoveClick=function(e){e.preventDefault(),e.stopPropagation();var t=0|e.target.parentNode.parentNode.dataset.index,i=n.getKeywordElementAtIndex(t);i.parentNode.removeChild(i),n.keywords.splice(t,1),n.keywords=n.keywords.map(function(e,t){return n.activeKeywordIndex===e.index&&(n.activeKeywordIndex=t),n.getKeywordElementAtIndex(t).setAttribute("data-index",t),_extends({},e,{index:t})}),n.activeKeywordIndex===t&&n.setActiveKeyword(0),n.onKeywordsChange()},this.onInputWrapClick=function(e){e.target===n.inputWrap&&n.input.focus()},this.onInputFocus=function(){n.inputWrap.classList.add("focused")},this.onInputBlur=function(){n.inputWrap.classList.remove("focused")},this.onInputKeyDown=function(e){if(13===e.keyCode){e.preventDefault();var t=e.target.value.trim(),i=!1;if(t){for(var r=n.keywords.length;r--;){var a=n.keywords[r],s=a.keyword,o=a.index;if(t.toLowerCase()===s.toLowerCase()){i=!0,n.setActiveKeyword(o);break}}!i&&n.createKeyword(t),e.target.value=""}}},this.createKeyword=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"neutral",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=null!==i?i:n.keywords.length,a=createElement("a",{href:"#",click:n.onKeywordClick,"data-index":String(r)},[createRating(t,"span"),e,createElement("object",{},createElement("a",{href:"#",title:"Remove",click:n.onKeywordRemoveClick},"Remove"))]);n.inputWrap.insertBefore(a,n.inputWrap.lastElementChild),null===i&&(n.keywords.push({keyword:e,rating:t,index:r}),n.setActiveKeyword(r),n.onKeywordsChange())},this.getKeywordElementAtIndex=function(e){return n.inputWrap.children[e]},this.namespace=t,this.SEO=i,this.keywordsChecklist=new KeywordChecklist(t,i),this.keywordsField=document.getElementById(t+"Keywords"),this.scoreField=document.getElementById(t+"Score"),this.keywordElem=document.getElementById(t+"Keyword"),this.ratingElem=document.getElementById(t+"Rating"),this.initInput(),this.initKeywords(),this.initWatcher()}return createClass(e,[{key:"initInput",value:function(){this.inputWrap=document.getElementById(this.namespace+"KeywordsInput"),this.input=this.inputWrap.lastElementChild,this.inputWrap.addEventListener("click",this.onInputWrapClick),this.input.addEventListener("focus",this.onInputFocus),this.input.addEventListener("blur",this.onInputBlur),this.input.addEventListener("keydown",this.onInputKeyDown)}},{key:"initKeywords",value:function(){var e=this;try{this.keywords=JSON.parse(this.keywordsField.value).map(function(t,i){return e.createKeyword(t.keyword,t.rating,i),_extends({},t,{index:i})})}catch(e){this.keywords=[]}this.keywords.length&&this.setActiveKeyword(0),this.onKeywordsChange()}},{key:"initWatcher",value:function(){this.mo=new MutationObserver(debounce(this.recalculateKeyword,500)),this.startObserving()}},{key:"startObserving",value:function(){if(this.mo){var e=this.keywordsField.form;null===e&&(e=Craft.livePreview.$editor[0]),this.mo.observe(e,{childList:!0,attributes:!0,characterData:!0,subtree:!0})}}},{key:"stopObserving",value:function(){this.mo&&(this.mo.disconnect(),this.mo.takeRecords())}},{key:"setActiveKeyword",value:function(e){!this.keywords.length>e&&(this.activeKeywordIndex=null),this.activeKeywordIndex!==e&&(null!==this.activeKeywordIndex&&this.keywords.length>this.activeKeywordIndex&&this.getKeywordElementAtIndex(this.activeKeywordIndex).classList.remove("active"),this.keywords.length>e?(this.activeKeywordIndex=0|e,this.getKeywordElementAtIndex(this.activeKeywordIndex).classList.add("active"),this.recalculateKeyword()):this.keywordsChecklist.clear(this.onEmptyRating))}}]),e}(),Snippet=function(){function e(t,i){classCallCheck(this,e),this.namespace=t,this.SEO=i,this.titleField=document.getElementById(t+"Title"),this.slugField=document.getElementById(t+"Slug"),this.descField=document.getElementById(t+"Description"),this.SEO.snippetFields={title:this.titleField,slug:this.slugField,desc:this.descField},this.title(),this.slugField&&i.options.hasPreview&&this.slug(),this.desc()}return createClass(e,[{key:"title",value:function(){var e=this,t=document.getElementById("title");if(t&&this.titleField.classList.contains("clean")&&this.SEO.options.isNew||""===this.titleField.value.trim()){var i=this.titleField.value,n=function(){e.SEO.options.suffixAsPrefix?e.titleField.value=i+" "+t.value:e.titleField.value=t.value+" "+i};t.addEventListener("input",n,!1),this.titleField.addEventListener("input",function i(){e.titleField.classList.remove("clean"),t.removeEventListener("input",n,!1),e.titleField.removeEventListener("input",i,!1)},!1),n()}}},{key:"slug",value:function(){var e=this,t=document.getElementById("slug");if(t){var i=function(){e.slugField.textContent=t.value};t.addEventListener("input",i);var n=document.getElementById("title");n&&n.addEventListener("input",debounce(i,500)),i()}}},{key:"desc",value:function(){var e=this,t=function(){setTimeout(function(){e.descField.style.height="",e.descField.style.height=e.descField.scrollHeight+"px"},1)};if(this.descField.addEventListener("keydown",function(e){13===e.keyCode&&e.preventDefault()}),this.descField.addEventListener("input",function(){e.descField.value=e.descField.value.replace(/(\r\n|\r|\n)/gm," "),e.descField.value.length>313?e.descField.classList.add("invalid"):e.descField.classList.remove("invalid"),t()}),document.getElementById("tabs"))for(var i=document.querySelectorAll("#tabs a.tab"),n=0;n0&&void 0!==arguments[0]?arguments[0]:"div",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],n=document.createElement(e),r=!0,a=!1,s=void 0;try{for(var o,l=Object.entries(t)[Symbol.iterator]();!(r=(o=l.next()).done);r=!0){var c=o.value,d=slicedToArray(c,2),h=d[0],u=d[1];u&&((void 0===u?"undefined":_typeof(u))!==_typeof(function(){})?("style"===h&&(u=u.replace(/(?:\r\n|\r|\n|\t|\s+)/g," ").trim()),n.setAttribute(h,u)):"ref"===h?u(n):n.addEventListener(h,u))}}catch(e){a=!0,s=e}finally{try{!r&&l.return&&l.return()}finally{if(a)throw s}}return Array.isArray(i)||(i=[i]),i.map(function(e){if(e)try{n.appendChild(e)}catch(t){n.appendChild(document.createTextNode(e))}}),n}var TextStatistics=function(){function e(t){classCallCheck(this,e),this.text=t?e.cleanText(t):t}return createClass(e,[{key:"fleschKincaidReadingEase",value:function(){return Math.round(10*(206.835-1.015*this.averageWordsPerSentence()-84.6*this.averageSyllablesPerWord()))/10}},{key:"fleschKincaidGradeLevel",value:function(){return Math.round(10*(.39*this.averageWordsPerSentence()+11.8*this.averageSyllablesPerWord()-15.59))/10}},{key:"gunningFogScore",value:function(){return Math.round(.4*(this.averageWordsPerSentence()+this.percentageWordsWithThreeSyllables(!1))*10)/10}},{key:"colemanLiauIndex",value:function(){return Math.round(10*(this.letterCount()/this.wordCount()*5.89-this.sentenceCount()/this.wordCount()*.3-15.8))/10}},{key:"smogIndex",value:function(){return Math.round(1.043*Math.sqrt(this.wordsWithThreeSyllables()*(30/this.sentenceCount())+3.1291)*10)/10}},{key:"automatedReadabilityIndex",value:function(){return Math.round(10*(this.letterCount()/this.wordCount()*4.71+this.wordCount()/this.sentenceCount()*.5-21.43))/10}},{key:"textLength",value:function(){return this.text.length}},{key:"letterCount",value:function(){return this.text.replace(/[^a-z]+/gi,"").length}},{key:"sentenceCount",value:function(){return this.text.replace(/[^.!?]/g,"").length||1}},{key:"wordCount",value:function(){return this.words().length||1}},{key:"words",value:function(){return this._words?this._words:(this._words=this.text.split(/[^a-z0-9']+/i),this._words)}},{key:"averageWordsPerSentence",value:function(){return this.wordCount()/this.sentenceCount()}},{key:"averageSyllablesPerWord",value:function(){var t=0,i=this.wordCount();return this.text.split(/\s+/).forEach(function(i){t+=e.syllableCount(i)}),(t||1)/(i||1)}},{key:"wordsWithThreeSyllables",value:function(){var e=this,t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=0;return t=!1!==t,this.text.split(/\s+/).forEach(function(n){n.match(/^[A-Z]/)&&!t||e.syllableCount(n)>2&&i++}),i}},{key:"percentageWordsWithThreeSyllables",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return this.wordsWithThreeSyllables(e)/this.wordCount()*100}}],[{key:"cleanText",value:function(e){return["li","p","h1","h2","h3","h4","h5","h6","dd"].forEach(function(t){e.replace("",".")}),e=e.replace(/<[^>]+>/g,"").replace(/[,:;()-]/g," ").replace(/[.!?]/g,".").replace(/^\s+/g,"").replace(/[ ]*(\n|\r\n|\r)[ ]*/g," ").replace(/([.])[. ]+/g,".").replace(/[ ]*([.])/g,". ").replace(/\s+/g," ").replace(/\s+$/g,"").replace(/'/g,""),e+="."}},{key:"syllableCount",value:function(e){var t=0,i=0;e=e.toLowerCase().replace(/[^a-z]/g,"");var n={simile:3,forever:3,shoreline:2};if(n.hasOwnProperty(e))return n[e];[/^un/,/^fore/,/ly$/,/less$/,/ful$/,/ers?$/,/ings?$/].forEach(function(t){e.match(t)&&(e=e.replace(t,""),i++)});var r=e.split(/[^aeiouy]+/gi).filter(function(e){return!!e.replace(/\s+/gi,"").length}).length;return t=r+i,[/cial/,/tia/,/cius/,/cious/,/giu/,/ion/,/iou/,/sia$/,/[^aeiuoyt]{2,}ed$/,/.ely$/,/[cg]h?e[rsd]?$/,/rved?$/,/[aeiouy][dt]es?$/,/[aeiouy][^aeiouydt]e[rsd]?$/,/^[dr]e[aeiou][^aeiou]+$/,/[aeiouy]rse$/].forEach(function(i){e.match(i)&&t--}),[/ia/,/riet/,/dien/,/iu/,/io/,/ii/,/[aeiouym]bl$/,/[aeiou]{3}/,/^mc/,/ism$/,/([^aeiouy])\1l$/,/[^l]lien/,/^coa[dglx]./,/[^gq]ua[^auieo]/,/dnt$/,/uity$/,/ie(r|st)$/].forEach(function(i){e.match(i)&&t++}),t||1}}]),e}();function fail(e){Craft.cp.displayError("SEO: "+e),window.console&&console.error.apply(console,["%cSEO: %c "+e,"font-weight: bold;","font-weight: normal;"])}function debounce(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:300,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=void 0;return 0===t?function(){e.apply(this,arguments)}:function(){var r=this,a=arguments;"SyntheticEvent"===a[0].constructor.name&&a[0].persist();var s=i&&!n;clearTimeout(n),n=setTimeout(function(){n=null,i||e.apply(r,a)},t),s&&e.apply(r,a)}}function capitalize(e){return e[0].toUpperCase()+e.slice(1)}function isExternalUrl(e){var t=function(e){var t=/https?:\/\/((?:[\w\d-]+\.)+[\w\d]{2,})/i.exec(e);return null!==t&&t[1]};return t(location.href)===t(e)}function countInArray(e,t){var i=0,n=e.length;if(Array.isArray(t))for(var r=e.length,a=t.length;n--;){for(var s=a,o=0;s--&&n+s1&&void 0!==arguments[1]?arguments[1]:"div",i=capitalize(e);return createElement(t,{class:"seo--light "+e,title:i},i)}var EntryMarkup=function(){function e(){classCallCheck(this,e),this.frame=null,this.postData=null,this.clean()}return createClass(e,[{key:"update",value:function(){var e=this;return new Promise(function(t,i){var n=Garnish.getPostData(document.getElementById("main-form"));e.postData&&Craft.compare(n,e.postData)?t(e.frame.contentWindow.document.body):(e.postData=n,$.ajax({url:Craft.livePreview.previewUrl,method:"POST",data:$.extend({},n,Craft.livePreview.basePostData),xhrFields:{withCredentials:!0},crossDomain:!0,success:function(n){n=(n=n.replace(//g,"")).replace(//g,""),e.frame.contentWindow.document.open(),e.frame.contentWindow.document.write(n),e.frame.contentWindow.document.close(),e.frame.contentWindow.document.body?t(e.frame.contentWindow.document.body):(fail("Failed to parse entry preview"),i())},error:function(){fail("Failed to retrieve entry preview"),i()}}))})}},{key:"clean",value:function(){this.frame&&document.body.removeChild(this.frame),this.frame=createElement("iframe",{frameborder:"0",style:"\n\t\t\t\twidth: 0;\n\t\t\t\theight: 0;\n\t\t\t"}),document.body.appendChild(this.frame)}}]),e}(),EntryMarkup$1=new EntryMarkup,SEO_RATING={NONE:"neutral",GOOD:"good",AVERAGE:"average",POOR:"poor"},SEO_RATING_LABEL={neutral:"Not yet rated",good:"Looks good",average:"Room for improvement",poor:"Needs work"},SEO_REASONS={noContent:"You don't have any content, adding some would be a good start!",titleLengthFailMin:"The title contains {l} characters which is less than the recommended minimum of 40 characters.",titleLengthFailMax:"The title contains {l} characters which is greater than the recommended maximum of 60 characters.",titleLengthSuccess:"The title is between the recommended minimum and maximum length.",titleKeywordFail:"The title does not contain the keyword. Try adding it near the beginning of the title.",titleKeywordSuccess:"The title contains the keyword near the beginning.",titleKeywordPosFail:"The title contains the keyword, but not near the beginning. Try to move it closer to the start of the title.",slugFail:"The URL does not contain the keyword. Try adding it to the slug.",slugSuccess:"The URL contains the keyword.",descFail:"The description does not contain the keyword. Try adding it near the beginning of the description.",descSuccess:"The description contains the keyword.",wordCountFail:"Your text contains {l} words, this is less than the recommended 300 word minimum.",wordCountSuccess:"Your text contains {l} words, this is more than the recommended 300 word minimum.",firstParagraphFail:"The keyword does not appear in the first paragraph of your text. Try adding it.",firstParagraphSuccess:"The keyword appears in the first paragraph of your text.",imagesFail:"Less than half of the images have alt tags containing the keyword, try adding it to more images.",imagesOk:"Half or more of the images have alt tags containing the keyword. To improve this, try adding keywords to all the images alt tags.",imagesSuccess:"All of the images have alt tags containing the keyword.",linksFail:"The page does not contain any outgoing links. Try adding some.",linksSuccess:"The page contains outgoing links.",headingsFail:"The page does not contain any headings that contain the keyword. Try adding some with the keyword.",headingsOk:"The page contains some lower importance headings that contain the keyword. Try adding the keyword to some h2's.",headingsSuccess:"The page contains higher importance headings with the keyword.",densityFail:"The keyword does not appear in the text. It is important to include it in your content.",densityFailUnder:"The keyword density is {d}%, which is well under the advised 2.5%. Try increasing the number of times the keyword is used.",densityOk:"The keyword density is {d}%, which is over the advised 2.5%. The keyword appears {c} times.",densitySuccess:"The keyword density is {d}%, which is near the advised 2.5%.",fleschFail:"The Flesch Reading ease score is {l} which is considered best for university graduates. Try reducing your sentence length to improve readability.",fleschOk:"The Flesch Reading ease score is {l} which is average, and considered easily readable by most users.",fleschSuccess:"The Flesch Reading ease score is {l} meaning your content is readable by all ages."},KeywordChecklist=function(){function e(t,i){classCallCheck(this,e),this.keyword=null,this.keywordLower=null,this.ratings=[],this.ratingOccurrence={},this.content=null,this.text=null,this.stats=null,this.renderChecklistItem=function(e){var t=e.rating,i=e.reason;return createElement("li",{},[createRating(t),createElement("p",{},i)])},this.namespace=t,this.SEO=i,this.bar=document.getElementById(t+"KeywordBar"),this.checklist=document.getElementById(t+"KeywordChecklist")}return createClass(e,[{key:"calculate",value:function(t,i){var n=this;EntryMarkup$1.update().then(function(r){if(n.ratings=[],n.keyword=t,n.keywordLower=t.toLowerCase(),n.text=r.textContent.replace(/(\r\n|\r|\n|\t+)/gim,""),""===n.text.trim())return n.addRating(SEO_RATING.POOR,SEO_REASONS.noContent),n.renderChecklist(),void i(SEO_RATING.POOR);n.content=r,n.stats=new TextStatistics(n.text),Object.getOwnPropertyNames(e.prototype).filter(function(e){return e.indexOf("judge")>-1}).forEach(function(e){n[e]()}),n.ratingOccurrence={},n.ratings.sort(function(t,i){return e.ratingValue(i.rating)-e.ratingValue(t.rating)}).forEach(function(e){var t=e.rating;n.ratingOccurrence.hasOwnProperty(t)||(n.ratingOccurrence[t]=0),n.ratingOccurrence[t]++});var a=Object.keys(n.ratingOccurrence).reduce(function(e,t){return n.ratingOccurrence[e]>n.ratingOccurrence[t]?e:t});n.renderChecklist(),i(a)}).catch(function(e){console.error(e)})}},{key:"clear",value:function(e){for(var t=this.bar.children.length;t--;){this.bar.children[t].style.transform=""}for(;this.checklist.firstElementChild;)this.checklist.removeChild(this.checklist.firstElementChild);var i=document.createElement("li");i.style.textAlign="center",i.textContent="No keyword selected",this.checklist.appendChild(i),e()}},{key:"renderChecklist",value:function(){for(var e=this,t=this.ratings.length,i=0,n=0;n60?SEO_RATING.POOR:SEO_RATING.GOOD,e<40?SEO_REASONS.titleLengthFailMin.replace("{l}",e):e>60?SEO_REASONS.titleLengthFailMax.replace("{l}",e):SEO_REASONS.titleLengthSuccess)}},{key:"judgeTitleKeyword",value:function(){var e=this.SEO.snippetFields.title.value,t=e.toLowerCase().indexOf(this.keywordLower);if(t>-1)return t<=.3*e.length?void this.addRating(SEO_RATING.GOOD,SEO_REASONS.titleKeywordSuccess):void this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.titleKeywordPosFail);this.addRating(SEO_RATING.POOR,SEO_REASONS.titleKeywordFail)}},{key:"judgeSlug",value:function(){if(this.SEO.snippetFields.slug){var e=this.SEO.snippetFields.slug.textContent.toLowerCase(),t=this.keywordLower.replace(/[^a-zA-Z0-9-_]/g,"");e.indexOf(t)>-1?this.addRating(SEO_RATING.GOOD,SEO_REASONS.slugSuccess):this.addRating(SEO_RATING.POOR,SEO_REASONS.slugFail)}}},{key:"judgeDesc",value:function(){this.SEO.snippetFields.desc.value.toLowerCase().indexOf(this.keywordLower)>-1?this.addRating(SEO_RATING.GOOD,SEO_REASONS.descSuccess):this.addRating(SEO_RATING.POOR,SEO_REASONS.descFail)}},{key:"judgeWordCount",value:function(){var e=this.stats.wordCount();e>300?this.addRating(SEO_RATING.GOOD,SEO_REASONS.wordCountSuccess.replace("{l}",e)):this.addRating(SEO_RATING.POOR,SEO_REASONS.wordCountFail.replace("{l}",e))}},{key:"judgeFirstParagraph",value:function(){var e=this.content.querySelector("p");e&&e.textContent.toLowerCase().indexOf(this.keywordLower)>-1?this.addRating(SEO_RATING.GOOD,SEO_REASONS.firstParagraphSuccess):this.addRating(SEO_RATING.POOR,SEO_REASONS.firstParagraphFail)}},{key:"judgeImages",value:function(){var e=this.content.getElementsByTagName("img");if(e.length){for(var t=0,i=e.length;i--;){var n=e[i].getAttribute("alt");n&&n.toLowerCase().indexOf(this.keywordLower)>-1&&t++}t>=.8*e.length?this.addRating(SEO_RATING.GOOD,SEO_REASONS.imagesSuccess):t>=.5*e.length?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.imagesOk):this.addRating(SEO_RATING.POOR,SEO_REASONS.imagesFail)}}},{key:"judgeLinks",value:function(){var e=this.content.getElementsByTagName("a");if(e.length){for(var t=0;t-1?t++:i++)}t>0?this.addRating(SEO_RATING.GOOD,SEO_REASONS.headingsSuccess):i>0?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.headingsOk):this.addRating(SEO_RATING.POOR,SEO_REASONS.headingsFail)}}},{key:"judgeDensity",value:function(){var e=this.stats.words(),t=~~this.keywordLower.indexOf(" ")?this.keywordLower.split(" "):this.keywordLower,i=countInArray(e,t),n=Array.isArray(t)?t.length:0,r=e.length-n,a=+(100+(i-r)/r*100).toFixed(2);a<1?this.addRating(SEO_RATING.POOR,SEO_REASONS.densityFailUnder.replace("{d}",a)):a<=2.5?this.addRating(SEO_RATING.GOOD,SEO_REASONS.densitySuccess.replace("{d}",a)):a>2.5?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.densityOk.replace("{d}",a).replace("{c}",i)):this.addRating(SEO_RATING.POOR,SEO_REASONS.densityFail)}},{key:"judgeFleschEase",value:function(){var e=this.stats.fleschKincaidReadingEase();e>=80?this.addRating(SEO_RATING.GOOD,SEO_REASONS.fleschSuccess.replace("{l}",e)):e>=60?this.addRating(SEO_RATING.AVERAGE,SEO_REASONS.fleschOk.replace("{l}",e)):this.addRating(SEO_RATING.POOR,SEO_REASONS.fleschFail.replace("{l}",e))}},{key:"addRating",value:function(e,t){this.ratings.push({rating:e,reason:t})}}],[{key:"ratingValue",value:function(e){switch(e){case SEO_RATING.POOR:return-1;case SEO_RATING.AVERAGE:return 1;case SEO_RATING.GOOD:return 2;default:return 0}}}]),e}(),FocusKeywords=function(){function e(t,i){var n=this;classCallCheck(this,e),this.activeKeywordIndex=null,this.recalculateKeyword=function(){if(n.stopObserving(),null!==n.activeKeywordIndex){var e=n.keywords[n.activeKeywordIndex];n.keywordsChecklist.calculate(e.keyword,n.onNewRating.bind(n,e.index))}setTimeout(function(){n.startObserving()},1)},this.onKeywordsChange=function(){var e={};n.keywordsField.value=JSON.stringify(n.keywords.map(function(t){var i=t.keyword,n=t.rating;return e.hasOwnProperty(n)||(e[n]=0),e[n]++,{keyword:i,rating:n}})),Object.keys(e).length?n.scoreField.value=Object.keys(e).reduce(function(t,i){return e[t]>e[i]?t:i}):n.scoreField.value=""},this.onNewRating=function(e,t){var i=n.keywords[e];if(i){i.rating=t;var r=n.getKeywordElementAtIndex(e);for(r.removeChild(r.firstElementChild),r.insertBefore(createRating(t,"span"),r.firstChild),n.keywordElem.textContent=i.keyword;n.ratingElem.firstChild;)n.ratingElem.removeChild(n.ratingElem.firstChild);n.ratingElem.appendChild(createRating(t)),n.ratingElem.appendChild(document.createTextNode(SEO_RATING_LABEL[t])),n.onKeywordsChange()}else n.onEmptyRating()},this.onEmptyRating=function(){for(n.keywordElem.innerHTML="No keyword selected";n.ratingElem.firstChild;)n.ratingElem.removeChild(n.ratingElem.firstChild);n.ratingElem.appendChild(createRating("neutral")),n.ratingElem.appendChild(document.createTextNode(SEO_RATING_LABEL.neutral))},this.onKeywordClick=function(e){e.preventDefault(),n.setActiveKeyword(e.target.dataset.index)},this.onKeywordRemoveClick=function(e){e.preventDefault(),e.stopPropagation();var t=0|e.target.parentNode.parentNode.dataset.index,i=n.getKeywordElementAtIndex(t);i.parentNode.removeChild(i),n.keywords.splice(t,1),n.keywords=n.keywords.map(function(e,t){return n.activeKeywordIndex===e.index&&(n.activeKeywordIndex=t),n.getKeywordElementAtIndex(t).setAttribute("data-index",t),_extends({},e,{index:t})}),n.activeKeywordIndex===t&&n.setActiveKeyword(0),n.onKeywordsChange()},this.onInputWrapClick=function(e){e.target===n.inputWrap&&n.input.focus()},this.onInputFocus=function(){n.inputWrap.classList.add("focused")},this.onInputBlur=function(e){n.inputWrap.classList.remove("focused"),""!==e.target.value.trim()&&n.onInputKeyDown({target:e.target,keyCode:13,preventDefault:function(){}})},this.onInputKeyDown=function(e){if(13===e.keyCode){e.preventDefault();var t=e.target.value.trim(),i=!1;if(t){for(var r=n.keywords.length;r--;){var a=n.keywords[r],s=a.keyword,o=a.index;if(t.toLowerCase()===s.toLowerCase()){i=!0,n.setActiveKeyword(o);break}}!i&&n.createKeyword(t),e.target.value=""}}},this.createKeyword=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"neutral",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=null!==i?i:n.keywords.length,a=createElement("a",{href:"#",click:n.onKeywordClick,"data-index":String(r)},[createRating(t,"span"),e,createElement("object",{},createElement("a",{href:"#",title:"Remove",click:n.onKeywordRemoveClick},"Remove"))]);n.inputWrap.insertBefore(a,n.inputWrap.lastElementChild),null===i&&(n.keywords.push({keyword:e,rating:t,index:r}),n.setActiveKeyword(r),n.onKeywordsChange())},this.getKeywordElementAtIndex=function(e){return n.inputWrap.children[e]},this.namespace=t,this.SEO=i,this.keywordsChecklist=new KeywordChecklist(t,i),this.keywordsField=document.getElementById(t+"Keywords"),this.scoreField=document.getElementById(t+"Score"),this.keywordElem=document.getElementById(t+"Keyword"),this.ratingElem=document.getElementById(t+"Rating"),this.initInput(),this.initKeywords(),this.initWatcher()}return createClass(e,[{key:"initInput",value:function(){this.inputWrap=document.getElementById(this.namespace+"KeywordsInput"),this.input=this.inputWrap.lastElementChild,this.inputWrap.addEventListener("click",this.onInputWrapClick),this.input.addEventListener("focus",this.onInputFocus),this.input.addEventListener("blur",this.onInputBlur),this.input.addEventListener("keydown",this.onInputKeyDown)}},{key:"initKeywords",value:function(){var e=this;try{this.keywords=JSON.parse(this.keywordsField.value).map(function(t,i){return e.createKeyword(t.keyword,t.rating,i),_extends({},t,{index:i})})}catch(e){this.keywords=[]}this.keywords.length&&this.setActiveKeyword(0),this.onKeywordsChange()}},{key:"initWatcher",value:function(){this.mo=new MutationObserver(debounce(this.recalculateKeyword,500)),this.startObserving()}},{key:"startObserving",value:function(){if(this.mo){var e=this.keywordsField.form;null===e&&(e=Craft.livePreview.$editor[0]),this.mo.observe(e,{childList:!0,attributes:!0,characterData:!0,subtree:!0})}}},{key:"stopObserving",value:function(){this.mo&&(this.mo.disconnect(),this.mo.takeRecords())}},{key:"setActiveKeyword",value:function(e){!this.keywords.length>e&&(this.activeKeywordIndex=null),this.activeKeywordIndex!==e&&(null!==this.activeKeywordIndex&&this.keywords.length>this.activeKeywordIndex&&this.getKeywordElementAtIndex(this.activeKeywordIndex).classList.remove("active"),this.keywords.length>e?(this.activeKeywordIndex=0|e,this.getKeywordElementAtIndex(this.activeKeywordIndex).classList.add("active"),this.recalculateKeyword()):this.keywordsChecklist.clear(this.onEmptyRating))}}]),e}(),Snippet=function(){function e(t,i){classCallCheck(this,e),this.namespace=t,this.SEO=i,this.titleField=document.getElementById(t+"Title"),this.slugField=document.getElementById(t+"Slug"),this.descField=document.getElementById(t+"Description"),this.SEO.snippetFields={title:this.titleField,slug:this.slugField,desc:this.descField},this.title(),this.slugField&&i.options.hasPreview&&this.slug(),this.desc()}return createClass(e,[{key:"title",value:function(){var e=this,t=document.getElementById("title");if(t&&this.titleField.classList.contains("clean")&&this.SEO.options.isNew||""===this.titleField.value.trim()){var i=this.titleField.value,n=function(){e.SEO.options.suffixAsPrefix?e.titleField.value=i+" "+t.value:e.titleField.value=t.value+" "+i};t.addEventListener("input",n,!1),this.titleField.addEventListener("input",function i(){e.titleField.classList.remove("clean"),t.removeEventListener("input",n,!1),e.titleField.removeEventListener("input",i,!1)},!1),n()}}},{key:"slug",value:function(){var e=this,t=document.getElementById("slug");if(t){var i=function(){e.slugField.textContent=t.value};t.addEventListener("input",i);var n=document.getElementById("title");n&&n.addEventListener("input",debounce(i,500)),i()}}},{key:"desc",value:function(){var e=this,t=function(){setTimeout(function(){e.descField.style.height="",e.descField.style.height=e.descField.scrollHeight+"px"},1)};if(this.descField.addEventListener("keydown",function(e){13===e.keyCode&&e.preventDefault()}),this.descField.addEventListener("input",function(){e.descField.value=e.descField.value.replace(/(\r\n|\r|\n)/gm," "),e.descField.value.length>313?e.descField.classList.add("invalid"):e.descField.classList.remove("invalid"),t()}),document.getElementById("tabs"))for(var i=document.querySelectorAll("#tabs a.tab"),n=0;n {\n\t\t\ta[b.dataset.seoTab] = b;\n\t\t\treturn a;\n\t\t}, {});\n\t\t\n\t\t// Set default active\n\t\tthis.setActiveTab(tabs[0]);\n\t\t\n\t\t// Events\n\t\ttabs.forEach(tab => {\n\t\t\ttab.addEventListener('click', e => {\n\t\t\t\te.preventDefault();\n\t\t\t\tthis.setActiveTab(tab);\n\t\t\t});\n\t\t});\n\t}\n\t\n\t// Misc\n\t// =========================================================================\n\t\n\tsetActiveTab (tab) {\n\t\tconst name = tab.dataset.seoTab;\n\t\t\n\t\tif (this.activeTab.tab) {\n\t\t\tthis.activeTab.tab.classList.remove('active');\n\t\t\tthis.activeTab.page.classList.remove('active');\n\t\t}\n\t\t\n\t\tthis.activeTab = {\n\t\t\ttab,\n\t\t\tpage: this.pages[name],\n\t\t};\n\t\t\n\t\tthis.activeTab.tab.classList.add('active');\n\t\tthis.activeTab.page.classList.add('active');\n\t}\n\t\n}\n","/**\n * ## Create Element\n * Quick and easy DOM element creation\n *\n * @param {string=} tag - The element tag\n * @param {object=} attributes - The attributes to add, mapping the key as\n * the attribute name, and the value as its value. If the value is a\n * function, it will be added as an event.\n * @param {(Array|*)=} children - An array of children (can be a mixture of\n * Nodes to append, or other values to be stringified and appended\n * as text).\n * @return {Element} - The created element\n */\nexport default function createElement (\n\ttag = 'div',\n\tattributes = {},\n\tchildren = []\n) {\n\tconst elem = document.createElement(tag);\n\t\n\tfor (let [key, value] of Object.entries(attributes)) {\n\t\tif (!value) continue;\n\t\t\n\t\tif (typeof value === typeof (() => {})) {\n\t\t\tif (key === 'ref') value(elem);\n\t\t\telse elem.addEventListener(key, value);\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tif (key === 'style')\n\t\t\tvalue = value.replace(/(?:\\r\\n|\\r|\\n|\\t|\\s+)/g, ' ').trim();\n\t\t\n\t\telem.setAttribute(key, value);\n\t}\n\t\n\tif (!Array.isArray(children))\n\t\tchildren = [children];\n\t\n\tchildren.map(child => {\n\t\tif (!child) return;\n\t\t\n\t\ttry {\n\t\t\telem.appendChild(child);\n\t\t} catch (_) {\n\t\t\telem.appendChild(document.createTextNode(child));\n\t\t}\n\t});\n\t\n\treturn elem;\n}","/**\n * TextStatistics.js\n * Christopher Giffard (2012)\n * 1:1 API Fork of TextStatistics.php by Dave Child (Thanks mate!)\n * https://github.com/DaveChild/Text-Statistics\n *\n * Modified & re-written for ES6 by Tam\n */\n\nexport default class TextStatistics {\n\t\n\tconstructor (text) {\n\t\tthis.text = text ? TextStatistics.cleanText(text) : text;\n\t}\n\t\n\t// Statistics\n\t// =========================================================================\n\t\n\t/**\n\t * Calculates the Flesch Kincaid reading ease score\n\t *\n\t * @return {number}\n\t */\n\tfleschKincaidReadingEase () {\n\t\treturn Math.round(\n\t\t\t(\n\t\t\t\t206.835\n\t\t\t\t- (1.015 * this.averageWordsPerSentence())\n\t\t\t\t- (84.6 * this.averageSyllablesPerWord())\n\t\t\t) * 10\n\t\t) / 10;\n\t}\n\t\n\t/**\n\t * Calculates the Flesch Kincaid grade level\n\t *\n\t * @return {number}\n\t */\n\tfleschKincaidGradeLevel () {\n\t\treturn Math.round(\n\t\t\t(\n\t\t\t\t(0.39 * this.averageWordsPerSentence())\n\t\t\t\t+ (11.8 * this.averageSyllablesPerWord())\n\t\t\t\t- 15.59\n\t\t\t) * 10\n\t\t) / 10;\n\t}\n\t\n\t/**\n\t * Calculates the Gunning Fog score\n\t *\n\t * @return {number}\n\t */\n\tgunningFogScore () {\n\t\treturn Math.round(\n\t\t\t(\n\t\t\t\t(\n\t\t\t\t\tthis.averageWordsPerSentence()\n\t\t\t\t\t+ this.percentageWordsWithThreeSyllables(false)\n\t\t\t\t) * 0.4\n\t\t\t) * 10\n\t\t) / 10;\n\t}\n\t\n\t/**\n\t * Calculates the Coleman Liau index\n\t *\n\t * @return {number}\n\t */\n\tcolemanLiauIndex () {\n\t\treturn Math.round(\n\t\t\t(\n\t\t\t\t(\n\t\t\t\t\t5.89\n\t\t\t\t\t* (this.letterCount() / this.wordCount())\n\t\t\t\t) - (\n\t\t\t\t\t0.3\n\t\t\t\t\t* (this.sentenceCount() / this.wordCount())\n\t\t\t\t) - 15.8\n\t\t\t) * 10\n\t\t) / 10;\n\t}\n\t\n\t/**\n\t * Calculates the Smog index\n\t *\n\t * @return {number}\n\t */\n\tsmogIndex () {\n\t\treturn Math.round(\n\t\t\t1.043\n\t\t\t* Math.sqrt(\n\t\t\t\t(\n\t\t\t\t\tthis.wordsWithThreeSyllables()\n\t\t\t\t\t* (30 / this.sentenceCount())\n\t\t\t\t) + 3.1291\n\t\t\t) * 10\n\t\t) / 10;\n\t}\n\t\n\t/**\n\t * Calculates the Automated Readability index\n\t *\n\t * @return {number}\n\t */\n\tautomatedReadabilityIndex () {\n\t\treturn Math.round(\n\t\t\t(\n\t\t\t\t(\n\t\t\t\t\t4.71\n\t\t\t\t\t* (this.letterCount() / this.wordCount())\n\t\t\t\t) + (\n\t\t\t\t\t0.5\n\t\t\t\t\t* (this.wordCount() / this.sentenceCount())\n\t\t\t\t) - 21.43\n\t\t\t) * 10\n\t\t) / 10;\n\t}\n\t\n\t// Helpers\n\t// =========================================================================\n\t\n\t/**\n\t * Cleans the text for processing\n\t *\n\t * @param {string} text - Text to clean\n\t * @return {string}\n\t */\n\tstatic cleanText (text) {\n\t\t// All these tags should be preceded by a full stop\n\t\t['li', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dd'].forEach(tag => {\n\t\t\ttext.replace(``, \".\");\n\t\t});\n\t\t\n\t\ttext = text\n\t\t\t\t// Strip tags\n\t\t\t\t.replace(/<[^>]+>/g, '')\n\t\t\t\t\n\t\t\t\t// Replace commas, hyphens etc (count them as spaces)\n\t\t\t\t.replace(/[,:;()-]/g, ' ')\n\t\t\t\t\n\t\t\t\t// Unify terminators\n\t\t .replace(/[.!?]/g, '.')\n\t\t\t\t\n\t\t\t\t// Strip leading whitespace\n\t\t\t\t.replace(/^\\s+/g, '')\n\t\t\t\t\n\t\t\t\t// Replace new lines with spaces\n\t\t\t\t.replace(/[ ]*(\\n|\\r\\n|\\r)[ ]*/g, ' ')\n\t\t\t\t\n\t\t\t\t// Check for duplicated terminators\n\t\t\t\t.replace(/([.])[. ]+/g, '.')\n\t\t\t\t\n\t\t\t\t// Pad sentence terminators\n\t\t\t\t.replace(/[ ]*([.])/g, '. ')\n\t\t\t\t\n\t\t\t\t// Remove multiple spaces\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t\n\t\t\t\t// Strip trailing whitespace\n\t\t\t\t.replace(/\\s+$/g, '')\n\t\t\t\t\n\t\t\t\t// Strip apostrophe\n\t\t\t\t.replace(/'/g, '');\n\t\t\n\t\t// Add final terminator, just in case it's missing\n\t\ttext += \".\";\n\t\t\n\t\treturn text;\n\t}\n\t\n\t/**\n\t * Counts the number of syllables a given word\n\t *\n\t * @param {string} word\n\t * @return {number}\n\t */\n\tstatic syllableCount (word) {\n\t\tlet syllableCount = 0,\n\t\t\tprefixSuffixCount = 0;\n\t\t\n\t\t// Prepare word - make lower case & remove non-word characters\n\t\tword = word.toLowerCase().replace(/[^a-z]/g, '');\n\t\t\n\t\t// Specific common exceptions that don't follow the rule set below are\n\t\t// handled individually.\n\t\t// Array of problem words (with word as key, syllable count as value)\n\t\tconst problemWords = {\n\t\t\t'simile': 3,\n\t\t\t'forever': 3,\n\t\t\t'shoreline': 2,\n\t\t};\n\t\t\n\t\t// Return if we've hit a problem word\n\t\tif (problemWords.hasOwnProperty(word)) return problemWords[word];\n\t\t\n\t\t// Syllables that would be counted as two, but should be one\n\t\tconst subSyllables = [\n\t\t\t/cial/,\n\t\t\t/tia/,\n\t\t\t/cius/,\n\t\t\t/cious/,\n\t\t\t/giu/,\n\t\t\t/ion/,\n\t\t\t/iou/,\n\t\t\t/sia$/,\n\t\t\t/[^aeiuoyt]{2,}ed$/,\n\t\t\t/.ely$/,\n\t\t\t/[cg]h?e[rsd]?$/,\n\t\t\t/rved?$/,\n\t\t\t/[aeiouy][dt]es?$/,\n\t\t\t/[aeiouy][^aeiouydt]e[rsd]?$/,\n\t\t\t/^[dr]e[aeiou][^aeiou]+$/, // Sorts out deal, deign etc.\n\t\t\t/[aeiouy]rse$/, // Purse, hearse\n\t\t];\n\t\t\n\t\t// Syllables that would be counted as one, but should be two\n\t\tconst addSyllables = [\n\t\t\t/ia/,\n\t\t\t/riet/,\n\t\t\t/dien/,\n\t\t\t/iu/,\n\t\t\t/io/,\n\t\t\t/ii/,\n\t\t\t/[aeiouym]bl$/,\n\t\t\t/[aeiou]{3}/,\n\t\t\t/^mc/,\n\t\t\t/ism$/,\n\t\t\t/([^aeiouy])\\1l$/,\n\t\t\t/[^l]lien/,\n\t\t\t/^coa[dglx]./,\n\t\t\t/[^gq]ua[^auieo]/,\n\t\t\t/dnt$/,\n\t\t\t/uity$/,\n\t\t\t/ie(r|st)$/\n\t\t];\n\t\t\n\t\t// Single syllable prefixes & suffixes\n\t\tconst prefixSuffix = [\n\t\t\t/^un/,\n\t\t\t/^fore/,\n\t\t\t/ly$/,\n\t\t\t/less$/,\n\t\t\t/ful$/,\n\t\t\t/ers?$/,\n\t\t\t/ings?$/\n\t\t];\n\t\t\n\t\t// Remove prefixes & suffixes, and count how many were takes\n\t\tprefixSuffix.forEach(regex => {\n\t\t\tif (word.match(regex)) {\n\t\t\t\tword = word.replace(regex, '');\n\t\t\t\tprefixSuffixCount++;\n\t\t\t}\n\t\t});\n\t\t\n\t\tlet wordPartCount = word\n\t\t\t.split(/[^aeiouy]+/ig)\n\t\t\t.filter(wordPart => !!wordPart.replace(/\\s+/ig, '').length)\n\t\t\t.length;\n\t\t\n\t\t// Get preliminary syllable count\n\t\tsyllableCount = wordPartCount + prefixSuffixCount;\n\t\t\n\t\t// Some syllables do not follow normal rules, check for them\n\t\tsubSyllables.forEach(syllable => {\n\t\t\tword.match(syllable) && syllableCount--;\n\t\t});\n\t\t\n\t\taddSyllables.forEach(syllable => {\n\t\t\tword.match(syllable) && syllableCount++;\n\t\t});\n\t\t\n\t\treturn syllableCount || 1;\n\t}\n\t\n\t/**\n\t * Returns the length of the text\n\t *\n\t * @return {Number}\n\t */\n\ttextLength () {\n\t\treturn this.text.length;\n\t}\n\t\n\t/**\n\t * Counts the number of letters in the text\n\t *\n\t * @return {Number}\n\t */\n\tletterCount () {\n\t\treturn this.text.replace(/[^a-z]+/ig, '').length;\n\t}\n\t\n\t/**\n\t * Counts the number of sentences in the text\n\t *\n\t * @return {Number|number}\n\t */\n\tsentenceCount () {\n\t\t// FIXME: This will be tripped up by \"Mr.\" or \"U.K.\"\n\t\treturn this.text.replace(/[^.!?]/g, '').length || 1;\n\t}\n\t\n\t/**\n\t * Counts the number of words in the text\n\t *\n\t * @return {number}\n\t */\n\twordCount () {\n\t\treturn this.words().length || 1;\n\t}\n\t\n\t/**\n\t * Splits the text into an array of words.\n\t *\n\t * @return {Array}\n\t */\n\twords () {\n\t\tif (this._words) return this._words;\n\t\tthis._words = this.text.split(/[^a-z0-9']+/i);\n\t\treturn this._words;\n\t}\n\t\n\t/**\n\t * Calculates the average number of words per sentence\n\t *\n\t * @return {number}\n\t */\n\taverageWordsPerSentence () {\n\t\treturn this.wordCount() / this.sentenceCount();\n\t}\n\t\n\t/**\n\t * Calculates the average number of syllables per word\n\t *\n\t * @return {number}\n\t */\n\taverageSyllablesPerWord () {\n\t\tlet syllableCount = 0,\n\t\t\twordCount = this.wordCount();\n\t\t\n\t\tthis.text.split(/\\s+/).forEach(word => {\n\t\t\tsyllableCount += TextStatistics.syllableCount(word);\n\t\t});\n\t\t\n\t\treturn (syllableCount || 1) / (wordCount || 1);\n\t}\n\t\n\t/**\n\t * Counts the number of words in the text w/ three syllables\n\t *\n\t * @param {boolean} countProperNouns - If true, will ignore proper nouns or\n\t * capitalized words.\n\t * @return {number}\n\t */\n\twordsWithThreeSyllables (countProperNouns = true) {\n\t\tlet longWordCount = 0;\n\t\t\n\t\tcountProperNouns = countProperNouns !== false;\n\t\t\n\t\tthis.text.split(/\\s+/).forEach(word => {\n\t\t\t// We don't count proper nouns or capitalized words if the\n\t\t\t// `countProperNouns` argument is set (defaults to true).\n\t\t\tif (!word.match(/^[A-Z]/) || countProperNouns) {\n\t\t\t\tif (this.syllableCount(word) > 2) longWordCount++;\n\t\t\t}\n\t\t});\n\t\t\n\t\treturn longWordCount;\n\t}\n\t\n\t/**\n\t * Calculates the percentage of words with three syllables\n\t *\n\t * @param {boolean} countProperNouns - If true, will ignore proper nouns or\n\t * capitalized words.\n\t * @return {number}\n\t */\n\tpercentageWordsWithThreeSyllables (countProperNouns = true) {\n\t\treturn (\n\t\t\tthis.wordsWithThreeSyllables(countProperNouns) / this.wordCount()\n ) * 100;\n\t}\n\t\n}","/* global Craft */\nexport default function fail (message) {\n\tCraft.cp.displayError(`SEO: ${message}`);\n\twindow.console && console.error.apply( // eslint-disable-line no-console\n\t\tconsole,\n\t\t[\n\t\t\t`%cSEO: %c ${message}`,\n\t\t\t'font-weight: bold;',\n\t\t\t'font-weight: normal;',\n\t\t]\n\t);\n}","/**\n * ## Debounce\n *\n * A function, that, as long as it continues to be invoked, will not\n * be triggered. The function will be called after it stops being called for\n * N milliseconds.\n *\n * If `immediate` is passed, trigger the function on the leading edge,\n * instead of the trailing.\n *\n * ```jsx\n *\n * // ...\n *\n * \n *\n * // ...\n *\n * handleInput = debounce(e => { /* ... *\\/ });\n *\n * ```\n *\n * @param {function} func - The function to debounce\n * @param {number=} wait - How long, in milliseconds, to delay between attempts\n * @param {boolean=} immediate - Fire on the leading edge\n * @returns {Function}\n */\nexport default function debounce (func, wait = 300, immediate = false) {\n\tlet timeout;\n\t\n\tif (wait === 0) {\n\t\treturn function () {\n\t\t\tfunc.apply(this, arguments);\n\t\t};\n\t}\n\t\n\treturn function () {\n\t\tconst context = this\n\t\t\t, args = arguments;\n\t\t\n\t\tif (args[0].constructor.name === 'SyntheticEvent')\n\t\t\targs[0].persist();\n\t\t\n\t\tconst later = function() {\n\t\t\ttimeout = null;\n\t\t\tif (!immediate) func.apply(context, args);\n\t\t};\n\t\tconst callNow = immediate && !timeout;\n\t\tclearTimeout(timeout);\n\t\ttimeout = setTimeout(later, wait);\n\t\tif (callNow) func.apply(context, args);\n\t};\n}","export default function capitalize (s) {\n\treturn s[0].toUpperCase() + s.slice(1);\n}","/**\n * External URL checker\n * From http://stackoverflow.com/a/9744104/550109\n *\n * @param {string} url\n */\nexport default function isExternalUrl (url) {\n\tconst domain = url => {\n\t\tlet res = /https?:\\/\\/((?:[\\w\\d-]+\\.)+[\\w\\d]{2,})/i.exec(url);\n\t\treturn res !== null ? res[1] : false;\n\t};\n\t\n\treturn domain(location.href) === domain(url);\n}","/**\n * Counts the number of times a string appears in an array\n *\n * @param {Array} arr\n * @param {string|Array} word - Can be a string with a single word, or an array\n * of words that would be space separated.\n * @return {number}\n */\nexport default function countInArray (arr, word) {\n\tlet c = 0,\n\t\ti = arr.length;\n\t\n\tif (Array.isArray(word)) {\n\t\tconst l = arr.length\n\t\t\t, w = word.length;\n\t\t\n\t\twhile (i--) {\n\t\t\tlet x = w,\n\t\t\t\ta = 0;\n\t\t\t\n\t\t\twhile (x-- && i + x < l)\n\t\t\t\tif (arr[i + x].toLowerCase() === word[x]) a++;\n\t\t\t\n\t\t\tif (a === w) c++;\n\t\t}\n\t} else {\n\t\twhile (i--) if (arr[i].toLowerCase() === word) c++;\n\t}\n\t\n\treturn c;\n}","/**\n * Creates a rating element\n *\n * @param {string} level\n * @param {string=} tag\n * @return {Element}\n */\nimport capitalize from './capitalize';\nimport t from './createElement';\n\nexport default function createRating (level, tag = 'div') {\n\tconst name = capitalize(level);\n\t\n\treturn t(tag, {\n\t\t'class': `seo--light ${level}`,\n\t\t'title': name,\n\t}, name);\n}","/* globals $, Craft, Garnish */\n/**\n * Entry Markup\n *\n * @author Tam McDonald\n * @copyright Ether Creative 2017\n * @link https://ethercreative.co.uk\n * @package SEO\n * @since 2.0.0\n */\n\nimport { c, fail } from \"../helpers\";\n\nclass EntryMarkup {\n\t\n\t// Variables\n\t// =========================================================================\n\t\n\tframe = null;\n\tpostData = null;\n\t\n\t// Entry Markup\n\t// =========================================================================\n\t\n\tconstructor () {\n\t\tthis.clean();\n\t}\n\t\n\t// Actions\n\t// =========================================================================\n\t\n\t/**\n\t * Gets and stores a parse-able preview of the entry markup\n\t *\n\t * @return {Promise}\n\t */\n\tupdate () {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst nextPostData = Garnish.getPostData(\n\t\t\t\tdocument.getElementById(\"main-form\")\n\t\t\t);\n\t\t\t\n\t\t\t// Skip if no changes have been made to the content\n\t\t\tif (this.postData && Craft.compare(nextPostData, this.postData)) {\n\t\t\t\tresolve(this.frame.contentWindow.document.body);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tthis.postData = nextPostData;\n\t\t\t\n\t\t\t// Get the markup from the live preview\n\t\t\t$.ajax({\n\t\t\t\turl: Craft.livePreview.previewUrl,\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tdata: $.extend({}, nextPostData, Craft.livePreview.basePostData),\n\t\t\t\txhrFields: { withCredentials: true },\n\t\t\t\tcrossDomain: true,\n\t\t\t\tsuccess: data => {\n\t\t\t\t\t// Remove all