nehan-player
About
nehan-player is fast, powerful, extensible web-component player for paged-media powered by nehan (dynamic and logical html layout engine).
You can see Demo.
Quickstart
<nehan-player writing-mode="horizontal-tb" width="responsive" height="500" font-size="16" layout="1x2">
<div slot="content">
<h1>Hello, nehan-player!</h1>
<p>My first nehan-player experience!</p>
</div>
</nehan-player>
<script src="https://cdn.jsdelivr.net/npm/nehan-player/dist/nehan-player.min.js"></script>
<script>
NehanPlayer.initialize();// Activate with default settings(no options).
</script>
How to install
npm
npm i nehan-player
cdn
<script src="https://cdn.jsdelivr.net/npm/nehan-player/dist/nehan-player.min.js"></script>
How to use
1. Markup HTML
You can load text content by src attribute.
<!-- use 'src' attribute -->
<nehan-player src="path/to/text.html" writing-mode="horizontal-tb" width="800"
height="500" font-size="16" layout="1x2">
</nehan-player>
Or you can set text content by content slot.
<!-- use 'content' slot -->
<nehan-player writing-mode="horizontal-tb" width="800" height="500"
font-size="16" layout="1x2">
<div slot="content">Your content text is here</div>
</nehan-player>
2. Activate nehan-player
To activate nehan-player, you should call NehanPlayer.initialize.
Typescript
import { NehanPlayer } from 'nehan-player';
NehanPlayer.initialize(); // Activate with default settings(no options).
Javascript
require { NehanPlayer } from 'nehan-player';
NehanPlayer.initialize(); // Activate with default settings(no options).
Note that there are many option argument for initialize method. (See NehanPlayer.initialize options)
Markup options
Supported element attributes
src |
Specify path for the target text file. Note that you can also set text content by content slot(See Supported slot) |
---|---|
theme |
Specify path for the theme css(e.g. 'themes/default.css') |
writing-mode |
Specify "writing-mode" value of paged media content. "horizontal-tb" or "vertical-rl" or "vertical-lr" are available. |
width |
Specify player width. px size or "responsive" are available. Note that min size of witdh is 300px. If width is "responsive", player width automatically changes for each time the size of parent elmement is changed. |
height |
Specify player height(px). Height of menu and footer are not included in this height. Note that min size of height is 300px. |
font-size |
Specify document font size(px) for paged media content. |
layout |
Specify layout grid format. "1x1" or "1x2" or "2x1" or "2x2" are available. To use multi-column format(1x2 or 2x2), at least 600px of width is required. If width is smaller than 600px, then single column format is used. To use multi-row format(2x1 or 2x2), at least 500px of height is required. If height is smaller than 500px, then single row format is used. |
Supported template slot
content slot
You can use content slot to define player content.
Note that use can use src attribute for remote resource.
<nehan-player>
<div slot="content">
<h1>This is my title!</h1>
<p>This is my player content!!</p>
</div>
</nehan-player>
title slot
You can set the title at the top-left corner of screen by title slot.
<nehan-player src="path/to/text">
<span slot="title">This is my great title</span>
</nehan-player>
sub-title slot
You can set the sub-title next to the title by sub-title slot.
<nehan-player src="path/to/text">
<span slot="title">This is my great title</span>
<span slot="sub-title"> - chapter 1</span>
</nehan-player>
button label slot
You can change the label of next/prev button by using goto-left-label slot or goto-right-label slot like this.
<nehan-player src="path/to/text">
<span slot="goto-left-label">goto left!!!</span>
<span slot="goto-right-label">goto right!!!</span>
</nehan-player>
footer slot
You can add footer text in the bottom-right corner of player by using footer slot.
<nehan-player src="path/to/text">
<span slot="footer">All rights reserved by <a href="#">xxx</a></span>
</nehan-player>
NehanPlayer.initialize options
You can set extra settings or callbacks in optional arugment of NehanPlayer.initialize.
cssFiles
Set css files for shadow DOM of nehan-player.
Apart from the theme attribute of the elemement, this field is used to load other external css files.
Note that you can't put it in host html, because shadow DOM is not affected by host css.
// if we use nehan-katex, we should import css for katex.
NehanPlayer.initialize({
cssFiles: ["katex.css"]
});
cssText(since ver 0.1.0)
Inline style text for shadow DOM of nehan-player.
// if we use nehan-katex, we should import css for katex.
NehanPlayer.initialize({
cssText: "#menu { display: none }"
});
onCreateNehanStyles
Unlike normal HTML content, the content layout of each page is calculated by logical layout engine (nehan).
So if you need to add some style to page content, you should declare nehan style (CssStyleSheet declaration for nehan layout engine).
For more details of nehan style, see About nehan style section.
onFetchContent
Fired when the content of src attribute or content slot is fetched.
You can fix the content here for you own use.
NehanPlayer.initialize({
onFetchContent(src: string, content: string){
if (src.endsWith(".txt")) {
return content.replace(/^\n+/, "").replace(/\n/g, "<br>");
}
return content;
}
});
onPlayerReady
Fired when PagedMediaPlayer is ready to use.
NehanPlayer.initialize({
onPlayerReady(player: PagedMediaPlayer){
// console.log("player ready:", player);
}
});
onParsePage
Fired when each page is parsed.
NehanPlayer.initialize({
onParsePage(player: PagedMediaPlayer, pageIndex: numuber){
// console.log(`parsed page(${pageIndex})`);
}
});
onParseComplete
Fired when final page is parsed.
This is perfect timing to create outline(toc) element.
import { PlayerSection } from 'nehan-player';
NehanPlayer.initialize({
onParseComplete(player: PagedMediaPlayer, pageCount: numuber, ellapsedTime: number){
// console.log(`finished! time = ${ellapsedTime / 1000}sec`);
const outline = player.createOutline((section: PlayerSection) => {
player.gotoPage(section.pageIndex);
});
}
});
onSetPage
Fired when user move the page and new page is set.
If you want some action for each paging, you can add some action here.
import { Page } from 'nehan';
NehanPlayer.initialize({
onSetPage(player: PagedMediaPlayer, page: Page){
// console.log("set page:", page);
}
});
onClickLeftPage
Fired when user click left side of the page.
NehanPlayer.initialize({
onClickLeftPage(player: PagedMediaPlayer){
player.gotoLeftPage();
}
});
onClickRightPage
Fired when user click right side of the page.
NehanPlayer.initialize({
onClickRightPage(player: PagedMediaPlayer){
player.gotoRightPage();
}
});
PagedMediaPlayer interface
Properties
$host
HTMLElement of <nehan-player>.
id(readonly)
id attribute value of <nehan-player> element.
src(readonly)
src attribute value of <nehan-player> element.
content(readonly)
Content string of player.
currentSection(readonly)
Current section data.
currentPageIndex
Current page index that starts from zero.
You can set or get.
const index = player.currentPageIndex; // get
player.currentPageIndex = 0; // set(go to fist page)
pageCount
Total page count of document.
width(readonly)
Player width size in px.
height(readonly)
Player height size in px.
layout
Grid layout of player.
Returns "1x1" or "1x2" or "2x1" or "2x2"
fontSize
Base font-size used for paged-media content of player.
writingMode
writing-mode of this player.
Returns "horizontal-tb" or "vertical-rl" or "vertical-lr".
const isVertical: boolean = player.writingMode.indexOf("vertical") >= 0;
const isRightToLeft: boolean = player.writingMode === "vertical-rl";
Method
gotoPage(pageIndex: number)
Move to page of pageIndex.
player.gotoPage(0); // goto first page
player.gotoPage(player.pageCount - 1); // goto last page
gotoNextPage()
Goto next page.
If LTR text, it's same as gotoRightPage().
If RTL text, it's same as gotoLeftPage().
gotoPrevPage()
Goto previous page.
If LTR text, it's same as gotoLeftPage().
If RTL text, it's same as gotoRightPage().
gotoLeftPage()
Goto left side of the page.
If LTR text, it's same as gotoPrevPage().
If RTL text, it's same as gotoNextPage().
gotoRightPage()
Goto right side of the page.
If LTR text, it's same as gotoNextPage().
If RTL text, it's same as gotoPrevPage().
update(attributes)
Update player attribute and refresh player layout if required.
About supported attributes, see Supported attributes.
player.update({ fontSize: 20 });
player.update({ writingMode: "vertical-rl" });
player.update({ width: "responsive", layout: "1x1" });
player.update({ width: "900", height: "500" });
refresh(throttle: boolean)
Unlike update method, refresh method forces make player updated (update method is ignored if no change is detected).
If argument throttle is true, player prevents display from being updated too frequently.
createOutline(onClickTocItem?)
Create toc DOM tree.
Optional argument onClickTocItem is called when each toc item is clicked.
Note that if you call this method BEFORE onParseComplete callback, the toc tree will be incomplete one.
import { PlayerSection } from 'nehan-player';
NehanPlayer.initialize({
onParseComplete(player: PagedMediaPlayer, pageCount: number, time: number){
const $toc: HTMLElement = player.createOutline((section: PlayerSection) => player.gotoPage(section.pageIndex));
}
});
setTitle(title: string)
Set the top-left corner text.
setSubTitle(title: string)
Set the top-left corner sub-title text.
NehanPayer.initialize({
onSetPage(player: PagedMediaPlayer, page: Page) {
const section = player.currentSection;
if (!section.isRoot()) {
player.setSubTitle(`> ${section.title}`);
}
}
});
About nehan style
You can create nehan style to modify inside style of page content.
Remember that each content of page is not calculated by native browser, but logical layout engine (nehan).
So if you need to modify the style of page content, you should declare nehan styles!
example1: back to top link
This is a tiny example of nehan style. By clicking link like <a href="#top">back to top</a>, we can goto first page, and it's color is green.
import { CssStyleSheet, DomCallbackContext } from 'nehan';
import { PagedMediaPlayer } from 'nehan-player';
function createBackToTopStyle(player: PagedMediaPlayer){
return new CssStyleSheet({
"a[href='#top']": {
"color": "green",
"@create": (ctx: DomCallbackContext) => {
ctx.dom.onclick = (e) => {
e.preventDefault();
player.gotoPage(0);
return false;
}
}
}
});
}
And you may be confused, because there is some strange property name. What is "@create"?
In nehan-style, the property name that starts with "@" is called callback style.
Each time the DOM of target selector is created, callback syle is called.
But you may wonder the word "each time". Isn't the DOM of selector associated with one node only created once?
No, it's not. Remember that nehan is "paged-media" layout engine, so one selector(for one node) can be generated into splited pages, and this callback function can be called multiple times for one node. So we use the word "each time".
Well, anyway, finally we got our first nehan style! Then we can set this style in onCreateNehanStyles.
And now you can use this "back to top" link anywhere in your page content.
NehanPlayer.initialize({
onCreateNehanStyles(player: PagedMediaPlayer){
return [
createBackToTopStyle(player), // our first nehan style!
];
}
});
example2: dynamic style
Unlike normal css, nehan-style can dynamically change the css value according to parent layout status.
The following is an example that dynamically creates page-break-before: always whether there is enough space left for the parent layout.
Note that in nehan, block size is expressed as extent (and inline size is expressed as measure).
And you may be confused again, because there is some strange property name once again. What is "!dynamic"?
In nehan-style, the property name that starts with "!" is called dynamic style.
This dynamic style runs each time before layout engine try to fill in the remaining space in the parent layout.
function requiredExtent(requiredSize: number) {
return (ctx: DynamicStyleContext): CssDeclarationBlock | undefined => {
if (!ctx.parentContext) {
return undefined;
}
if (ctx.parentContext.restExtent >= requiredSize) {
return undefined; // enough block size is left!
}
// There is not enough block size left, so break the page.
return { "page-break-before": "always" };
};
}
const myStyleSheet = new CssStyleSheet({
".require-60": {
"!dynamic": requiredExtent(60)
}
});
Other examples
There are other plugins for nehan style, here is some examples.
- nehan-anchor is a nehan plugin to create anchor preview for anchor link.
- nehan-highlight is a nehan plugin to enable code highlighting.
- nehan-katex is a nehan plugin to display katex math expressions.
- nehan-speech-border is a nehan plugin to display border of speech-balloon.
- DynamicStyleUtils is part of nehan layout engine, and it defines many dynamic layout examples.
- SemanticStyle is part of nehan layout engine, and it defines many semantic markup classes.
About css propertie names in nehan-style
Be aware that nehan is a logical layout engine, so some propertie names of css are different from normal ones.
For example, margin-left is not available in nehan, instead, we use margin-start (for writing-mode:"horizontal-tb") or margin-after (for writing-mode:"vertical-rl").
Besides, border, padding, width, height, and direction(like left/right/top/bottom) are different from normal ones.
For more details, see "About logical size" and "About logical direction" section of README of nehan.