Commit 1aec68cb by ramdayalmunda

css included

parent 5c44509d
.a-doc-editor {
.main {
position: relative;
display: flex;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #858585;
flex-direction: column;
height: 520px;
}
.a-doc-editor canvas:focus-visible {
outline: auto rgb(0, 68, 255, 50%);
background: #fff;
justify-content: flex-start;
}
.a-doc-editor .header {
background-color: #f0f0f0;
padding: 3px;
cursor:default;
}
.a-doc-editor .dropdown-label {
display: flex;
flex-direction: column;
text-align: center;
justify-content: center;
}
.a-doc-editor .dropdown-list {
display: none;
position: absolute;
z-index: 100;
background-color: #f5f5f5;
border-radius: 5px;
max-height: 300px;
overflow: auto;
top: 100%;
}
.a-doc-editor .dropdown-list.show {
display: block;
width: max-content;
}
.a-doc-editor .dropdown-list .dropdown-option{
padding: 10px;
border-radius: 5px;
}
.a-doc-editor .dropdown-list .dropdown-option:hover{
background: #91d8bd;
}
.a-doc-editor .header .option-bar{
display:flex;
}
.a-doc-editor .header .option{
.header {
position: relative;
border-bottom: 1px solid rgba(0, 0, 0, 0.137);
padding: 1px;
display: flex;
}
.a-doc-editor .header .option .list-label{
position: relative;
font-size: 0.5em;
border-bottom: 1px solid rgba(0, 0, 0, 0.137);
padding: 1px;
display: flex;
flex-direction: column;
width: 25px;
}
.a-doc-editor .header .option .thumbnail-options{
display: none;
position: absolute;
z-index: 100;
background-color: #f5f5f5;
border-radius: 5px;
max-height: 300px;
overflow: auto;
top: 100%;
}
.a-doc-editor .header .option .thumbnail-options.show {
display: block;
width: max-content;
}
.a-doc-editor .header button{
width: 25px;
}
.a-doc-editor .header .option-button{
width: 25px;
display: flex;
flex-direction: column;
text-align: center;
justify-content: center;
}
.a-doc-editor .header .option input{
width: 25px;
}
.a-doc-editor .header .option .font-color{
display: flex;
flex-direction: column;
text-align: center;
}
.a-doc-editor .header .option .color-bar{
display: block;
width: 20px;
height: 8px;
background-color: green;
}
.a-doc-editor .header .option .font-color input{
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
.a-doc-editor .header .option:hover{
padding: 1px;
background-color: white;
cursor: pointer;
}
.a-doc-editor .header .option.selected{
padding: 1px;
background-color: #91d8bd;
cursor: pointer;
}
.a-doc-editor .footer {
overflow: auto;
background-color: #91d8bd;
background-color: red;
}
.a-doc-editor .body {
flex: 1;
.toolbar {
position: relative;
background-color: #ccc;
overflow: hidden;
transition: 300ms;
}
.a-doc-editor .body .left-sidebar {
left: 0;
border-radius: 0 10px 10px 0;
box-shadow: 4px 0px 10px 4px rgba(0, 0, 0, 0.4);
width: 100%;
background: pink;
display: flex;
flex-direction: column;
position: absolute;
padding: 8px;
width: 17%;
top: 0;
bottom: 0;
background-color: #8a9a9b;
transition: 300ms;
gap: 10px;
}
.a-doc-editor .body .left-sidebar.hide {
left: -16%;
transition: 300ms;
}
.a-doc-editor .body .toggle-sidebar {
.toolbar .item {
position: relative;
background: #408640;
border: 1px solid gray;
border-radius: 3px;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 4px;
padding-right: 4px;
margin: 4px;
cursor: pointer;
display: flex;
justify-content: space-between;
}
.a-doc-editor .body .right-sidebar {
right: 0;
border-radius: 10px 0 0 10px;
box-shadow: -4px 0px 10px 4px rgba(0, 0, 0, 0.4);
display: flex;
flex-direction: column;
.popover{
position: absolute;
padding: 8px;
width: 17%;
top: 0;
bottom: 0;
background-color: #8a9a9b;
transition: 300ms;
}
.a-doc-editor .body .right-sidebar.hide {
right: -16%;
transition: 300ms;
}
.a-doc-editor .body .sidebar-body{
flex: 1;
padding: 8px;
text-align: center;
overflow: auto;
display: flex;
flex-direction: column;
padding: 5px;
background-color: gray;
border: 1px solid purple;
opacity: 0;
pointer-events: none;
}
.a-doc-editor .body .hide .sidebar-body{
overflow: hidden;
.popover.show{
opacity: 1;
pointer-events: auto;
z-index: 10;
}
.a-doc-editor .body .scrolling-area {
position: absolute;
padding: 8px;
left: 5%;
right: 5%;
top: 0;
bottom: 0;
text-align: center;
overflow: auto;
.toolbar .item:hover {
background: #5fad5f;
}
.a-doc-editor .body .scrolling-area canvas.page {
background: #f3f3f3;
margin-bottom: 10px;
width: 100%;
box-shadow: 4px 0px 10px 4px rgba(0, 0, 0, 0.4);
[adc-type="popover"] {
pointer-events: none;
}
.a-doc-editor .body .scrolling-area canvas.page:first-child {
margin-top: 10px;
.small-input {
width: 50px;
}
.a-doc-editor ::-webkit-scrollbar {
width: 10px;
/* Set the width of the scrollbar */
.small-btn {
width: 25px;
}
/* Thumb */
.a-doc-editor ::-webkit-scrollbar-thumb {
background-color: #888;
/* Set the color of the scrollbar thumb */
border-radius: 5px;
/* Set border radius */
.option {
border: 1px solid black;
padding: 5px;
margin: 5px;
border-radius: 4px;
}
/* Track */
.a-doc-editor ::-webkit-scrollbar-track {
background-color: #f0f0f0;
/* Set the color of the scrollbar track */
border-radius: 5px;
/* Set border radius */
.option:hover {
background-color: #f5c468;
}
.full-screen-overlay {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background-color: rgba(0, 0, 0, 0.7); /* Optional background color */
display: flex;
justify-content: center;
.page-list {
position: relative;
display: block;
overflow-y: auto;
overflow-x: auto;
background: #858585;
gap: 20px;
align-items: center;
color: white;
font-size: xx-large;
}
.page-list canvas {
background-color: #fff;
width: 210mm;
height: auto;
display: block;
position: relative;
margin: 20px;
}
.footer {
position: relative;
display: block;
width: 100%;
background-color: red;
}
\ No newline at end of file
let isModule = (typeof module != 'undefined') ? true : false
if (!isModule) console.log('Browser Environment')
var ADocEditor = function (customConfig) {
var fs, path;
var PDFDocument, rgb;
if (isModule) {
fs = require("fs");
path = require("path")
} else {
var { PDFDocument, rgb } = PDFLib
var container, counter = 0, shadow, pxMmRatio, canvasPxMmRatio, config, htmlStr, htmlTag, htmlObj = {}, fontList = [], headerToolbar = [];
var paperSizes = {
"A4": { mmWidth: 210, mmHeight: 297 },
}
var styleTag;
var screenWidth = (typeof screen != 'undefined') ? screen.width : 1280
var counter = 7
var container = null
var mainComponent = null
var scrollingComponent = null
var headerComponent = null
var footerComponent = null
var leftSidebar = null
var rightSidebar = null
var fontFamily = null;
var fontFamilyDropdown = null
var fontList = []
var fontSize;
var fontColor;
var fontColorLabel;
var fontBold;
var fontItalic;
var fullScreenLoadingOverlay;
const letters = { "0": "a","1": "b","2": "c","3": "d","4": "e","5": "f","6": "g","7": "h","8": "i","9": "j","a": "k","b": "l","c": "m","d": "n","e": "o","f": "p","g": "q","h": "r","i": "s","j": "t","k": "u","l": "v","m": "w","n": "x","o": "y","p": "z" }
const romanNumerals = [ ["I", "IV", "V", "IX"], ["X", "XL", "L", "XC"], ["C", "CD", "D", "CM"], ["M"] ];
var defaultConfig = {
element: "",
pageSetup: {
width: 210,
height: 297,
orientation: "p",
},
pageSetup: { size: "A4" }, zoom: 1,
format: {
background: "#fff",
margin: 20,
margin: 15, // mm
border: "",
fontSize: 10,
tabWidth: 20,
tabWidth: 20, // mm
},
style: {
fontSize: 16,
fontSize: 10, // this is in mm
fontFamily: 'Calibri',
bold: false,
italic: false,
fontColor: "#001"
fontColor: "#f01"
},
}
var config = JSON.parse(JSON.stringify(defaultConfig));
var canvasList = []
var lastFocusCanvas = null;
const dataTypes = [
"paragraph", // simple text filled line by line
"list", // sequence of line that preserves indentation
]
var dataSet = [
config = JSON.parse(JSON.stringify(defaultConfig))
var dataList = [
{
id: ++counter,
type: 0,
formatedText: [],
plainContent: "",
tabCount: 0,
style: { ...config.style }
style: { ...config.style },
listItemNumber: 0,
// newPage: false, // if this is true the data is send to new page
},
]
// // this is the combination of paragraph and list //
// dataSet=[{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Sarah: Alex, we need some groceries from the store. Could you please go and get them?"},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Alex: (Without looking up from his laptop) Mom, I'm right in the middle of something important. Can't it wait?"},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Sarah: (Smiling) I understand, dear. But I've got a lot to do here, and it'd be a great help if you could pick up the groceries."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Alex: (Sighing) Can't we go later? I've got this project deadline..."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Sarah: (Interrupting gently) Alex, we're running low on essentials. It won't take much time. Plus, some fresh air might do you good after being glued to your laptop for hours."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Alex: (Reluctantly) But it's your day off, Mom. You should relax."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Sarah: (Chuckling) Relaxing is fetching groceries? No worries, it's alright. I can go."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Alex: (Feeling guilty) No, no, I'll go. Just let me finish this task, okay?"},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Sarah: (Smiling warmly) Thanks, dear. I really appreciate it."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"They share a smile, and Alex closes his laptop, heading out to the store."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Sarah: (Calling after him) Don't forget the list!"},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Alex waves in acknowledgment and leaves, while Sarah returns to her cooking, content that the groceries will soon be sorted. Alex looks at the list, there were a lot of items on the list."},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Vegetables",tabCount:0},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Carrots",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Broccli",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Bell Peppers",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Fruits",tabCount:0},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Apples",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Oranges",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Dairy",tabCount:0},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Milk",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Cheese",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Yogurt",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Household",tabCount:0},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Cottonelle Toilet Paper (Pack of 6). Make sure to bring the items where each pack has 100 meter rolls.",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Mrs. Meyer's Lemon Verbena Dish Soap",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Tide Free & Gentle Laundry Detergent",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Bakery",tabCount:0},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Bread",tabCount:1},{id:++counter,type:1,style:{...config.style},formatedText:[],plainContent:"Bagels",tabCount:1},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Once Alex stepped out of his house, he felt the crisp autumn air greet him. Armed with his phone displaying Sarah's detailed grocery list, he set off on his mission to the nearby supermarket."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"The store was bustling with shoppers, but Alex navigated through the aisles with purpose. He began in the produce section, carefully selecting the freshest carrots, crisp broccoli, and vibrant red bell peppers. He lingered a moment longer in the fruit section, examining each apple, banana, and orange to ensure they met the criteria on Sarah's list."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"With the vegetables and fruits secured, he made his way to the dairy aisle. He picked up the specified 2% organic milk, sharp cheddar cheese, and Greek vanilla yogurt, double-checking the brands to match Sarah's preferences."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Next on the list were bakery items. He chose a loaf of whole-grain bread and a pack of everything bagels, making sure they were fresh before moving on to the pantry essentials. Penne pasta, Basmati rice, fire-roasted diced tomatoes, and honey nut crunch cereal found their way into his cart."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"As he moved to the meat and protein section, he carefully selected free-range chicken breasts, a pound of lean ground beef, and a dozen farm-fresh eggs, ensuring they met the quality standards Sarah had in mind."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"After collecting household items like cottonelle toilet paper, Mrs. Meyer's lemon verbena dish soap, and Tide Free & Gentle laundry detergent, Alex made his way to the checkout counter. The cashier scanned each item, and Alex packed them meticulously into bags, arranging delicate produce separately to prevent any damage."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"Balancing the bags on his arms, Alex made his way back home. The sky had turned a warm orange hue as the sun began to set. He navigated through the busy streets, feeling a sense of accomplishment for successfully checking off every item on Sarah's exhaustive list."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"As he approached his house, he could already smell the aroma of the dinner Sarah was preparing. Alex walked in, slightly out of breath but proud of his successful grocery run."},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:'"Hey, Mom, I\'m back!" he called out.'},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:'Sarah, busy at the stove, turned around, beaming. "Oh, Alex, you\'re a lifesaver! Did you manage to get everything?"'},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:'"Absolutely," Alex replied, setting the bags down on the counter. "And I even got the specific brands and types you asked for!"'},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:'Sarah gave him a grateful smile. "Thank you so much, Alex. You\'re a star! Now, dinner will be ready in no time."'},{id:++counter,type:0,style:{...config.style},formatedText:[],plainContent:"As they unpacked the groceries together, Sarah couldn't help but appreciate her thoughtful son's effort and attention to detail, making the task much more manageable and enjoyable for her."},];
var lines = []
var caretData = {
activeData: dataSet[0],
activeData: dataList[0],
index: 0,
interval: null,
intervalDuration: 800,
blink: false,
canvasIndex: 0,
timeout: null,
timeoutDuration: 800,
blink: true,
pageIndex: 0,
caretSize: config.style.fontSize,
x: config.format.margin,
y: config.format.margin + (3 * config.style.fontSize / 4),
previousCaret: null,
style: { ...dataSet[0].style },
style: { ...dataList[0].style, x: config.format.margin, y: config.format.margin + (3 * config.style.fontSize / 4) },
}
var renderInProgress = false;
function inititalize(customConfig) {
config = { ...defaultConfig, ...customConfig }
config.pageSetup.canvasMultiplier = Math.round(screenWidth / config.pageSetup.width)
config.pageSetup.fontMultiplier = config.pageSetup.canvasMultiplier / 3
config.pageSetup.canvasWidth = config.pageSetup.canvasMultiplier * config.pageSetup.width
config.pageSetup.canvasHeight = config.pageSetup.canvasMultiplier * config.pageSetup.height
if (!isModule) {
if (!config.element) throw "'container' not provided in the config"
if (typeof config.element == 'string') container = document.querySelector(config.element)
if (config.element instanceof HTMLElement) container = config.element
if (!container) throw "undefined"
styleTag = document.createElement('style')
let mainComponentId = `main-${new Date().getTime()}`
let headerComponentId = `header-${new Date().getTime()}`
let footerComponentId = `footer-${new Date().getTime()}`
let scrollingAreaId = `scrolling-area-${new Date().getTime()}`
let leftSidebarId = `left-sidebar-${new Date().getTime()}`
let rightSidebarId = `right-sidebar-${new Date().getTime()}`
let fontFamilyDropdownId = `font-family-${new Date().getTime()}`
let htmlSetup = /*html*/`
<div class="a-doc-editor" id="${mainComponentId}">
<div class="header" id="${headerComponentId}">
<div class="option-bar">
<div class="option">File</div>
<div class="option">Edit</div>
<div class="option">Help</div>
</div>
<div class="option-bar">
<div class="option">
<div class="dropdown-label" ade-target="#${fontFamilyDropdownId}" style="min-width:180px"></div>
<div class="dropdown-list" id="${fontFamilyDropdownId}" ade-type="dropdown"></div>
</div>
<div class="option">
<button ade-action="font-size-minus">-</button>
<input ade-action="font-size-set"/>
<button ade-action="font-size-plus">+</button>
</div>
<div class="option" ade-action="font-bold">
<div class="option-button"><strong>B</strong></div>
</div>
<div class="option" ade-action="font-italic">
<div class="option-button"><em>I</em></div>
</div>
<div class="option">
<div class="font-color">
<span>A</span>
<span class="color-bar" ade-label="font-color"></span>
<input type="color" ade-action="font-color">
</div>
</div>
<div class="option">
<div class="list-label">
<span>1&ndash;&ndash;&ndash;&ndash;</span>
<span>2&ndash;&ndash;&ndash;&ndash;</span>
<span>3&ndash;&ndash;&ndash;&ndash;</span>
</div>
<div class="thumbnail-options">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
<div>Item 5</div>
<div>Item 6</div>
<div>Item 7</div>
<div>Item 8</div>
<div>Item 9</div>
</div>
</div>
var pageList = []
var pageScrollingDiv = null
var focussedPage = null
var lastFocussedPage = null
var isRendering = false
function initialize() {
config = JSON.parse(JSON.stringify(defaultConfig))
container = customConfig.container
shadow = container.attachShadow({ mode: "open" })
htmlStr = /*html*/`
<div class="header">
<div class="toolbar">
<select class="item" adc="font-select"></select>
<div class="item" adc="list-handler" adc-target="list-popover">
<span>L</span>
<div class="popover" adc="list-popover" adc-type="popover">
<div class="option" adc-toggle="listing-option" value="1">•&nbsp;&nbsp;Bullets&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div class="option" adc-toggle="listing-option" value="0">1. Numbers</div>
</div>
</div>
<div class="body">
<div class="scrolling-area" id="${scrollingAreaId}">
</div>
<div class="left-sidebar" id="${leftSidebarId}">
<div class="toggle-sidebar">
<span></span>
<span>Summary</span>
<span>&lrarr;</span>
</div>
<div class="sidebar-body"></div>
</div>
<div class="right-sidebar" id="${rightSidebarId}">
<div class="toggle-sidebar">
<span>&lrarr;</span>
<span>Comments</span>
<span></span>
</div>
<div class="sidebar-body"></div>
</div>
</div>
<div class="footer" id="${footerComponentId}">
<div>Footer Data</div>
<div class="item">
<button adc="font-size-change" class="small-btn" value="-1">-</button>
<input type="number" adc="font-size-input" class="small-input">
<button adc="font-size-change" class="small-btn" value="+1">+</button>
</div>
</div>
`;
</div>
<div class="page-list" adc="page-list"></div>
<div class="footer">
<div class="toolbar">
<select class="item" title="Select zoom" adc="zoom">
<option value="" disabled>Select Zoom</option>
<option value="0.1">10%</option>
<option value="0.2">20%</option>
<option value="0.3">30%</option>
<option value="0.4">40%</option>
<option value="0.5">50%</option>
<option value="0.6">60%</option>
<option value="0.7">70%</option>
<option value="0.8">80%</option>
<option value="0.9">90%</option>
<option value="1" selected>100%</option>
<option value="1.2">120%</option>
<option value="1.5">150%</option>
<option value="1.8">180%</option>
<option value="2">200%</option>
</select>
<span class="item">Words : 0</span>
<span class="item">Sentences : 0</span>
<span class="item">Pages : 1</span>
</div>
</div>
`;
let styleSheets = customConfig?.styleSheet?.length ? customConfig.styleSheet : []
styleSheets.splice(0, 0, "/assets/a-doc-editor.css")
for (let i = 0; i < styleSheets.length; i++) {
let link = document.createElement('link')
link.setAttribute('rel', 'stylesheet');
link.setAttribute('href', styleSheets[i]);
shadow.append(link)
}
htmlTag = document.createElement('div')
htmlTag.setAttribute('class', 'main')
htmlTag.innerHTML = htmlStr
shadow.append(htmlTag)
htmlObj = {
header: shadow.querySelector('.header'),
pageList: shadow.querySelector('page-list')
}
pageScrollingDiv = shadow.querySelector('[adc="page-list"]')
!(function zoomHandles() {
let zoomSelect = shadow.querySelector('[adc="zoom"]')
zoomSelect.addEventListener('change', (e) => {
config.zoom = Number(e.target.value)
reConfigure(config)
})
})()
!(function fontSelectHandle() {
let fontSelect = shadow.querySelector('[adc="font-select"]')
headerToolbar.push(fontSelect)
fontSelect.addEventListener('change', (e) => {
caretData.style = {
...caretData.style,
fontFamily: e.target.value,
}
focusOnPage()
})
addFonts(["./assets/fonts/Afacad-VariableFont_wght.woff2", "./assets/fonts/Afacad-VariableFont_wght.ttf"], 'Afacad')
addFonts(["./assets/fonts/ArchitectsDaughter-Regular.woff2", "./assets/fonts/ArchitectsDaughter-Regular.ttf"], 'Architects Daughter')
addFonts(["./assets/fonts/Assistant-VariableFont_wght.woff2", "./assets/fonts/Assistant-VariableFont_wght.ttf"], 'Assistant')
addFonts(["./assets/fonts/Bitter-VariableFont_wght.woff2", "./assets/fonts/Bitter-VariableFont_wght.ttf"], 'Bitter')
addFonts(["./assets/fonts/calibri-regular.woff2", "./assets/fonts/calibri-regular.ttf"], 'Calibri')
addFonts(["./assets/fonts/Caveat-VariableFont_wght.woff2", "./assets/fonts/Caveat-VariableFont_wght.ttf"], 'Caveat')
addFonts(["./assets/fonts/Comfortaa-VariableFont_wght.woff2", "./assets/fonts/Comfortaa-VariableFont_wght.ttf"], 'Comfortaa')
addFonts(["./assets/fonts/CrimsonText-Regular.woff2", "./assets/fonts/CrimsonText-Regular.ttf"], 'Crimson Text')
addFonts(["./assets/fonts/EduTASBeginner-VariableFont_wght.woff2", "./assets/fonts/EduTASBeginner-VariableFont_wght.ttf"], 'Edu TAS Beginner')
addFonts(["./assets/fonts/FiraSans-Regular.woff2", "./assets/fonts/FiraSans-Regular.ttf"], 'Fira Sans')
addFonts(["./assets/fonts/IndieFlower-Regular.woff2", "./assets/fonts/IndieFlower-Regular.ttf"], 'Indie Flower')
addFonts(["./assets/fonts/Kanit-Regular.woff2", "./assets/fonts/Kanit-Regular.ttf"], 'Kanit')
addFonts(["./assets/fonts/Karla-VariableFont_wght.woff2", "./assets/fonts/Karla-VariableFont_wght.ttf"], 'Karla')
addFonts(["./assets/fonts/Kenia-Regular.woff2", "./assets/fonts/Kenia-Regular.ttf"], 'Kenia')
addFonts(["./assets/fonts/Lato-Regular.woff2", "./assets/fonts/Lato-Regular.ttf"], 'Lato')
addFonts(["./assets/fonts/LibreFranklin-VariableFont_wght.woff2", "./assets/fonts/LibreFranklin-VariableFont_wght.ttf"], 'Libre Franklin')
addFonts(["./assets/fonts/Lora-Regular.woff2", "./assets/fonts/Lora-Regular.ttf"], 'Lora')
addFonts(["./assets/fonts/Macondo-Regular.woff2", "./assets/fonts/Macondo-Regular.ttf"], 'Macondo')
addFonts(["./assets/fonts/Merriweather-Regular.woff2", "./assets/fonts/Merriweather-Regular.ttf"], 'Merriweather')
addFonts(["./assets/fonts/Montserrat-VariableFont_wght.woff2", "./assets/fonts/Montserrat-VariableFont_wght.ttf"], 'Montserrat')
addFonts(["./assets/fonts/Mulish-VariableFont_wght.woff2", "./assets/fonts/Mulish-VariableFont_wght.ttf"], 'Mulish')
addFonts(["./assets/fonts/NotoSans-VariableFont_wdth,wght.woff2", "./assets/fonts/NotoSans-VariableFont_wdth,wght.ttf"], 'Noto Sans')
addFonts(["./assets/fonts/Nunito-VariableFont_wght.woff2", "./assets/fonts/Nunito-VariableFont_wght.ttf"], 'Nunito')
addFonts(["./assets/fonts/OpenSans-VariableFont_wdth,wght.woff2", "./assets/fonts/OpenSans-VariableFont_wdth,wght.ttf"], 'Open Sans')
addFonts(["./assets/fonts/Outfit-VariableFont_wght.woff2", "./assets/fonts/Outfit-VariableFont_wght.ttf"], 'Outfit')
addFonts(["./assets/fonts/Pacifico-Regular.woff2", "./assets/fonts/Pacifico-Regular.ttf"], 'Pacifico')
addFonts(["./assets/fonts/Poppins-Regular.woff2", "./assets/fonts/Poppins-Regular.ttf"], 'Poppins')
addFonts(["./assets/fonts/Prompt-Regular.woff2", "./assets/fonts/Prompt-Regular.ttf"], 'Prompt')
addFonts(["./assets/fonts/Rajdhani-Regular.woff2", "./assets/fonts/Rajdhani-Regular.ttf"], 'Rajdhani')
addFonts(["./assets/fonts/Roboto-Regular.woff2", "./assets/fonts/Roboto-Regular.ttf"], 'Roboto')
addFonts(["./assets/fonts/Rubik-VariableFont_wght.woff2", "./assets/fonts/Rubik-VariableFont_wght.ttf"], 'Rubik')
addFonts(["./assets/fonts/SourceCodePro-VariableFont_wght.woff2", "./assets/fonts/SourceCodePro-VariableFont_wght.ttf"], 'Source Code Pro')
addFonts(["./assets/fonts/Teko-VariableFont_wght.woff2", "./assets/fonts/Teko-VariableFont_wght.ttf"], 'Teko')
addFonts(["./assets/fonts/TitilliumWeb-Regular.woff2", "./assets/fonts/TitilliumWeb-Regular.ttf"], 'Titillium Web')
addFonts(["./assets/fonts/Ubuntu-Regular.woff2", "./assets/fonts/Ubuntu-Regular.ttf"], 'Ubuntu')
addFonts(["./assets/fonts/VarelaRound-Regular.woff2", "./assets/fonts/VarelaRound-Regular.ttf"], 'Varela Round')
addFonts(["./assets/fonts/WorkSans-Regular.woff2", "./assets/fonts/WorkSans-Regular.ttf"], 'Work Sans')
container.innerHTML = htmlSetup
container.append(styleTag)
mainComponent = document.getElementById(mainComponentId)
headerComponent = document.getElementById(headerComponentId)
scrollingComponent = document.getElementById(scrollingAreaId)
footerComponent = document.getElementById(footerComponentId)
leftSidebar = document.getElementById(leftSidebarId)
rightSidebar = document.getElementById(rightSidebarId)
leftSidebar.classList.add('hide')
rightSidebar.classList.add('hide')
mainComponent.getElementsByClassName('toggle-sidebar')[0].addEventListener('click', (e) => {
leftSidebar.classList.toggle('hide')
})
mainComponent.getElementsByClassName('toggle-sidebar')[1].addEventListener('click', (e) => {
rightSidebar.classList.toggle('hide')
})
fontFamily = container.querySelector(`[ade-target="#${fontFamilyDropdownId}"]`)
fontFamilyDropdown = document.getElementById(fontFamilyDropdownId)
fontFamilyDropdown.addEventListener('click', changeFontFamily)
reRenderFontDropdown()
fontSize = container.querySelector('[ade-action="font-size-set"]')
fontColor = container.querySelector('[ade-action="font-color"]')
fontColor.addEventListener('input', changeFontColor)
fontColorLabel = container.querySelector('[ade-label="font-color"]')
fontBold = container.querySelector('[ade-action="font-bold"]')
fontBold.addEventListener('click', toggleBold)
fontItalic = container.querySelector('[ade-action="font-italic"]')
fontItalic.addEventListener('click', toggleItalic)
fullScreenLoadingOverlay = document.createElement('div')
fullScreenLoadingOverlay.innerText = 'Loading...'
fullScreenLoadingOverlay.setAttribute('class', 'full-screen-overlay')
})()
!(function handleList() {
let listItems = shadow.querySelectorAll('[adc-toggle="listing-option"]')
let popover = shadow.querySelector('[adc-type="popover"]')
for(let i=0; i<listItems.length; i++){
listItems[i].addEventListener('click', (e) => {
let value = Number(e.target.getAttribute('value'))
value = value?value:0
if (caretData.activeData) {
caretData.activeData.type = (caretData.activeData.type == 1 && caretData.activeData.listStyle==value) ? 0 : 1
if (caretData.activeData.type==1){
caretData.activeData.listStyle = value
}
changeListStyle()
caretData.blink = false
reRenderCanvas()
popover.classList.remove('show')
focusOnPage()
}
})
}
})()
!(function fontSizeHandler(){
let fontSizeInput = shadow.querySelector('[adc="font-size-input"]')
fontSizeInput.value = caretData.style.fontSize
let fontSizeChangers = shadow.querySelectorAll('[adc="font-size-change"]')
fontSizeChangers.forEach( btn => {
btn.addEventListener( 'click',(e)=>{
let change = Number(e.target.getAttribute('value'))
change = change?change:0
change = caretData.style.fontSize + change
change = change?change:config.style.fontSize
caretData.style.fontSize = change
fontSizeInput.value = change
focusOnPage()
} )
} )
function changeFontEvent(e){
let value = Number( e.target.value )
value = value?value:config.style.fontSize
caretData.style.fontSize = value
if (e?.key == 'Enter') focusOnPage()
}
fontSizeInput.addEventListener('keydown', changeFontEvent)
fontSizeInput.addEventListener('input', changeFontEvent)
})()
reConfigure(customConfig)
reRenderCanvas()
addGlobalEvents()
}
function reConfigure(newConfig) {
if (newConfig?.size && paperSizes[newConfig?.size]) { config.pageSetup = { ...paperSizes[newConfig.size], size: newConfig.size } }
else if (newConfig?.width && newConfig?.height) {
config.pageSetup = { size: "Custom", mmWidth: newConfig.width, mmHeight: newConfig.height }
}
else { config.pageSetup = { size: "A4", ...paperSizes['A4'] } }
bindGlobalEvents()
if (typeof window == 'object') {
config.pageSetup.multipler = config.pageSetup.mmHeight / config.pageSetup.mmWidth
config.pageSetup.pxWidth = 2480
config.pageSetup.pxHeight = Math.ceil(config.pageSetup.pxWidth * config.pageSetup.multipler)
} else {
config.pageSetup.pxHeight = config.pageSetup.mmHeight
config.pageSetup.pxWidth = config.pageSetup.mmWidth
}
if (config.zoom) {
config.pageSetup.uiWidth = config.pageSetup.mmWidth * config.zoom
config.pageSetup.uiHeight = config.pageSetup.mmHeight * config.zoom
}
for (let p = 0; p < pageList.length; p++) {
pageList[p].el.style.width = `${config.pageSetup.uiWidth}mm`
}
reRenderPages(dataSet)
pxMmRatio = config.pageSetup.pxWidth / config.pageSetup.mmWidth;
return config
}
function reRenderPages(dataList, option) {
if (renderInProgress) return
renderInProgress = true
let canvasIndex = 0
if (!canvasList.length) canvasList[0] = { el: createNewCanvas(), dataIndex: 0 }
function reRenderCanvas() {
if (isRendering) return
let pageIndex = 0
if (!pageList.length) {
pageList[pageIndex] = { id: ++counter, el: createNewPage(), dataIndex: 0 }
shadow.querySelector('.page-list').append(pageList[pageIndex].el)
}
// to clear the canvases
for (let i = 0; i < canvasList.length; i++) {
let ctx = canvasList[i].el.getContext('2d', { willReadFrequently: true })
for (let i = 0; i < pageList.length; i++) {
let ctx = pageList[i].el.getContext('2d', { willReadFrequently: true })
ctx.save()
ctx.clearRect(0, 0, canvasList[i].el.width, canvasList[i].el.height); // clears the canvas
ctx.clearRect(0, 0, pageList[i].el.width, pageList[i].el.height); // clears the canvas
ctx.fillStyle = config.format.background
ctx.fillRect(0, 0, canvasList[i].el.width, canvasList[i].el.height)
ctx.fillRect(0, 0, pageList[i].el.width, pageList[i].el.height)
ctx.restore()
}
lines = []
for (let i = 0; i < dataList.length; i++) {
calculateTextSizeAndPosition(canvasIndex, i)
}
renderTheLines()
if (!caretData.blink && caretData.activeData) renderCaret()
lines = []
function calculateTextSizeAndPosition(canvasIndex, dataSetIndex) {
let dataBlock = dataList[dataSetIndex]
let canvas = canvasList[canvasIndex].el
let ctx = canvas.getContext('2d', { willReadFrequently: true })
ctx.save()
calculateTextSizeAndPosition()
renderLines()
caretData.previousCaret = null
renderCaret(true)
// to calculate the lines
let dataLineArr = []
function calculateTextSizeAndPosition() {
let d = 0, c = 0;
function getLineObj() {
let newLineObj = {
...config.style,
x: 0, // this is the starting point x; it will change based on the tabNumber
y: 0, // this is the starting y coordinate; it will change based on the max font size
plainContent: "",
dataSetIndex: dataSetIndex,
dataIndex: d,
charStartIndex: 0, // index from where to check
charEndIndex: 0, // index till where to check// not including this index.
listItemNumber: 0,
}
newLineObj.maxFontSize = newLineObj.fontSize
return newLineObj
}
if (!dataBlock.formatedText) dataBlock.formatedText = []
let lineObj = new getLineObj()
dataLineArr.push(lineObj)
lineObj.type = dataBlock.type
lineObj.blockStart = true
let wordEndIndex = 0; // this stores the index of the word which can fit in the line;
let tempLineWidth = 0;
let maxLineWidth = config.pageSetup.canvasWidth - (config.format.margin * 2 * config.pageSetup.canvasMultiplier)
if (dataBlock.tabCount || dataBlock.type == 1) {
let finalTabCount = dataBlock.tabCount
for (d = 0; d < dataList.length; d++) {
let lineObj = getLineObj()
let dataBlock = dataList[d]
let canvas = pageList[pageIndex].el
let ctx = canvas.getContext('2d', { willReadFrequently: true })
ctx.save()
// to calculate the lines
let tempLineWidth = 0;
let maxLineWidth = config.pageSetup.pxWidth - (config.format.margin * pxMmRatio * 2)
let tabWidth = ((dataBlock.type == 1 ? 1 : 0) + dataBlock.tabCount) * config.format.tabWidth * pxMmRatio
lineObj.tabWidth = tabWidth
maxLineWidth -= tabWidth
lineObj.dataType = dataBlock.type
let wordEndIndex = 0; // this stores the index of the word which can fit in the line;
let tempWordWidth = 0
if (dataBlock.type == 1) {
finalTabCount++
}
let tabDistance = finalTabCount * config.format.tabWidth * config.pageSetup.canvasMultiplier
if (tabDistance > maxLineWidth * 5 / 6) { tabDistance = 0 }
maxLineWidth = maxLineWidth - tabDistance
}
lineObj.maxLineWidth = maxLineWidth
lineObj.tabCount = dataBlock.tabCount
// // for checking the listIndex
if (dataBlock.type == 1) {
for (let i = dataSetIndex - 1; i >= 0; i--) {
if (dataList[i].type != 1) {
dataBlock.listIndex = 0
break;
} else if (dataList[i].tabCount == dataBlock.tabCount) {
dataBlock.listIndex = dataList[i].listIndex + 1
break
} else if (dataList[i].tabCount < dataBlock.tabCount) {
dataBlock.listIndex = 0
break;
let previousBlock = dataList[d - 1]
if (!previousBlock || previousBlock?.type != 1) dataBlock.listItemNumber = 1
else if (previousBlock.tabCount == dataBlock.tabCount) {
dataBlock.listItemNumber = previousBlock.listItemNumber + 1
} else {
dataBlock.listItemNumber = 1
let olderBlockIndex = d - 1
while (dataList?.[olderBlockIndex]?.type == 1 && dataList?.[olderBlockIndex]?.tabCount >= dataBlock.tabCount) {
if (dataList?.[olderBlockIndex]?.tabCount == dataBlock.tabCount) {
dataBlock.listItemNumber = dataList?.[olderBlockIndex].listItemNumber + 1
break;
}
--olderBlockIndex;
}
}
}
}
lineObj.listIndex = dataBlock.listIndex
let i = 0
for (i = 0; i < dataBlock.plainContent.length; i++) {
let style = dataBlock?.formatedText?.[i]
if (!style) style = JSON.parse(JSON.stringify(config.style))
if (/\s/.test(dataBlock.plainContent[i])) {
wordEndIndex = i
lineObj.charEndIndex = wordEndIndex;
}
let charWidth = getCharacterWidth(canvasIndex, dataBlock.plainContent[i], style)
dataBlock.formatedText[i] = {
...config.style,
...dataBlock.formatedText[i],
width: charWidth
}
for (c = 0; c < dataBlock.plainContent.length; c++) {
let style = dataBlock?.formatedText?.[c]
lineObj.maxFontSize = (lineObj.maxFontSize<dataBlock?.formatedText?.[c].fontSize)?dataBlock?.formatedText?.[c].fontSize:lineObj.maxFontSize
if (/\s/.test(dataBlock.plainContent[c])) {
// condition to check if a blank character is found.
wordEndIndex = c
lineObj.charEndIndex = c
tempWordWidth = 0
}
let charWidth = getCharacterWidth(dataBlock.plainContent[c], style)
lineObj.maxFontSize = (!lineObj.maxFontSize)?style.fontSize:( style.fontSize>lineObj.maxFontSize?style.fontSize:lineObj.maxFontSize )
// lineObj.fontSize = style.fontSize
tempLineWidth += charWidth
if (tempLineWidth <= maxLineWidth) {
// can be added to the line //
} else {
// cannot add this// new line should be added//
i = wordEndIndex;
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex + 1)
lineObj = new getLineObj()
lineObj.listIndex = dataBlock.listIndex
lineObj.maxLineWidth = maxLineWidth
lineObj.tabCount = dataBlock.tabCount
lineObj.charStartIndex = i
lineObj.charEndIndex = i
dataLineArr.push(lineObj)
tempLineWidth = 0
}
dataBlock.formatedText[c] = {
...config.style,
...style,
width: charWidth
}
}
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex + 1)
// there is chance that the last line is not at the width// so we need to handle the last line separately
if (lineObj.charEndIndex <= dataBlock.plainContent.length) {
lineObj.charEndIndex = dataBlock.plainContent.length - 1
}
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex)
// // if with the current char the string could not fit on one line
if (tempLineWidth + charWidth > maxLineWidth) {
// cannot add this// new line should be added//
lines.push(...dataLineArr)
if (tempWordWidth + charWidth >= maxLineWidth) {
// this is to manage the casse where there is a set of long string without any blank space. In that case the text is broken and moved to next line.
wordEndIndex = c
lineObj.charEndIndex = c
tempWordWidth = 0
}
ctx.restore()
return true
}
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex + 1)
lines.push(lineObj)
lineObj = new getLineObj()
lineObj.maxLineWidth = maxLineWidth
lineObj.maxFontSize = style.fontSize
if (dataBlock.type == 1){
lineObj.tabWidth = tabWidth
lineObj.tabCount = dataBlock.tabCount
}else{
maxLineWidth = config.pageSetup.pxWidth - (config.format.margin * pxMmRatio * 2)
lineObj.tabWidth = 0
lineObj.tabCount = 0
}
lineObj.charStartIndex = wordEndIndex + 1
lineObj.charEndIndex = wordEndIndex + 1
tempLineWidth = tempWordWidth
}
else {
// if the char can fit in the same line. then it is well and good
tempLineWidth += charWidth
tempWordWidth += charWidth
}
function renderTheLines() {
let canvasIndex = 0
let ctx = canvasList[canvasIndex].el.getContext('2d', { willReadFrequently: true })
let x = 0
let y = (config.format.margin * config.pageSetup.canvasMultiplier)
let maxVericalWidth = (config.pageSetup.canvasHeight - config.format.margin * config.pageSetup.canvasMultiplier * 2); // this is the height of writable area; so this excludes the margin from top and bottom.
for (let l = 0; l < lines.length; l++) {
x = (config.format.margin * config.pageSetup.canvasMultiplier);
}
y += (lines[l].maxFontSize * config.pageSetup.fontMultiplier)
if (lines[l].blockStart && l != 0) y += lines[l].maxFontSize
if ((maxVericalWidth + lines[l].maxFontSize) < (y - lines[l].maxFontSize)) {
canvasIndex++
if (!canvasList[canvasIndex]) {
canvasList[canvasIndex] = { id: ++counter, el: createNewCanvas(), dataIndex: lines[l].dataSetIndex, lineIndex: l }
}
ctx = canvasList[canvasIndex].el.getContext('2d', { willReadFrequently: true })
y = lines[l].maxFontSize + (config.format.margin * config.pageSetup.canvasMultiplier)
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex + 1)
// there is chance that the last line is not at the width// so we need to handle the last line separately
if (lineObj.charEndIndex <= dataBlock.plainContent.length) {
lineObj.charEndIndex = dataBlock.plainContent.length - 1
}
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex)
lines.push(lineObj)
let setData = dataSet[lines[l].dataSetIndex]
ctx.restore()
}
return
}
function renderLines() {
let x = 0, y = config.format.margin * pxMmRatio;
for (let l = 0; l < lines.length; l++) {
let setData = dataList[lines[l].dataIndex]
let ctx = pageList[0].el.getContext('2d', { willReadFrequently: true })
x = config.format.margin * pxMmRatio
x += lines[l].tabWidth
y = y + (lines[l].maxFontSize * pxMmRatio)
lines[l].y = y
if (lines[l].listIndex >= 0) {
let totalTabDistance = config.format.tabWidth * config.pageSetup.canvasMultiplier * (lines[l].tabCount + 1)
x += totalTabDistance;
let numberX = x - config.format.tabWidth * config.pageSetup.canvasMultiplier / 2
if (lines[l].blockStart) {
let style = {
...config.style,
...setData.style
}
ctx.save()
ctx.fillStyle = `${style.fontColor}`
// 10 * Math.PI/180 = 0.174
ctx.setTransform(1, 0.174, 0, 1, 0, 0)
ctx.font = `${style.bold ? 'bold ' : ''} ${style.fontSize * config.pageSetup.fontMultiplier}px ${style.fontFamily}`
ctx.fillText(`${lines[l].listIndex + 1}.`, numberX, y)
ctx.restore()
}
ctx.save()
// this is to render the numbering and bullets etc
if (lines[l].dataType == 1) {
ctx.save()
ctx.font = `${setData.style.fontSize * pxMmRatio}px ${setData.style.fontFamily}`
ctx.fillStyle = `${setData.style.fontColor}`
let label = getLabel(lines[l].dataIndex)
let labelWidth = ctx.measureText(label).width
labelWidth += pxMmRatio * 5
ctx.fillText(label, x - labelWidth, y)
ctx.restore()
}
lines[l].x = x
lines[l].canvasIndex = canvasIndex
for (let c = lines[l].charStartIndex; c <= lines[l].charEndIndex; c++) {
let char = setData?.plainContent[c]
if (char) {
let style = setData.formatedText[c]
ctx.save()
ctx.font = `${style?.bold ? 'bold ' : ''}${style?.italic ? 'italic ' : ''} ${style.fontSize * config.pageSetup.fontMultiplier}px ${style.fontFamily}`
ctx.font = `${style?.bold ? 'bold ' : ''}${style?.italic ? 'italic ' : ''} ${style.fontSize * pxMmRatio}px ${style.fontFamily}`
ctx.fillStyle = `${style?.fontColor}`
ctx.fillText(char, x, y)
setData.formatedText[c].x = x
......@@ -474,414 +424,275 @@ var ADocEditor = function (customConfig) {
}
}
ctx.restore()
}
return
}
// to render caret
function renderCaret() {
let ctx = canvasList[caretData.canvasIndex].el.getContext('2d', { willReadFrequently: true })
ctx.save()
function getLabel(dataIndex) {
let label = "x."
if (dataList[dataIndex].listStyle == 0) { // 1. a. i.
let tabCount = dataList[dataIndex].tabCount
tabCount = tabCount ? tabCount : 0
if (tabCount % 3 == 0) label = `${dataList[dataIndex].listItemNumber}.`
else if (tabCount % 3 == 1) label = `${convertToLetter(dataList[dataIndex].listItemNumber)}.`
else label = `${convertToRoman(dataList[dataIndex].listItemNumber).toLowerCase()}.`
}
else if (dataList[dataIndex].listStyle == 1) { // >
label = `>`
}
let activeDataIndex = dataSet.findIndex(item => item.id == caretData.activeData.id)
let activeLine = lines.find((item, i) => {
if (item.dataSetIndex == activeDataIndex) {
if (item.charEndIndex == -1) return true
if (item.charEndIndex >= caretData.index) return true
if (!(lines[i + 1]?.dataSetIndex == activeDataIndex)) return true
else false
} else return false
})
function convertToLetter(num) {
num = (num-1).toString(26)
let label = ""
for(let i=0;i<num.length;i++) label+= letters[num[i]]
// handle left right on multiple pages
if (activeLine) {
if (!isModule && option?.scrollIntoView) canvasList[activeLine.canvasIndex].el.focus()
caretData.canvasIndex = activeLine.canvasIndex
ctx.restore()
ctx = canvasList[activeLine.canvasIndex].el.getContext('2d', { willReadFrequently: true })
ctx.save()
return label
}
function convertToRoman(num) {
let characterData = dataSet[activeDataIndex].formatedText[caretData.index]
let result = '';
let divisor = 1000;
let rectX = characterData?.x,
rectY = activeLine.y - (activeLine.maxFontSize * config.pageSetup.fontMultiplier),
rectWidth = 2,
rectHeight = 5 * (activeLine.maxFontSize * config.pageSetup.fontMultiplier) / 4;
if (!characterData && dataSet[activeDataIndex].formatedText?.[caretData.index - 1]) {
rectX = dataSet[activeDataIndex].formatedText?.[caretData.index - 1]?.x + dataSet[activeDataIndex].formatedText?.[caretData.index - 1]?.width
}
if (activeLine.charEndIndex == caretData.index) {
rectX = dataSet[activeDataIndex].formatedText?.[caretData.index - 1]?.x + dataSet[activeDataIndex].formatedText?.[caretData.index - 1]?.width
}
if (!(rectX > 0 || rectX == 0)) { rectX = activeLine.x }
for (let i = 3; i >= 0; i--) {
const digit = Math.floor(num / divisor);
num %= divisor;
divisor /= 10;
const imageData = ctx.getImageData(rectX, rectY, rectWidth, rectHeight);
const data = imageData.data;
if (digit > 0) {
if (digit === 9) {
result += romanNumerals[i][3];
} else if (digit >= 5) {
result += romanNumerals[i][2];
result += romanNumerals[i][0].repeat(digit - 5);
} else if (digit === 4) {
result += romanNumerals[i][1];
} else {
result += romanNumerals[i][0].repeat(digit);
}
}
}
// Invert the color of the rectangular region
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // Red
data[i + 1] = 255 - data[i + 1]; // Green
data[i + 2] = 255 - data[i + 2]; // Blue
// Alpha channel remains unchanged (data[i + 3])
ctx.putImageData(imageData, rectX, rectY);
}
if (characterData) {
caretData.style = { ...characterData }
} else {
caretData.style = { ...caretData.activeData.style }
return result;
}
ctx.restore()
return label
}
function getCharacterWidth(canvasIndex, char, style) {
let canvas = canvasList[canvasIndex].el
function getCharacterWidth(char, style) {
let canvas = pageList[0].el
let ctx = canvas.getContext('2d', { willReadFrequently: true })
ctx.save()
ctx.font = `${style?.bold ? 'bold ' : ''}${style?.italic ? 'italic ' : ''} ${style.fontSize * config.pageSetup.fontMultiplier}px ${style.fontFamily}`
ctx.font = `${style?.bold ? 'bold ' : ''}${style?.italic ? 'italic ' : ''} ${style.fontSize * pxMmRatio}px ${style.fontFamily}`
ctx.fillStyle = `${style?.fontColor}`
let width = ctx.measureText(char).width
ctx.restore()
return width
}
function createNewCanvas() {
function createNewPage() {
let canvas;
if (isModule) {
const { createCanvas } = require('canvas')
canvas = createCanvas(config.pageSetup.canvasWidth, config.pageSetup.canvasHeight)
} else {
canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d', { willReadFrequently: true })
canvas.setAttribute('class', 'page')
canvas.width = config.pageSetup.canvasWidth
canvas.height = config.pageSetup.canvasHeight
canvas.setAttribute("tabIndex", -1)
canvas.setAttribute('tabIndex', '-1')
canvas.width = config.pageSetup.pxWidth
canvas.height = config.pageSetup.pxHeight
canvas.style.width = `${config.pageSetup.uiWidth}mm`
let ctx = canvas.getContext('2d', { willReadFrequently: true })
ctx.fillStyle = config.format.background
ctx.fillRect(0, 0, canvas.width, canvas.height)
canvas.addEventListener('keydown', keydownHandler)
canvas.addEventListener('mousedown', mousedownHandler)
canvas.addEventListener("focus", onFocusHandler)
canvas.addEventListener("blur", onBlurHandler)
canvas.setAttribute("tabIndex", -1)
scrollingComponent.append(canvas)
}
return canvas
}
function onFocusHandler(e) {
focusedCanvas = e.target
caretData.blink = false
reRenderPages(dataSet)
}
function onBlurHandler(e) {
lastFocusCanvas = e.target
focusedCanvas = null
caretData.blink = false
reRenderPages(dataSet, { scrollIntoView: false })
clearInterval(caretData.interval)
}
function keydownHandler(e) {
caretData.blink = false
if (e.shiftKey && (e.ctrlKey || e.metaKey)) {
return
}
else if (e.keyCode == 16) { // only shift key
return
}
// ctr+shift combination
else if (e.ctrlKey || e.metaKey) { // only ctrl or meta key
if (e.keyCode == 8) { // ctrl+backspace: delete the entrite word
let separatedSentence = caretData.activeData.plainContent.slice(0, caretData.index)
let indexOfpreviousBlankChar = separatedSentence.search(/[^a-zA-Z0-9](?=[a-zA-Z0-9]*$)/)
if (indexOfpreviousBlankChar <= 0) caretData.activeData.plainContent = ""
else caretData.activeData.plainContent = caretData.activeData.plainContent.slice(0, indexOfpreviousBlankChar) + caretData.activeData.plainContent.slice(caretData.index)
caretData.index = indexOfpreviousBlankChar <= 0 ? 0 : indexOfpreviousBlankChar
}
}
else if (e.keyCode == 9) {// tab input // no need to add case for casesensitivity
if (e.altKey) return
else if (e.key == 'Tab') {
e.preventDefault()
}
else if (e.keyCode == 13) { // Enter Key
if (caretData) {
if (caretData.activeData.type == 0 || caretData.activeData.type == 1) { // for plain text // just go to next line
let style = caretData?.activeData?.formatedText?.[caretData.activeData.formatedText.length - 1]
if (!style) style = caretData?.activeData?.style
if (!style) style = config.style
let newLineData = {
id: ++counter,
type: caretData.activeData.type,
plainContent: "",
style: JSON.parse(JSON.stringify(style))
manageIndentation(e.shiftKey ? -1 : 1)
} else if ([']', '['].includes(e.key) && e.ctrlKey) {
manageIndentation(e.key == '[' ? -1 : 1)
}
else if (e.key == 'Backspace') {
if (caretData.index == 0) {
let activeDataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
if (caretData.activeData.tabCount) {
if (caretData.activeData.type==1){
caretData.activeData.type = 0
}else{
caretData.activeData.tabCount--
}
if (typeof caretData.activeData.tabCount == 'number') newLineData.tabCount = caretData.activeData.tabCount
newLineData.plainContent = caretData.activeData.plainContent.slice(caretData.index)
caretData.activeData.plainContent = caretData.activeData.plainContent.slice(0, caretData.index)
let currentDataIndex = dataSet.findIndex(item => item.id == caretData.activeData.id)
dataSet.splice(currentDataIndex + 1, 0, newLineData)
caretData.activeData = dataSet[currentDataIndex + 1]
caretData.y += style.fontSize
caretData.index = 0
}
}
}
else if (e.key.length == 1 || e.keyCode == 32) { // these are printable characters key
e.preventDefault()
caretData.style.fontFamily = fontFamily.innerText
caretData.style.fontSize = Number(fontSize.value)
caretData.style.fontColor = fontColor.value
caretData.style.bold = fontBold.classList.contains('selected')
caretData.style.italic = fontItalic.classList.contains('selected')
caretData.activeData.formatedText.splice(caretData.index, 0, { ...caretData.style })
caretData.activeData.plainContent = caretData.activeData.plainContent.slice(0, caretData.index) + e.key + caretData.activeData.plainContent.slice(caretData.index)
++caretData.index
}
else if (e.keyCode == 8) { // backspace
if (caretData.index <= 0) {
let currentIndex = dataSet.findIndex(item => item.id == caretData.activeData.id)
if (currentIndex > 0) {
caretData.activeData = dataSet[currentIndex - 1]
caretData.index = dataSet[currentIndex - 1].plainContent.length
if (dataSet[currentIndex].plainContent.length != 0) {
caretData.activeData.plainContent += dataSet[currentIndex].plainContent
caretData.activeData.formatedText.push(...dataSet[currentIndex].formatedText)
console.log()
else if (activeDataIndex > 0) {
if (caretData.activeData.type == 0) {
if (!caretData.activeData.plainContent.length) { dataList.splice(activeDataIndex, 1) }
caretData.activeData = dataList[activeDataIndex - 1]
caretData.index = caretData.activeData.plainContent.length
} else if (caretData.activeData.type == 1) {
caretData.activeData.type = 0
}
dataSet.splice(currentIndex, 1)
}
} else {
caretData.activeData.plainContent = caretData.activeData.plainContent.slice(0, caretData.index - 1) + caretData.activeData.plainContent.slice(caretData.index)
caretData.index = caretData.index - 1
caretData.activeData.formatedText.splice( caretData.index, 1 )
caretData.activeData.formatedText.splice(caretData.index - 1, 1)
--caretData.index;
}
}
else if (e.keyCode == 37) { // left key
else if (e.key == 'Enter') {
let dataObj = {
id: ++counter,
type: caretData.activeData.type,
formatedText: [],
plainContent: "",
listStyle: caretData.activeData.listStyle,
tabCount: caretData.activeData.tabCount,
style: { ...caretData.activeData.style }
}
if (e.ctrlKey || e.metaKey) dataObj.newPage = true
caretData.index = 0
caretData.activeData = dataObj
if (dataObj.type == 0) dataObj.tabCount = 0
dataList.push(dataObj)
}
else if (e.key.length == 1 && !e.ctrlKey && !e.metaKey) { // displayable text
// ***** DISPLAYABLE TEXT **** //
e.preventDefault()
if (caretData.index <= 0) {
let currentIndex = dataSet.findIndex(item => item.id == caretData.activeData.id)
let previousData = dataSet[currentIndex - 1]
if (previousData) {
caretData.activeData = previousData
caretData.index = previousData.plainContent.length
}
caretData.canvasIndex = canvasList.findIndex(item => item.el == e.target)
caretData.activeData.plainContent = caretData.activeData.plainContent.slice(0, caretData.index) + e.key + caretData.activeData.plainContent.slice(caretData.index)
caretData.activeData.formatedText.splice(caretData.index, 0, caretData.style)
++caretData.index
}
else if (e.key == 'ArrowLeft') {
if (caretData.index) {
--caretData.index
} else {
caretData.index = (caretData.index <= 0) ? 0 : caretData.index - 1
let dataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
if (dataIndex > 0) {
caretData.activeData = dataList[dataIndex - 1]
caretData.index = caretData.activeData.plainContent.length
}
}
reRenderPages(dataSet, { onlyCursor: true })
return
}
else if (e.keyCode == 38) { // up key
e.preventDefault()
}
else if (e.keyCode == 39) { // right key
e.preventDefault()
if (caretData.index <= caretData.activeData.plainContent.length - 1) {
caretData.index = (caretData.index >= caretData.activeData.plainContent.length) ? caretData.activeData.plainContent.length : caretData.index + 1
else if (e.key == 'ArrowRight') {
if (caretData.index < caretData.activeData.plainContent.length) {
++caretData.index
} else {
let currentIndex = dataSet.findIndex(item => item.id == caretData.activeData.id)
let nextData = dataSet[currentIndex + 1]
if (nextData) {
let dataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
if (dataList[dataIndex + 1]) {
caretData.activeData = dataList[dataIndex + 1]
caretData.index = 0
caretData.activeData = nextData
}
}
caretData.canvasIndex = canvasList.findIndex(item => item.el == e.target)
reRenderPages(dataSet, { onlyCursor: true })
return
}
else if (e.keyCode == 40) { // down key
e.preventDefault()
else if (e.key == 'ArrowUp') {
console.log('Up')
}
else { } // unhandled cases
reRenderPages(dataSet)
clearInterval(caretData.interval)
}
function setCaretPosition(e) {
let canvasIndex = canvasList.findIndex(item => item.el == e.target)
let rect = e.target.getBoundingClientRect()
let position = {
x: (e.offsetX / rect.width) * config.pageSetup.canvasWidth,
y: (e.offsetY / rect.height) * config.pageSetup.canvasHeight,
else if (e.key == 'ArrowDown') {
console.log('Down')
}
let closestLine = lines.find((item, i) => {
let thisLine = (item.y + item.maxFontSize / 4) > position.y && canvasIndex == item.canvasIndex
if (thisLine) return true
else if (!(lines[i + 1]) || (lines[i + 1]?.canvasIndex > canvasIndex)) return true
return false
})
if (closestLine) {
caretData.activeData = dataSet[closestLine.dataSetIndex]
let charIndex = closestLine.charStartIndex
let found = false
for (let i = charIndex; (i <= closestLine.charEndIndex && caretData.activeData.formatedText[i].y == closestLine.y); i++) {
let formatedText = caretData.activeData.formatedText[i]
if (position.x < (formatedText.x + formatedText.width)) {
charIndex = i
found = true
break;
}
charIndex = i + 1
}
if (/\s+/.test(caretData.activeData.plainContent[charIndex]) && caretData.activeData.plainContent[charIndex + 1] && charIndex == closestLine.charStartIndex) {
charIndex++
}
caretData.index = charIndex
function manageIndentation(value) {
caretData.activeData.tabCount += value
if (caretData.activeData.tabCount < 0) caretData.activeData.tabCount = 0
else if (caretData.activeData.tabCount > 5) caretData.activeData.tabCount = 5
return true
}
reRenderPages(dataSet)
}
function mousedownHandler(e) {
e.target.focus({ preventScroll: true });
setCaretPosition(e)
}
renderInProgress = false
}
function removeStyles(dataList) {
for (let i = 0; i < dataList.length; i++) {
dataList[i].formatedText = []
for (let j = 0; j < dataList[i].plainContent.length; j++) {
dataList[i].formatedText.push(JSON.parse(JSON.stringify(config.style)))
}
}
}
async function generatePDF(file) {
showLoader()
let embededFonts = {}
// Create a new PDFDocument
const pdfDoc = await PDFDocument.create()
function embedFont(name) {
return new Promise((res, rej) => {
let fontObj = fontList.find(item => item.name == name)
let url = fontObj.paths.find(item => item.slice(-4) == '.ttf')
if (!url) fontObj.paths[0]
fetch(url)
.then(async (data) => {
let arrayBuffer = await data.arrayBuffer()
embededFonts[name] = await pdfDoc.embedFont(arrayBuffer)
res(embededFonts[name])
}).catch((err) => {
rej(err)
})
})
caretData.blink = false
caretData.previousCaret = null
reRenderCanvas()
}
// register font-kit
pdfDoc.registerFontkit(fontkit)
// Add a blank page to the document
var page = pdfDoc.addPage([config.pageSetup.width, config.pageSetup.height])
let canvasIndex = 0
let x = 0
let y = (config.format.margin * config.pageSetup.canvasMultiplier)
let maxVericalWidth = (config.pageSetup.canvasHeight - config.format.margin * config.pageSetup.canvasMultiplier * 2)
function placeCaret(cursor = { x: 0, y: 0 }) {
for (let l = 0; l < lines.length; l++) {
x = (config.format.margin * config.pageSetup.canvasMultiplier);
y += (lines[l].maxFontSize * config.pageSetup.fontMultiplier)
if (lines[l].blockStart && l != 0) y += lines[l].maxFontSize
if ((maxVericalWidth + lines[l].maxFontSize) < (y - lines[l].maxFontSize)) {
canvasIndex++
page = pdfDoc.addPage([config.pageSetup.width, config.pageSetup.height])
y = lines[l].maxFontSize + (config.format.margin * config.pageSetup.canvasMultiplier)
}
let found = false
for (let l = 0; l < lines.length; l++) {
if ((cursor.y <= lines[l].y) && (cursor.y >= (lines[l].maxFontSize * pxMmRatio))) {
let dataSet = dataList[lines[l].dataIndex]
for (let c = lines[l].charStartIndex; c <= lines[l].charEndIndex; c++) {
if ((cursor.x >= dataSet.formatedText[c].x) && (cursor.x <= (dataSet.formatedText[c].width + dataSet.formatedText[c].x))) {
caretData.activeData = dataSet
if (cursor.x <= dataSet.formatedText[c].x + dataSet.formatedText[c].width / 2) {
caretData.index = c
} else {
caretData.index = c + 1
}
found = true
caretData.blink = false
renderCaret(true)
return
}
let setData = dataSet[lines[l].dataSetIndex]
lines[l].y = y
if (lines[l].listIndex >= 0) {
let totalTabDistance = config.format.tabWidth * config.pageSetup.canvasMultiplier * (lines[l].tabCount + 1)
x += totalTabDistance;
let numberX = x - config.format.tabWidth * config.pageSetup.canvasMultiplier / 2
if (lines[l].blockStart) {
let style = {
...config.style,
...setData.style
}
if (!embededFonts[style.fontFamily]) await embedFont(style.fontFamily)
page.drawText(`${lines[l].listIndex + 1}.`, {
x: numberX / config.pageSetup.canvasMultiplier,
y: (config.pageSetup.canvasHeight - y) / config.pageSetup.canvasMultiplier,
size: (style.fontSize * config.pageSetup.fontMultiplier) / config.pageSetup.canvasMultiplier,
font: embededFonts[style.fontFamily],
color: rgb( ...getRgbArrayFromHex( style.fontColor ) ),
})
if (found) break; // break only when the caret index is determined
}
}
lines[l].x = x
lines[l].canvasIndex = canvasIndex
for (let c = lines[l].charStartIndex; c <= lines[l].charEndIndex; c++) {
let char = setData?.plainContent[c]
if (char) {
let style = setData.formatedText[c]
if (!embededFonts[style.fontFamily]) await embedFont(style.fontFamily)
page.drawText(dataSet[lines[l].dataSetIndex].plainContent[c], {
x: style.x / config.pageSetup.canvasMultiplier,
y: (config.pageSetup.canvasHeight - style.y) / config.pageSetup.canvasMultiplier,
size: (style.fontSize * config.pageSetup.fontMultiplier) / config.pageSetup.canvasMultiplier,
font: embededFonts[style.fontFamily],
color: rgb( ...getRgbArrayFromHex( style.fontColor ) ),
ySkew: { type: 'radians', angle: style.italic?0.174:0 },
})
setData.formatedText[c].x = x
setData.formatedText[c].y = y
if (setData.formatedText[c]?.width) {
x += setData.formatedText[c]?.width
}
}
if (!found) {
console.log('no char found')
}
}
const pdfBytes = await pdfDoc.save()
const blob = new Blob([pdfBytes], { type: "application/pdf" });
const downloadLink = document.createElement("a");
document.body.appendChild(downloadLink);
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = file; // Set desired filename
downloadLink.style.display = "none"; // Keep it hidden
downloadLink.click()
document.body.removeChild(downloadLink)
hideLoader()
function mousedownHandler(e) {
if (focussedPage) {
const rect = e.target.getBoundingClientRect(); // Get the position of the canvas
const pxX = (e.clientX - rect.left) * config.pageSetup.pxWidth / rect.width;
const pxY = (e.clientY - rect.top) * config.pageSetup.pxHeight / rect.height;
placeCaret({ x: pxX, y: pxY })
let style = caretData.activeData.formatedText[caretData.index]
if (style) caretData.style = { ...caretData.style,
fontSize: style.fontSize,
}
shadow.querySelector('[adc="font-size-input"]').value = caretData.style.fontSize
}
}
function onFocusHandler(e) {
focussedPage = e.target
lastFocussedPage = e.target
reRenderCanvas()
}
function onBlurHandler(e) {
caretData.blink = true
focussedPage = null
}
}
function getRgbArrayFromHex(hexString){
let rgbArr= [ 0,0,0 ]
if (hexString.length==4 || hexString.length==5){
rgbArr = [ parseInt(hexString[1], 16)/15, parseInt(hexString[2], 16)/15, parseInt(hexString[3], 16)/15 ]
}else if (hexString.length==7 || hexString.length==9){
rgbArr = [ parseInt(hexString.slice(1,3), 16)/255, parseInt(hexString.slice(3,5), 16)/255, parseInt(hexString.slice(5,7), 16)/255 ]
function changeListStyle(){
let activeDataIndex = dataList.findIndex( item => item.id == caretData.activeData.id )
let d = activeDataIndex-1
while( dataList?.[d]?.type==1){
dataList[d].listStyle = caretData.activeData.listStyle
--d
}
d = activeDataIndex+1
while(dataList?.[d]?.type==1){
dataList[d].listStyle = caretData.activeData.listStyle
++d
}
return rgbArr
}
function addFonts(paths, name) {
if (typeof paths == 'string') paths = [paths]
let fontObj = {
......@@ -909,179 +720,132 @@ var ADocEditor = function (customConfig) {
}
}
// // these are methods related to the toolbar items
function changeFontFamily(e) {
if (e) {
caretData.style.fontFamily = e.target.innerText
caretData.styleModified = false
focusLastCanvas()
if (!caretData.activeData.plainContent.length) {
caretData.activeData.style.fontFamily = e.target.innerText
function reRenderFontDropdown() {
let fontFamilyDropdown = headerToolbar.find(item => item.getAttribute('adc') == 'font-select')
if (fontFamilyDropdown) {
fontFamilyDropdown.innerHTML = ""
fontList.forEach((font, i) => {
let optionTag = document.createElement('option')
optionTag.setAttribute('value', font.name)
optionTag.style.fontFamily = font.name
optionTag.innerText = font.name
fontFamilyDropdown.append(optionTag)
})
}
} else {
let labels = container.querySelectorAll(`[ade-target="#${fontFamilyDropdown.id}"]`)
labels.forEach(item => item.innerText = caretData.style.fontFamily)
}
}
changeFontFamily()
function changeFontSize(change){
fontSize.value = change
caretData.style.fontSize = change
focusLastCanvas()
if (!caretData.activeData.plainContent.length) {
caretData.activeData.style.fontSize = change
}
}
function changeFontColor(e){
fontColorLabel.style.backgroundColor = e.target.value
caretData.style.fontColor = e.target.value
focusLastCanvas()
if (!caretData.activeData.plainContent.length) {
caretData.activeData.style.fontColor = e.target.value
function changeFontFamily() {
let fontFamilyDropdown = headerToolbar.find(item => item.getAttribute('adc') == 'font-select')
if (fontFamilyDropdown) fontFamilyDropdown.value = caretData.style.fontFamily
}
}
function toggleBold(e){
let isBold = fontBold.classList.contains('selected')
caretData.style.bold = !isBold
if (!isBold) fontBold.classList.add('selected')
else fontBold.classList.remove('selected')
focusLastCanvas()
if (!caretData.activeData.plainContent.length) {
caretData.activeData.style.bold = !isBold
}
}
function toggleItalic(e){
let isItalic = fontItalic.classList.contains('selected')
caretData.style.italic = !isItalic
if (!isItalic) fontItalic.classList.add('selected')
else fontItalic.classList.remove('selected')
focusLastCanvas()
if (!caretData.activeData.plainContent.length) {
caretData.activeData.style.italic = !isItalic
}
}
function focusLastCanvas(){
if (!lastFocusCanvas) lastFocusCanvas = canvasList[0].el
if (lastFocusCanvas) {
lastFocusCanvas.focus({ preventScroll: true })
}
}
function onMouseWheelHandler(e) {
if (e.ctrlKey || e.metaKey) e.preventDefault()
}
function reRenderFontDropdown() {
if (fontFamilyDropdown) {
fontFamilyDropdown.innerHTML = ""
fontList.forEach((font, i) => {
let optionTag = document.createElement('div')
optionTag.setAttribute('ade-type', 'option')
optionTag.setAttribute('class', 'dropdown-option')
optionTag.setAttribute('value', font.name)
optionTag.style.fontFamily = font.name
optionTag.value = font.name
optionTag.innerText = font.name
fontFamilyDropdown.append(optionTag)
})
function addGlobalEvents(e) {
shadow.querySelector('.page-list').addEventListener('wheel', onMouseWheelHandler)
shadow.addEventListener('mousedown', globalMouseDownHandler)
}
function globalMouseDownHandler(e) {
var elem = e.target
var targetId = elem.getAttribute('adc-target')
if (elem.getAttribute('adc-type') == 'popover') { return }
while (!targetId && elem) {
elem = elem.parentNode
if (elem?.getAttribute?.('adc-type') == 'popover') return
targetId = elem?.getAttribute?.('adc-target')
}
changeFontFamily()
let allPopovers = shadow.querySelectorAll('[adc-type="popover"]')
allPopovers.forEach(item => {
if (item.getAttribute('adc') == targetId) item.classList.toggle('show')
else item.classList.remove('show')
})
}
function bindGlobalEvents() {
let options = container.querySelectorAll('[ade-target]')
options.forEach(option => {
let target = container.querySelector(option.getAttribute('ade-target'))
option.innerText = target?.children?.[0]?.innerText ? target?.children?.[0]?.innerText : ''
})
fontSize.value = caretData.style.fontSize
fontColor.value = caretData.style.fontColor
fontColorLabel.style.backgroundColor = caretData.style.fontColor
container.addEventListener('click', (e) => {
if (e.target.getAttribute('ade-type') == 'option') {
let parent = e.target.parentElement
let labelList = container.querySelectorAll(`[ade-target="#${parent.id}"]`)
labelList.forEach(item => {
item.innerText = e.target.innerText
})
closeAllExcept()
} else {
let target = container.querySelector(e.target.getAttribute('ade-target'))
closeAllExcept(target)
function renderCaret(toLoop) {
clearTimeout(caretData.timeout)
let ctx = pageList[0].el.getContext('2d', { willReadFrequently: true })
ctx.save()
if (caretData.previousCaret) {
ctx.putImageData(caretData.previousCaret.imageData, caretData.previousCaret.x, caretData.previousCaret.y);
caretData.previousCaret = null
}
if (!caretData.blink) {
let dataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
let lineObj = lines.find(item => item.dataIndex == dataIndex && caretData.index >= item.charStartIndex)
let x = (config.format.margin * pxMmRatio) + lineObj.tabWidth
let y = (config.format.margin) * pxMmRatio
let height = caretData.style.fontSize * pxMmRatio * 5 / 4
let width = height / 10
let charData = caretData.activeData.formatedText[caretData.index - 1]
if (lineObj) {
x = (charData?(charData.x+charData.width):x)
if(charData){
y = charData.y - (caretData.style.fontSize*pxMmRatio)
}else{
y = lineObj.y - (lineObj.maxFontSize*pxMmRatio)
}
}
if (e.target.getAttribute('ade-action')=='font-size-minus'){
changeFontSize(Number(fontSize.value)-1)
}
if (e.target.getAttribute('ade-action')=='font-size-plus'){
changeFontSize(Number(fontSize.value)+1)
}
function closeAllExcept(except) {
let dropdownLists = container.querySelectorAll('[ade-type="dropdown"]')
dropdownLists.forEach(item => {
if (item == except) {
item.classList.toggle('show')
}
else item.classList.remove('show')
})
const imageData = ctx.getImageData(x, y, width, height);
const data = imageData.data;
caretData.previousCaret = { imageData: structuredClone(imageData), x, y }
// Invert the color of the rectangular region
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // Red
data[i + 1] = 255 - data[i + 1]; // Green
data[i + 2] = 255 - data[i + 2]; // Blue
// Alpha channel remains unchanged (data[i + 3])
}
})
}
function showLoader(){
container.append(fullScreenLoadingOverlay)
fullScreenLoadingOverlay.addEventListener('mousedown', preventEvents, true)
fullScreenLoadingOverlay.addEventListener('keydown', preventEvents, true)
}
function hideLoader(){
fullScreenLoadingOverlay.removeEventListener('mousedown', preventEvents, true)
fullScreenLoadingOverlay.removeEventListener('keydown', preventEvents, true)
fullScreenLoadingOverlay.remove()
}
function preventEvents(event) {
event.preventDefault();
event.stopPropagation();
}
function setupListItem(){
}
ctx.putImageData(imageData, x, y);
caretData.blink = true
} else {
caretData.blink = false
}
inititalize(customConfig)
function destory() {
clearInterval(caretData.interval)
ctx.restore()
if (toLoop) {
caretData.timeout = setTimeout(() => {
renderCaret(true)
}, caretData.timeoutDuration)
}
return
}
function getContent() {
return JSON.parse(JSON.stringify(dataSet))
function focusOnPage() {
caretData.blink = false
if (!lastFocussedPage) lastFocussedPage = pageList[0].el
if (lastFocussedPage) {
const scrollTop = pageScrollingDiv.scrollTop
lastFocussedPage.focus()
pageScrollingDiv.scrollTop = scrollTop
}
}
var returnObj = {
destory,
loadContent: function (data) {
dataSet = JSON.parse(JSON.stringify(data))
reRenderPages(dataSet)
caretData.activeData = null
caretData.activeData = dataSet[0]
caretData.index = 0
addFonts,
getConfiguration() { return JSON.parse(JSON.stringify(config)) },
getPages() {
let pagesToReturn = JSON.parse(JSON.stringify(pageList))
for (let i = 0; i < pagesToReturn.length; i++) pagesToReturn[i].canvas = pageList[i].canvas
return pagesToReturn
},
getContent,
addFonts: addFonts,
generatePDF,
log() {
console.log('dataList', dataList)
console.log('lines', lines)
console.log('caretData', caretData)
},
focusOnPage,
}
initialize()
return returnObj
}
......
.main {
position: relative;
display: flex;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #858585;
flex-direction: column;
justify-content: flex-start;
}
.header {
position: relative;
display: block;
width: 100%;
background-color: red;
}
.toolbar {
position: relative;
width: 100%;
background: pink;
display: flex;
gap: 10px;
}
.toolbar .item {
position: relative;
background: #408640;
border: 1px solid gray;
border-radius: 3px;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 4px;
padding-right: 4px;
margin: 4px;
cursor: pointer;
}
.popover{
position: absolute;
padding: 5px;
background-color: gray;
border: 1px solid purple;
opacity: 0;
pointer-events: none;
}
.popover.show{
opacity: 1;
pointer-events: auto;
z-index: 10;
}
.toolbar .item:hover {
background: #5fad5f;
}
[adc-type="popover"] {
pointer-events: none;
}
.small-input {
width: 50px;
}
.small-btn {
width: 25px;
}
.option {
border: 1px solid black;
padding: 5px;
margin: 5px;
border-radius: 4px;
}
.option:hover {
background-color: #f5c468;
}
.page-list {
position: relative;
display: block;
overflow-y: auto;
overflow-x: auto;
background: #858585;
gap: 20px;
align-items: center;
}
.page-list canvas {
background-color: #fff;
width: 210mm;
height: auto;
display: block;
position: relative;
margin: 20px;
}
.footer {
position: relative;
display: block;
width: 100%;
background-color: red;
}
\ No newline at end of file
let isModule = (typeof module != 'undefined') ? true : false
if (!isModule) console.log('Browser Environment')
var ADocEditor = function (customConfig) {
var container, counter = 0, shadow, pxMmRatio, canvasPxMmRatio, config, htmlStr, htmlTag, htmlObj = {}, fontList = [], headerToolbar = [];
var paperSizes = {
"A4": { mmWidth: 210, mmHeight: 297 },
}
const letters = { "0": "a","1": "b","2": "c","3": "d","4": "e","5": "f","6": "g","7": "h","8": "i","9": "j","a": "k","b": "l","c": "m","d": "n","e": "o","f": "p","g": "q","h": "r","i": "s","j": "t","k": "u","l": "v","m": "w","n": "x","o": "y","p": "z" }
const romanNumerals = [ ["I", "IV", "V", "IX"], ["X", "XL", "L", "XC"], ["C", "CD", "D", "CM"], ["M"] ];
var defaultConfig = {
pageSetup: { size: "A4" }, zoom: 1,
format: {
background: "#fff",
margin: 15, // mm
border: "",
tabWidth: 20, // mm
},
style: {
fontSize: 10, // this is in mm
fontFamily: 'Calibri',
bold: false,
italic: false,
fontColor: "#f01"
},
}
config = JSON.parse(JSON.stringify(defaultConfig))
var dataList = [
{
id: ++counter,
type: 0,
formatedText: [],
plainContent: "",
tabCount: 0,
style: { ...config.style },
listItemNumber: 0,
// newPage: false, // if this is true the data is send to new page
},
]
var caretData = {
activeData: dataList[0],
index: 0,
timeout: null,
timeoutDuration: 800,
blink: true,
pageIndex: 0,
caretSize: config.style.fontSize,
previousCaret: null,
style: { ...dataList[0].style, x: config.format.margin, y: config.format.margin + (3 * config.style.fontSize / 4) },
}
var pageList = []
var pageScrollingDiv = null
var focussedPage = null
var lastFocussedPage = null
var isRendering = false
function initialize() {
config = JSON.parse(JSON.stringify(defaultConfig))
container = customConfig.container
shadow = container.attachShadow({ mode: "open" })
htmlStr = /*html*/`
<div class="header">
<div class="toolbar">
<select class="item" adc="font-select"></select>
<div class="item" adc="list-handler" adc-target="list-popover">
<span>L</span>
<div class="popover" adc="list-popover" adc-type="popover">
<div class="option" adc-toggle="listing-option" value="1">•&nbsp;&nbsp;Bullets&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div class="option" adc-toggle="listing-option" value="0">1. Numbers</div>
</div>
</div>
<div class="item">
<button adc="font-size-change" class="small-btn" value="-1">-</button>
<input type="number" adc="font-size-input" class="small-input">
<button adc="font-size-change" class="small-btn" value="+1">+</button>
</div>
</div>
</div>
<div class="page-list" adc="page-list"></div>
<div class="footer">
<div class="toolbar">
<select class="item" title="Select zoom" adc="zoom">
<option value="" disabled>Select Zoom</option>
<option value="0.1">10%</option>
<option value="0.2">20%</option>
<option value="0.3">30%</option>
<option value="0.4">40%</option>
<option value="0.5">50%</option>
<option value="0.6">60%</option>
<option value="0.7">70%</option>
<option value="0.8">80%</option>
<option value="0.9">90%</option>
<option value="1" selected>100%</option>
<option value="1.2">120%</option>
<option value="1.5">150%</option>
<option value="1.8">180%</option>
<option value="2">200%</option>
</select>
<span class="item">Words : 0</span>
<span class="item">Sentences : 0</span>
<span class="item">Pages : 1</span>
</div>
</div>
`;
let styleSheets = customConfig?.styleSheet?.length ? customConfig.styleSheet : []
styleSheets.splice(0, 0, "/assets/a-doc-editor2.css")
for (let i = 0; i < styleSheets.length; i++) {
let link = document.createElement('link')
link.setAttribute('rel', 'stylesheet');
link.setAttribute('href', styleSheets[i]);
shadow.append(link)
}
htmlTag = document.createElement('div')
htmlTag.setAttribute('class', 'main')
htmlTag.innerHTML = htmlStr
shadow.append(htmlTag)
htmlObj = {
header: shadow.querySelector('.header'),
pageList: shadow.querySelector('page-list')
}
pageScrollingDiv = shadow.querySelector('[adc="page-list"]')
!(function zoomHandles() {
let zoomSelect = shadow.querySelector('[adc="zoom"]')
zoomSelect.addEventListener('change', (e) => {
config.zoom = Number(e.target.value)
reConfigure(config)
})
})()
!(function fontSelectHandle() {
let fontSelect = shadow.querySelector('[adc="font-select"]')
headerToolbar.push(fontSelect)
fontSelect.addEventListener('change', (e) => {
caretData.style = {
...caretData.style,
fontFamily: e.target.value,
}
focusOnPage()
})
addFonts(["./assets/fonts/Afacad-VariableFont_wght.woff2", "./assets/fonts/Afacad-VariableFont_wght.ttf"], 'Afacad')
addFonts(["./assets/fonts/ArchitectsDaughter-Regular.woff2", "./assets/fonts/ArchitectsDaughter-Regular.ttf"], 'Architects Daughter')
addFonts(["./assets/fonts/Assistant-VariableFont_wght.woff2", "./assets/fonts/Assistant-VariableFont_wght.ttf"], 'Assistant')
addFonts(["./assets/fonts/Bitter-VariableFont_wght.woff2", "./assets/fonts/Bitter-VariableFont_wght.ttf"], 'Bitter')
addFonts(["./assets/fonts/calibri-regular.woff2", "./assets/fonts/calibri-regular.ttf"], 'Calibri')
})()
!(function handleList() {
let listItems = shadow.querySelectorAll('[adc-toggle="listing-option"]')
let popover = shadow.querySelector('[adc-type="popover"]')
for(let i=0; i<listItems.length; i++){
listItems[i].addEventListener('click', (e) => {
let value = Number(e.target.getAttribute('value'))
value = value?value:0
if (caretData.activeData) {
caretData.activeData.type = (caretData.activeData.type == 1 && caretData.activeData.listStyle==value) ? 0 : 1
if (caretData.activeData.type==1){
caretData.activeData.listStyle = value
}
changeListStyle()
caretData.blink = false
reRenderCanvas()
popover.classList.remove('show')
focusOnPage()
}
})
}
})()
!(function fontSizeHandler(){
let fontSizeInput = shadow.querySelector('[adc="font-size-input"]')
fontSizeInput.value = caretData.style.fontSize
let fontSizeChangers = shadow.querySelectorAll('[adc="font-size-change"]')
fontSizeChangers.forEach( btn => {
btn.addEventListener( 'click',(e)=>{
let change = Number(e.target.getAttribute('value'))
change = change?change:0
change = caretData.style.fontSize + change
change = change?change:config.style.fontSize
caretData.style.fontSize = change
fontSizeInput.value = change
focusOnPage()
} )
} )
function changeFontEvent(e){
let value = Number( e.target.value )
value = value?value:config.style.fontSize
caretData.style.fontSize = value
if (e?.key == 'Enter') focusOnPage()
}
fontSizeInput.addEventListener('keydown', changeFontEvent)
fontSizeInput.addEventListener('input', changeFontEvent)
})()
reConfigure(customConfig)
reRenderCanvas()
addGlobalEvents()
}
function reConfigure(newConfig) {
if (newConfig?.size && paperSizes[newConfig?.size]) { config.pageSetup = { ...paperSizes[newConfig.size], size: newConfig.size } }
else if (newConfig?.width && newConfig?.height) {
config.pageSetup = { size: "Custom", mmWidth: newConfig.width, mmHeight: newConfig.height }
}
else { config.pageSetup = { size: "A4", ...paperSizes['A4'] } }
if (typeof window == 'object') {
config.pageSetup.multipler = config.pageSetup.mmHeight / config.pageSetup.mmWidth
config.pageSetup.pxWidth = 2480
config.pageSetup.pxHeight = Math.ceil(config.pageSetup.pxWidth * config.pageSetup.multipler)
} else {
config.pageSetup.pxHeight = config.pageSetup.mmHeight
config.pageSetup.pxWidth = config.pageSetup.mmWidth
}
if (config.zoom) {
config.pageSetup.uiWidth = config.pageSetup.mmWidth * config.zoom
config.pageSetup.uiHeight = config.pageSetup.mmHeight * config.zoom
}
for (let p = 0; p < pageList.length; p++) {
pageList[p].el.style.width = `${config.pageSetup.uiWidth}mm`
}
pxMmRatio = config.pageSetup.pxWidth / config.pageSetup.mmWidth;
return config
}
function reRenderCanvas() {
if (isRendering) return
let pageIndex = 0
if (!pageList.length) {
pageList[pageIndex] = { id: ++counter, el: createNewPage(), dataIndex: 0 }
shadow.querySelector('.page-list').append(pageList[pageIndex].el)
}
// to clear the canvases
for (let i = 0; i < pageList.length; i++) {
let ctx = pageList[i].el.getContext('2d', { willReadFrequently: true })
ctx.save()
ctx.clearRect(0, 0, pageList[i].el.width, pageList[i].el.height); // clears the canvas
ctx.fillStyle = config.format.background
ctx.fillRect(0, 0, pageList[i].el.width, pageList[i].el.height)
ctx.restore()
}
lines = []
calculateTextSizeAndPosition()
renderLines()
caretData.previousCaret = null
renderCaret(true)
function calculateTextSizeAndPosition() {
let d = 0, c = 0;
function getLineObj() {
let newLineObj = {
...config.style,
x: 0, // this is the starting point x; it will change based on the tabNumber
y: 0, // this is the starting y coordinate; it will change based on the max font size
plainContent: "",
dataIndex: d,
charStartIndex: 0, // index from where to check
charEndIndex: 0, // index till where to check// not including this index.
listItemNumber: 0,
}
newLineObj.maxFontSize = newLineObj.fontSize
return newLineObj
}
for (d = 0; d < dataList.length; d++) {
let lineObj = getLineObj()
let dataBlock = dataList[d]
let canvas = pageList[pageIndex].el
let ctx = canvas.getContext('2d', { willReadFrequently: true })
ctx.save()
// to calculate the lines
let tempLineWidth = 0;
let maxLineWidth = config.pageSetup.pxWidth - (config.format.margin * pxMmRatio * 2)
let tabWidth = ((dataBlock.type == 1 ? 1 : 0) + dataBlock.tabCount) * config.format.tabWidth * pxMmRatio
lineObj.tabWidth = tabWidth
maxLineWidth -= tabWidth
lineObj.dataType = dataBlock.type
let wordEndIndex = 0; // this stores the index of the word which can fit in the line;
let tempWordWidth = 0
if (dataBlock.type == 1) {
let previousBlock = dataList[d - 1]
if (!previousBlock || previousBlock?.type != 1) dataBlock.listItemNumber = 1
else if (previousBlock.tabCount == dataBlock.tabCount) {
dataBlock.listItemNumber = previousBlock.listItemNumber + 1
} else {
dataBlock.listItemNumber = 1
let olderBlockIndex = d - 1
while (dataList?.[olderBlockIndex]?.type == 1 && dataList?.[olderBlockIndex]?.tabCount >= dataBlock.tabCount) {
if (dataList?.[olderBlockIndex]?.tabCount == dataBlock.tabCount) {
dataBlock.listItemNumber = dataList?.[olderBlockIndex].listItemNumber + 1
break;
}
--olderBlockIndex;
}
}
}
for (c = 0; c < dataBlock.plainContent.length; c++) {
let style = dataBlock?.formatedText?.[c]
lineObj.maxFontSize = (lineObj.maxFontSize<dataBlock?.formatedText?.[c].fontSize)?dataBlock?.formatedText?.[c].fontSize:lineObj.maxFontSize
if (/\s/.test(dataBlock.plainContent[c])) {
// condition to check if a blank character is found.
wordEndIndex = c
lineObj.charEndIndex = c
tempWordWidth = 0
}
let charWidth = getCharacterWidth(dataBlock.plainContent[c], style)
dataBlock.formatedText[c] = {
...config.style,
...style,
width: charWidth
}
// // if with the current char the string could not fit on one line
if (tempLineWidth + charWidth > maxLineWidth) {
// cannot add this// new line should be added//
if (tempWordWidth + charWidth >= maxLineWidth) {
// this is to manage the casse where there is a set of long string without any blank space. In that case the text is broken and moved to next line.
wordEndIndex = c
lineObj.charEndIndex = c
tempWordWidth = 0
}
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex + 1)
lines.push(lineObj)
lineObj = new getLineObj()
lineObj.maxLineWidth = maxLineWidth
lineObj.maxFontSize = style.fontSize
if (dataBlock.type == 1){
lineObj.tabWidth = tabWidth
lineObj.tabCount = dataBlock.tabCount
}else{
maxLineWidth = config.pageSetup.pxWidth - (config.format.margin * pxMmRatio * 2)
lineObj.tabWidth = 0
lineObj.tabCount = 0
}
lineObj.charStartIndex = wordEndIndex + 1
lineObj.charEndIndex = wordEndIndex + 1
tempLineWidth = tempWordWidth
}
else {
// if the char can fit in the same line. then it is well and good
tempLineWidth += charWidth
tempWordWidth += charWidth
}
}
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex + 1)
// there is chance that the last line is not at the width// so we need to handle the last line separately
if (lineObj.charEndIndex <= dataBlock.plainContent.length) {
lineObj.charEndIndex = dataBlock.plainContent.length - 1
}
lineObj.plainContent = dataBlock.plainContent.slice(lineObj.charStartIndex, lineObj.charEndIndex)
lines.push(lineObj)
ctx.restore()
}
return
}
function renderLines() {
let x = 0, y = config.format.margin * pxMmRatio;
for (let l = 0; l < lines.length; l++) {
let setData = dataList[lines[l].dataIndex]
let ctx = pageList[0].el.getContext('2d', { willReadFrequently: true })
x = config.format.margin * pxMmRatio
x += lines[l].tabWidth
y = y + (lines[l].maxFontSize * pxMmRatio)
lines[l].y = y
ctx.save()
// this is to render the numbering and bullets etc
if (lines[l].dataType == 1) {
ctx.save()
ctx.font = `${setData.style.fontSize * pxMmRatio}px ${setData.style.fontFamily}`
ctx.fillStyle = `${setData.style.fontColor}`
let label = getLabel(lines[l].dataIndex)
let labelWidth = ctx.measureText(label).width
labelWidth += pxMmRatio * 5
ctx.fillText(label, x - labelWidth, y)
ctx.restore()
}
for (let c = lines[l].charStartIndex; c <= lines[l].charEndIndex; c++) {
let char = setData?.plainContent[c]
if (char) {
let style = setData.formatedText[c]
ctx.save()
ctx.font = `${style?.bold ? 'bold ' : ''}${style?.italic ? 'italic ' : ''} ${style.fontSize * pxMmRatio}px ${style.fontFamily}`
ctx.fillStyle = `${style?.fontColor}`
ctx.fillText(char, x, y)
setData.formatedText[c].x = x
setData.formatedText[c].y = y
ctx.restore()
if (setData.formatedText[c]?.width) {
x += setData.formatedText[c]?.width
}
}
}
ctx.restore()
}
return
}
function getLabel(dataIndex) {
let label = "x."
if (dataList[dataIndex].listStyle == 0) { // 1. a. i.
let tabCount = dataList[dataIndex].tabCount
tabCount = tabCount ? tabCount : 0
if (tabCount % 3 == 0) label = `${dataList[dataIndex].listItemNumber}.`
else if (tabCount % 3 == 1) label = `${convertToLetter(dataList[dataIndex].listItemNumber)}.`
else label = `${convertToRoman(dataList[dataIndex].listItemNumber).toLowerCase()}.`
}
else if (dataList[dataIndex].listStyle == 1) { // >
label = `>`
}
function convertToLetter(num) {
num = (num-1).toString(26)
let label = ""
for(let i=0;i<num.length;i++) label+= letters[num[i]]
return label
}
function convertToRoman(num) {
let result = '';
let divisor = 1000;
for (let i = 3; i >= 0; i--) {
const digit = Math.floor(num / divisor);
num %= divisor;
divisor /= 10;
if (digit > 0) {
if (digit === 9) {
result += romanNumerals[i][3];
} else if (digit >= 5) {
result += romanNumerals[i][2];
result += romanNumerals[i][0].repeat(digit - 5);
} else if (digit === 4) {
result += romanNumerals[i][1];
} else {
result += romanNumerals[i][0].repeat(digit);
}
}
}
return result;
}
return label
}
function getCharacterWidth(char, style) {
let canvas = pageList[0].el
let ctx = canvas.getContext('2d', { willReadFrequently: true })
ctx.save()
ctx.font = `${style?.bold ? 'bold ' : ''}${style?.italic ? 'italic ' : ''} ${style.fontSize * pxMmRatio}px ${style.fontFamily}`
ctx.fillStyle = `${style?.fontColor}`
let width = ctx.measureText(char).width
ctx.restore()
return width
}
function createNewPage() {
let canvas;
if (isModule) {
const { createCanvas } = require('canvas')
canvas = createCanvas(config.pageSetup.canvasWidth, config.pageSetup.canvasHeight)
} else {
canvas = document.createElement('canvas')
canvas.setAttribute('class', 'page')
canvas.width = config.pageSetup.canvasWidth
canvas.height = config.pageSetup.canvasHeight
canvas.setAttribute("tabIndex", -1)
canvas.setAttribute('tabIndex', '-1')
canvas.width = config.pageSetup.pxWidth
canvas.height = config.pageSetup.pxHeight
canvas.style.width = `${config.pageSetup.uiWidth}mm`
let ctx = canvas.getContext('2d', { willReadFrequently: true })
ctx.fillStyle = config.format.background
ctx.fillRect(0, 0, canvas.width, canvas.height)
canvas.addEventListener('keydown', keydownHandler)
canvas.addEventListener('mousedown', mousedownHandler)
canvas.addEventListener("focus", onFocusHandler)
canvas.addEventListener("blur", onBlurHandler)
}
return canvas
}
function keydownHandler(e) {
if (e.altKey) return
else if (e.key == 'Tab') {
e.preventDefault()
manageIndentation(e.shiftKey ? -1 : 1)
} else if ([']', '['].includes(e.key) && e.ctrlKey) {
manageIndentation(e.key == '[' ? -1 : 1)
}
else if (e.key == 'Backspace') {
if (caretData.index == 0) {
let activeDataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
if (caretData.activeData.tabCount) {
if (caretData.activeData.type==1){
caretData.activeData.type = 0
}else{
caretData.activeData.tabCount--
}
}
else if (activeDataIndex > 0) {
if (caretData.activeData.type == 0) {
if (!caretData.activeData.plainContent.length) { dataList.splice(activeDataIndex, 1) }
caretData.activeData = dataList[activeDataIndex - 1]
caretData.index = caretData.activeData.plainContent.length
} else if (caretData.activeData.type == 1) {
caretData.activeData.type = 0
}
}
} else {
caretData.activeData.plainContent = caretData.activeData.plainContent.slice(0, caretData.index - 1) + caretData.activeData.plainContent.slice(caretData.index)
caretData.activeData.formatedText.splice(caretData.index - 1, 1)
--caretData.index;
}
}
else if (e.key == 'Enter') {
let dataObj = {
id: ++counter,
type: caretData.activeData.type,
formatedText: [],
plainContent: "",
listStyle: caretData.activeData.listStyle,
tabCount: caretData.activeData.tabCount,
style: { ...caretData.activeData.style }
}
if (e.ctrlKey || e.metaKey) dataObj.newPage = true
caretData.index = 0
caretData.activeData = dataObj
if (dataObj.type == 0) dataObj.tabCount = 0
dataList.push(dataObj)
}
else if (e.key.length == 1 && !e.ctrlKey && !e.metaKey) { // displayable text
// ***** DISPLAYABLE TEXT **** //
e.preventDefault()
caretData.activeData.plainContent = caretData.activeData.plainContent.slice(0, caretData.index) + e.key + caretData.activeData.plainContent.slice(caretData.index)
caretData.activeData.formatedText.splice(caretData.index, 0, caretData.style)
++caretData.index
}
else if (e.key == 'ArrowLeft') {
if (caretData.index) {
--caretData.index
} else {
let dataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
if (dataIndex > 0) {
caretData.activeData = dataList[dataIndex - 1]
caretData.index = caretData.activeData.plainContent.length
}
}
}
else if (e.key == 'ArrowRight') {
if (caretData.index < caretData.activeData.plainContent.length) {
++caretData.index
} else {
let dataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
if (dataList[dataIndex + 1]) {
caretData.activeData = dataList[dataIndex + 1]
caretData.index = 0
}
}
}
else if (e.key == 'ArrowUp') {
console.log('Up')
}
else if (e.key == 'ArrowDown') {
console.log('Down')
}
function manageIndentation(value) {
caretData.activeData.tabCount += value
if (caretData.activeData.tabCount < 0) caretData.activeData.tabCount = 0
else if (caretData.activeData.tabCount > 5) caretData.activeData.tabCount = 5
return true
}
caretData.blink = false
caretData.previousCaret = null
reRenderCanvas()
}
function placeCaret(cursor = { x: 0, y: 0 }) {
let found = false
for (let l = 0; l < lines.length; l++) {
if ((cursor.y <= lines[l].y) && (cursor.y >= (lines[l].maxFontSize * pxMmRatio))) {
let dataSet = dataList[lines[l].dataIndex]
for (let c = lines[l].charStartIndex; c <= lines[l].charEndIndex; c++) {
if ((cursor.x >= dataSet.formatedText[c].x) && (cursor.x <= (dataSet.formatedText[c].width + dataSet.formatedText[c].x))) {
caretData.activeData = dataSet
if (cursor.x <= dataSet.formatedText[c].x + dataSet.formatedText[c].width / 2) {
caretData.index = c
} else {
caretData.index = c + 1
}
found = true
caretData.blink = false
renderCaret(true)
return
}
}
if (found) break; // break only when the caret index is determined
}
}
if (!found) {
console.log('no char found')
}
}
function mousedownHandler(e) {
if (focussedPage) {
const rect = e.target.getBoundingClientRect(); // Get the position of the canvas
const pxX = (e.clientX - rect.left) * config.pageSetup.pxWidth / rect.width;
const pxY = (e.clientY - rect.top) * config.pageSetup.pxHeight / rect.height;
placeCaret({ x: pxX, y: pxY })
let style = caretData.activeData.formatedText[caretData.index]
if (style) caretData.style = { ...caretData.style,
fontSize: style.fontSize,
}
shadow.querySelector('[adc="font-size-input"]').value = caretData.style.fontSize
}
}
function onFocusHandler(e) {
focussedPage = e.target
lastFocussedPage = e.target
reRenderCanvas()
}
function onBlurHandler(e) {
caretData.blink = true
focussedPage = null
}
}
function changeListStyle(){
let activeDataIndex = dataList.findIndex( item => item.id == caretData.activeData.id )
let d = activeDataIndex-1
while( dataList?.[d]?.type==1){
dataList[d].listStyle = caretData.activeData.listStyle
--d
}
d = activeDataIndex+1
while(dataList?.[d]?.type==1){
dataList[d].listStyle = caretData.activeData.listStyle
++d
}
}
function addFonts(paths, name) {
if (typeof paths == 'string') paths = [paths]
let fontObj = {
paths: [],
name: name,
}
if (!isModule) {
let linkString = ''
for (let i = 0; i < paths.length; i++) {
let format = paths[i].split('.');
format = format[format.length - 1]
fontObj.paths.push(paths[i])
linkString += `url(${paths[i]}) format("${format == 'ttf' ? 'truetype' : format}")${(i >= paths.length - 1) ? '' : ',\n'}`
}
const customFont = new FontFace(`${name}`, `${linkString}`);
customFont.load()
.then((loadedFont) => {
document.fonts.add(loadedFont);
loadedFont.loaded.then(() => {
fontList.push(fontObj)
reRenderFontDropdown()
})
})
}
function reRenderFontDropdown() {
let fontFamilyDropdown = headerToolbar.find(item => item.getAttribute('adc') == 'font-select')
if (fontFamilyDropdown) {
fontFamilyDropdown.innerHTML = ""
fontList.forEach((font, i) => {
let optionTag = document.createElement('option')
optionTag.setAttribute('value', font.name)
optionTag.style.fontFamily = font.name
optionTag.innerText = font.name
fontFamilyDropdown.append(optionTag)
})
}
changeFontFamily()
}
function changeFontFamily() {
let fontFamilyDropdown = headerToolbar.find(item => item.getAttribute('adc') == 'font-select')
if (fontFamilyDropdown) fontFamilyDropdown.value = caretData.style.fontFamily
}
}
function onMouseWheelHandler(e) {
if (e.ctrlKey || e.metaKey) e.preventDefault()
}
function addGlobalEvents(e) {
shadow.querySelector('.page-list').addEventListener('wheel', onMouseWheelHandler)
shadow.addEventListener('mousedown', globalMouseDownHandler)
}
function globalMouseDownHandler(e) {
var elem = e.target
var targetId = elem.getAttribute('adc-target')
if (elem.getAttribute('adc-type') == 'popover') { return }
while (!targetId && elem) {
elem = elem.parentNode
if (elem?.getAttribute?.('adc-type') == 'popover') return
targetId = elem?.getAttribute?.('adc-target')
}
let allPopovers = shadow.querySelectorAll('[adc-type="popover"]')
allPopovers.forEach(item => {
if (item.getAttribute('adc') == targetId) item.classList.toggle('show')
else item.classList.remove('show')
})
}
function renderCaret(toLoop) {
clearTimeout(caretData.timeout)
let ctx = pageList[0].el.getContext('2d', { willReadFrequently: true })
ctx.save()
if (caretData.previousCaret) {
ctx.putImageData(caretData.previousCaret.imageData, caretData.previousCaret.x, caretData.previousCaret.y);
caretData.previousCaret = null
}
if (!caretData.blink) {
let dataIndex = dataList.findIndex(item => item.id == caretData.activeData.id)
let lineObj = lines.find(item => item.dataIndex == dataIndex && caretData.index >= item.charStartIndex)
let x = (config.format.margin * pxMmRatio) + lineObj.tabWidth
let y = (config.format.margin) * pxMmRatio
let height = caretData.style.fontSize * pxMmRatio * 5 / 4
let width = height / 10
let charData = caretData.activeData.formatedText[caretData.index - 1]
if (lineObj) {
x = (charData?(charData.x+charData.width):x)
if(charData){
y = charData.y - (caretData.style.fontSize*pxMmRatio)
}else{
y = lineObj.y - (lineObj.maxFontSize*pxMmRatio)
}
}
const imageData = ctx.getImageData(x, y, width, height);
const data = imageData.data;
caretData.previousCaret = { imageData: structuredClone(imageData), x, y }
// Invert the color of the rectangular region
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // Red
data[i + 1] = 255 - data[i + 1]; // Green
data[i + 2] = 255 - data[i + 2]; // Blue
// Alpha channel remains unchanged (data[i + 3])
}
ctx.putImageData(imageData, x, y);
caretData.blink = true
} else {
caretData.blink = false
}
ctx.restore()
if (toLoop) {
caretData.timeout = setTimeout(() => {
renderCaret(true)
}, caretData.timeoutDuration)
}
return
}
function focusOnPage() {
caretData.blink = false
if (!lastFocussedPage) lastFocussedPage = pageList[0].el
if (lastFocussedPage) {
const scrollTop = pageScrollingDiv.scrollTop
lastFocussedPage.focus()
pageScrollingDiv.scrollTop = scrollTop
}
}
var returnObj = {
addFonts,
getConfiguration() { return JSON.parse(JSON.stringify(config)) },
getPages() {
let pagesToReturn = JSON.parse(JSON.stringify(pageList))
for (let i = 0; i < pagesToReturn.length; i++) pagesToReturn[i].canvas = pageList[i].canvas
return pagesToReturn
},
log() {
console.log('dataList', dataList)
console.log('lines', lines)
console.log('caretData', caretData)
}
}
initialize()
return returnObj
}
if (isModule) module.exports = ADocEditor
\ No newline at end of file
......@@ -4,10 +4,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Document Editor</title>
<link rel="stylesheet" href="./assets/a-doc-editor2.css">
<script src="./assets/fontkit.umd.min.js"></script>
<script src="./assets/pdf-lib.min.js" ></script>
<script src="./assets/a-doc-editor2.js"></script>
<script src="./assets/a-doc-editor.js"></script>
<script src="./assets/html-docx.min.js"></script>
<link rel="icon" id="favicon" href="favicon.svg" type="image/png">
......
......@@ -14,6 +14,7 @@ var editor = new ADocEditor({
container: document.getElementById("user-container-for-editor")
})
var extractedData = null
editor.focusOnPage()
function log() {
editor.log()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment