Getting Started - Surfer Guidelines
FAQ / Troubleshooting - Surfer Guidelines
interface Window {
surferGuidelines: {
/**
* Initializes and injects Surfer Guidelines into target.
*
* It's a simple wrapper around `initWithOptions`.
*
* When using this function Surfer's iframe will always have
* `surfer-guidelines` attached and `surfer-guidelines-wide`
* will be added when presented view won't fit into sidebar.
*
* ### Example
*
* ```js
* window.surferGuidelines.init(document.body, "sharing key");
* ```
*
*
* @param target HTMLElement reference to which Surfer
* guidelines will be appended
*
* @param sharingToken A prefix to your draft's url which enables
* users who are not logged into Surfer to access guidelines
*/
init(
target: HTMLElement,
sharingToken?: string,
options?: {
permalink?: string
anonymous?: boolean
partner?: string
onContentScoreChanged?: (contentScore: number) => void
onContentScoreDetailsChanged?: (
scoreDetails: ContentScoreDetails
) => void
onTermsToUseChanged?: (termsToUse: any[]) => void
onFocusTermChanged?: (focusTerm: any) => void
}
): void
/**
* Sets html
*
* Provided html should contain a draft only,
* Surfer won't try to extract it from the entire page.
*
* This function works only if Surfer Guidelines
* were created with the `init` function.
*
* ```js
* const editor = document.getElementById("#editor");
*
* surferGuidelines.init(document.body);
*
* editor
* .addEventListener(
* 'keyup',
* (e) => surferGuidelines.setHtml(e.currentTarget.value)
* )
*
* surferGuidelines.setHtml(editor.value)
* ```
*/
setHtml(html: string): void
/**
* Creates an iframe with Surfer guidelines and returns
* reference and callbacks.
*
* Iframe element has to be manually placed into the DOM.
*
* ### Example
*
* ```js
* const {
* $iframe,
* setPermalink,
* setHtml,
* requestView,
* } = window.surferGuidelines.initWithOptions({
* onNavigation(view) {
* if (view === "draft_configuration") {
* // make the iframe wider so it has space for configuration
* $iframe.classList.add("surfer-guidelines-wide");
* window.addEventListener("click", goToGuidelines);
* } else {
* // place it into the sidebar
* $iframe.classList.remove("surfer-guidelines-wide");
* window.removeEventListener("click", goToGuidelines);
* }
* },
* });
*
* const goToGuidelines = () => {
* requestView("guidelines");
* };
*
* const target = document.getElementById("surfer_guidelines");
* target.appendChild($iframe);
*
* setPermalink(
* <https://your.cms.com/draft/some-draft-id>"
* );
*
* setHtml("<p>My HTML</p>")
* ```
*
*/
initWithOptions(opts?: FrameCommunicatorOptions): {
/**
* Reference to Surfer's iframe. It isn't attached to the DOM yet.
*
* ### Example
*
* ```js
* const target = document.getElementById("surfer_guidelines");
* target.appendChild($iframe);
* ```
*/
$iframe: HTMLIFrameElement
/**
* Sets new permalink
*
* If new permalink is different than the old one html
* will be set to `null`
*
* ## Secure vs not secure permalink
*
* All permalinks with `http://` or `https://` are treated as
* not secure which means that in order to access them user
* needs to be logged into owners Surfer account.
*
* This is because a lot of CMS websites have easily guessable
* editor links potentially allowing malicious party to get access
* to your drafts.
*
* If you want to allow other users who don't have access to your
* Surfer account edit drafts add some random prefix to your
* drafts url. Such integration key will be managed only by
* you and has to be constant for given draft.
*
* The best practice would be to generate some random string for each
* new draft and add it before your url.
* Such as `some-random-uuid:<https://your.cms.com/draft/123`>.
*
* Permalink can also be just a random string such as `some-random-uuid`.
*
* It's also possible to generate one constant key for your entire
* integration but when you share one draft with third party
* they will be able to guess other links to your drafts.
* `CMS-wide-sharing-key:<https://your.cms.com/draft/123`>
*
* ### Example
*
* Not secure permalink
*
* ```js
* setPermalink("<https://your.cms.com/draft/some-draft-id>");
* ```
*
* Secure permalink
*
* ```js
* setPermalink(
* encodeURIComponent(
* "sharing key" + "<https://your.cms.com/draft/some-draft-id>"
* )
* );
* ```
*/
setPermalink: (permalink: string | null) => void
/**
* Sets current html
*
* Provided html should contain a draft only, Surfer won't try to
* extract it from whole page.
*
* Note that since calling `setPermalink` with different permalink
* drops html content from the state this function
* should be called after such change
*
* ### Example
*
* ```js
*
* ```
*/
setHtml: (html: string | null) => void
/**
* Enables client to change view inside the iFrame
*/
requestView: (requestedView: RpcRequestedView) => void
/**
* Indicates that some draft properties were changed and it should be reloaded
*/
refreshDraft: () => void
/**
* Configures the `guidelines` view
*/
configureView: (
config: Readonly<{
disableDraftConfiguration?: boolean
disableBatchContentEditorCreation?: boolean
disableHelpMenu?: boolean
alwaysDisplayContentScoreDetails?: boolean
enableGuidelinesFilePasting?: boolean
}>
) => void
/*
* Returns terms that were matched in the text, with their positions.
*/
getMatchedTerms: (terms: string[]) => Readonly<{
terms: string[]
matches: Array<{
term: string
wordSegments: Array<{
index: number
originalSegment: string
}>
}>
}>
}
}
}
interface FrameCommunicatorOptions {
/**
* Disables attempt to authorize, showing guidelines for a not logged in user
*/
anonymous?: boolean
/**
* Allows to customize iframe appearance based on who is using it
*/
partner?: string
/**
* Callback triggered each time iframe navigates to different view
*/
onNavigation?(view: RpcView): void
/**
* Callback triggered each time iframe finishes loading some draft
*
* null means that no draft with such permalink was found
*/
onDraftLoaded?(permalink: string | null): void
/**
* Callback triggered when the content score is updated
*/
onContentScoreChanged?(contentScore: number): void
/**
* Callback triggered when the content score is updated
*/
onContentScoreDetailsChanged?(contentScoreDetails: ContentScoreDetails): void
/**
* Callback triggered when terms to use change (e.g. specific term was used OR terms were changed in configuration view)
*/
onTermsToUseChanged?(termsToUse: any[]): void
/**
* Callback triggered when focus term changes
*/
onFocusTermChanged?(focusTerm: any): void
/**
* Callback triggered when structural guidelines (i.e. headings or paragraphs count) change
*/
onStructuralGuidelinesChanged?(
structuralGuidelines: Array<{
id: string
factor: string
target: { value: number }
value: number
ranges: { [key: string]: [number, number] }
}>
): void
/**
* Callback triggered when Draft wants to paste guidelines file
*/
onPasteGuidelinesFile?(guidelines: string): void
}
type RpcView =
| 'guidelines'
| 'draft_configuration'
| 'draft_creation'
| 'draft_not_found'
| 'draft_loading'
type RpcRequestedView = 'guidelines' | 'draft_configuration'
interface Group {
value: number
maxValue: number
groupName: string
}
type ContentScoreDetails = {
score: number
competitors_avg?: number
competitors_max?: number
details?: Group[]
}